if_cs.c revision 38305
185587Sobrien/*
285587Sobrien * Copyright (c) 1997,1998 Maxim Bolotin and Oleg Sharoiko.
385587Sobrien * All rights reserved.
485587Sobrien *
585587Sobrien * Redistribution and use in source and binary forms, with or without
685587Sobrien * modification, are permitted provided that the following conditions
785587Sobrien * are met:
885587Sobrien * 1. Redistributions of source code must retain the above copyright
985587Sobrien *    notice unmodified, this list of conditions, and the following
1085587Sobrien *    disclaimer.
1185587Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1285587Sobrien *    notice, this list of conditions and the following disclaimer in the
1385587Sobrien *    documentation and/or other materials provided with the distribution.
1485587Sobrien *
1585587Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1685587Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1785587Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1885587Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1985587Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2085587Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2185587Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2285587Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2385587Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2485587Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2585587Sobrien * SUCH DAMAGE.
2685587Sobrien *
2785587Sobrien */
28125601Sru
29125601Sru/*
30125601Sru * $Id: if_cs.c,v 1.2 1998/08/12 18:02:48 bde Exp $
31125601Sru *
32125601Sru * Device driver for Crystal Semiconductor CS8920 based ethernet
33125601Sru *   adapters. By Maxim Bolotin and Oleg Sharoiko, 27-April-1997
34125601Sru */
35125601Sru
36125601Sru/* #define	 CS_DEBUG */
37125601Sru#include "cs.h"
38125601Sru#include "bpfilter.h"
39125601Sru
40125601Sru#include <sys/param.h>
41125601Sru#include <sys/systm.h>
42125601Sru#include <sys/malloc.h>
43125601Sru#include <sys/sockio.h>
44125601Sru#include <sys/kernel.h>
45125601Sru#include <sys/mbuf.h>
46118194Sru#include <sys/socket.h>
47118194Sru#include <sys/syslog.h>
48118194Sru
49118194Sru#include <net/if.h>
50118194Sru#include <net/if_arp.h>
51118194Sru#include <net/if_media.h>
52118194Sru#include <net/ethernet.h>
53118194Sru
54118194Sru#if NBPFILTER > 0
55118194Sru#include <net/bpf.h>
56118194Sru#endif
57118194Sru
58118194Sru#include <machine/clock.h>
59118194Sru
60118194Sru#include <i386/isa/isa_device.h>
61118194Sru#include <i386/isa/if_csreg.h>
62118194Sru
63118194Sru#include "pnp.h"
64118194Sru
65118194Sru#if NPNP > 0
66118194Sru#include <i386/isa/pnp.h>
67118194Sru#endif
68118194Sru
69118194Sru#ifdef  CS_USE_64K_DMA
70118194Sru#define CS_DMA_BUFFER_SIZE 65536
71118194Sru#else
72118194Sru#define CS_DMA_BUFFER_SIZE 16384
73118194Sru#endif
74118194Sru
75118194Sru#ifndef CS_WAIT_NEXT_PACKET
76118194Sru#define	CS_WAIT_NEXT_PACKET 570
77118194Sru#endif
78118194Sru
79118194Sru/*
80118194Sru * cs_softc: per line info and status
81118194Sru */
82118194Srustatic struct cs_softc {
83118194Sru
84118194Sru        /* Ethernet common code */
85118194Sru        struct arpcom arpcom;
86118194Sru
87118194Sru        /* Configuration words from EEPROM */
88118194Sru        int auto_neg_cnf;               /* AutoNegotitation configuration */
89118194Sru	int adapter_cnf;                /* Adapter configuration */
90118194Sru        int isa_config;                 /* ISA configuration */
91118194Sru        int chip_type;			/* Type of chip */
92112336Sobrien
93112336Sobrien        struct ifmedia media;		/* Media information */
94112336Sobrien
95112336Sobrien        int nic_addr; 			/* Base IO address of card */
96112336Sobrien	int send_cmd;
97112336Sobrien        int line_ctl;                   /* */
98112336Sobrien        int send_underrun;
99112336Sobrien        void *recv_ring;
100112336Sobrien
101112336Sobrien        unsigned char *buffer;
102112336Sobrien        int buf_len;
103112336Sobrien
104112336Sobrien} cs_softc[NCS];
105112336Sobrien
106112336Sobrienstatic u_long	cs_unit = NCS;
107108072Sobrien
108108072Sobrienstatic int      cs_attach               __P((struct cs_softc *, int, int));
109108072Sobrienstatic int      cs_attach_isa           __P((struct isa_device *));
110108072Sobrienstatic void	cs_init			__P((void *));
111108072Sobrienstatic int	cs_ioctl		__P((struct ifnet *, u_long, caddr_t));
112108072Sobrienstatic int	cs_probe		__P((struct isa_device *));
113107806Sobrienstatic int	cs_cs89x0_probe		__P((struct cs_softc *,
114107806Sobrien					 u_int *, int *, int, int, int));
115107806Sobrienstatic void	cs_start		__P((struct ifnet *));
116107806Sobrienstatic void	cs_stop			__P((struct cs_softc *));
117107806Sobrienstatic void	cs_reset		__P((struct cs_softc *));
118107806Sobrienstatic void	cs_watchdog		__P((struct ifnet *));
119107806Sobrien
120107806Sobrienstatic int	cs_mediachange	__P((struct ifnet *));
121107806Sobrienstatic void	cs_mediastatus	__P((struct ifnet *, struct ifmediareq *));
122107806Sobrienstatic int      cs_mediaset	__P((struct cs_softc *, int));
123107806Sobrien
124107806Sobrienstatic void	cs_write_mbufs(struct cs_softc*, struct mbuf*);
125107806Sobrienstatic void	cs_xmit_buf(struct cs_softc*);
126107806Sobrienstatic int	cs_get_packet(struct cs_softc*);
127107806Sobrienstatic void	cs_setmode(struct cs_softc*);
128107806Sobrien
129107806Sobrienstatic int	get_eeprom_data(struct cs_softc *sc, int, int, int *);
130107806Sobrienstatic int	get_eeprom_cksum(int, int, int *);
131107806Sobrienstatic int	wait_eeprom_ready( struct cs_softc *);
132107806Sobrienstatic void	control_dc_dc( struct cs_softc *, int );
133107806Sobrienstatic int	send_test_pkt( struct cs_softc * );
134107806Sobrienstatic int	enable_tp(struct cs_softc *);
135107806Sobrienstatic int	enable_aui(struct cs_softc *);
136107806Sobrienstatic int	enable_bnc(struct cs_softc *);
137107806Sobrienstatic int      cs_duplex_auto(struct cs_softc *);
138107806Sobrien
139107806Sobrienstruct isa_driver csdriver = {
140107806Sobrien	cs_probe,
141107806Sobrien	cs_attach_isa,
142107806Sobrien	CS_NAME,
143107806Sobrien	0
144107806Sobrien};
145107806Sobrien
146107806Sobrienstatic int
147107806Sobrienget_eeprom_data( struct cs_softc *sc, int off, int len, int *buffer)
148107806Sobrien{
149107806Sobrien	int i;
15090902Sdes
15190902Sdes#ifdef CS_DEBUG
15290902Sdes	printf(CS_NAME":EEPROM data from %x for %x:\n", off,len);
15390902Sdes#endif
15490902Sdes
15590902Sdes	for (i=0;i<len;i++) {
15690902Sdes		if (wait_eeprom_ready(sc) < 0) return -1;
15790902Sdes		/* Send command to EEPROM to read */
15890902Sdes		cs_writereg(sc->nic_addr, PP_EECMD, (off+i)|EEPROM_READ_CMD );
15990902Sdes		if (wait_eeprom_ready(sc)<0)
16090902Sdes			return -1;
16190902Sdes		buffer[i] = cs_readreg (sc->nic_addr, PP_EEData);
16290902Sdes
16390902Sdes#ifdef CS_DEBUG
16490902Sdes		printf("%02x %02x ",(unsigned char)buffer[i],
16590902Sdes					(unsigned char)buffer[i+1]);
16690902Sdes#endif
16790902Sdes	}
16890902Sdes
16990902Sdes#ifdef CS_DEBUG
17090902Sdes	printf("\n");
17190902Sdes#endif
17290902Sdes
17390902Sdes	return 0;
17490902Sdes}
17590902Sdes
17690902Sdesstatic int
17790902Sdesget_eeprom_cksum(int off, int len, int *buffer)
17890902Sdes{
17990902Sdes	int i,cksum=0;
18090902Sdes
18190902Sdes	for (i=0;i<len;i++)
18285587Sobrien		cksum+=buffer[i];
18385587Sobrien	cksum &= 0xffff;
18485587Sobrien	if (cksum==0)
18585587Sobrien		return 0;
18685587Sobrien	return -1;
18785587Sobrien}
18885587Sobrien
18985587Sobrienstatic int
19085587Sobrienwait_eeprom_ready(struct cs_softc *sc)
19185587Sobrien{
19285587Sobrien	int timeout=1000;
19385587Sobrien	DELAY ( 30000 );	/* XXX should we do some checks here ? */
19485587Sobrien	return 0;
19585587Sobrien}
19685587Sobrien
19785587Sobrienstatic void
19885587Sobriencontrol_dc_dc(struct cs_softc *sc, int on_not_off)
19985587Sobrien{
20085587Sobrien	unsigned int self_control = HCB1_ENBL;
20185587Sobrien
20285587Sobrien	if (((sc->adapter_cnf & A_CNF_DC_DC_POLARITY)!=0) ^ on_not_off)
20385587Sobrien		self_control |= HCB1;
20485587Sobrien	else
20585587Sobrien		self_control &= ~HCB1;
20685587Sobrien	cs_writereg( sc->nic_addr, PP_SelfCTL, self_control );
20785587Sobrien
20885587Sobrien	DELAY( 500000 );
20985587Sobrien}
21085587Sobrien
21185587Sobrien
21285587Sobrienstatic int
21385587Sobriencs_duplex_auto(struct cs_softc *sc)
21485587Sobrien{
21585587Sobrien        int i, error=0, unit=sc->arpcom.ac_if.if_unit;
21685587Sobrien
21785587Sobrien        cs_writereg(sc->nic_addr, PP_AutoNegCTL,
21885587Sobrien                    RE_NEG_NOW | ALLOW_FDX | AUTO_NEG_ENABLE );
21985587Sobrien        for (i=0; cs_readreg(sc->nic_addr,PP_AutoNegST)&AUTO_NEG_BUSY; i++) {
22085587Sobrien                if (i > 40000) {
22185587Sobrien                        printf(CS_NAME"%1d: full/half duplex "
22285587Sobrien                               "auto negotiation timeout\n", unit);
22385587Sobrien			error = ETIMEDOUT;
22485587Sobrien                        break;
22585587Sobrien                }
22685587Sobrien                DELAY(1000);
22785587Sobrien        }
22885587Sobrien        DELAY( 1000000 );
22985587Sobrien	return error;
23085587Sobrien}
23185587Sobrien
23285587Sobrienstatic int
23385587Sobrienenable_tp(struct cs_softc *sc)
23485587Sobrien{
23585587Sobrien	int i;
23685587Sobrien	int unit = sc->arpcom.ac_if.if_unit;
23785587Sobrien
23885587Sobrien	cs_writereg(sc->nic_addr, PP_LineCTL, sc->line_ctl & ~AUI_ONLY);
23985587Sobrien	control_dc_dc(sc, 0);
24085587Sobrien	DELAY( 150000 );
24185587Sobrien
24285587Sobrien	if ((cs_readreg(sc->nic_addr, PP_LineST) & LINK_OK)==0) {
24385587Sobrien		printf(CS_NAME"%1d: failed to enable TP\n", unit);
24485587Sobrien                return EINVAL;
24585587Sobrien	}
24685587Sobrien
24785587Sobrien	return 0;
24885587Sobrien}
24985587Sobrien
25085587Sobrien/*
25185587Sobrien * XXX This was rewritten from Linux driver without any tests.
25285587Sobrien */
25385587Sobrienstatic int
25485587Sobriensend_test_pkt(struct cs_softc *sc)
25585587Sobrien{
25685587Sobrien	int unit = sc->arpcom.ac_if.if_unit;
25785587Sobrien	char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
25885587Sobrien				0, 46,  /* A 46 in network order */
25985587Sobrien				0, 0,   /* DSAP=0 & SSAP=0 fields */
26085587Sobrien				0xf3, 0 /* Control (Test Req + P bit set) */ };
26185587Sobrien	int i;
26285587Sobrien	u_char ether_address_backup[ETHER_ADDR_LEN];
26385587Sobrien
26485587Sobrien	for (i = 0; i < ETHER_ADDR_LEN; i++) {
26585587Sobrien		ether_address_backup[i] = sc->arpcom.ac_enaddr[i];
26685587Sobrien	}
26785587Sobrien
26885587Sobrien	cs_writereg(sc->nic_addr, PP_LineCTL,
26985587Sobrien		cs_readreg(sc->nic_addr, PP_LineCTL) | SERIAL_TX_ON );
27085587Sobrien	bcopy(test_packet,
27185587Sobrien			sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
27285587Sobrien	bcopy(test_packet+ETHER_ADDR_LEN,
27385587Sobrien			sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
27485587Sobrien	outw(sc->nic_addr + TX_CMD_PORT, sc->send_cmd);
27585587Sobrien	outw(sc->nic_addr + TX_LEN_PORT, sizeof(test_packet));
27685587Sobrien
27785587Sobrien	/* Wait for chip to allocate memory */
27885587Sobrien	DELAY(50000);
27985587Sobrien	if (!(cs_readreg(sc->nic_addr, PP_BusST) & READY_FOR_TX_NOW)) {
28085587Sobrien		for (i = 0; i < ETHER_ADDR_LEN; i++) {
28185587Sobrien			sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
28285587Sobrien		}
28385587Sobrien		return 0;
28485587Sobrien	}
28585587Sobrien
28685587Sobrien	outsw(sc->nic_addr + TX_FRAME_PORT, test_packet, sizeof(test_packet));
28785587Sobrien
28885587Sobrien	DELAY(30000);
28985587Sobrien
29085587Sobrien	if ((cs_readreg(sc->nic_addr,PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
29185587Sobrien		for (i = 0; i < ETHER_ADDR_LEN; i++) {
29285587Sobrien			sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
29385587Sobrien		}
29485587Sobrien		return 1;
29585587Sobrien	}
29685587Sobrien	for (i = 0; i < ETHER_ADDR_LEN; i++) {
29785587Sobrien		sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
29885587Sobrien	}
29985587Sobrien	return 0;
30085587Sobrien}
30185587Sobrien
30285587Sobrien/*
30385587Sobrien * XXX This was rewritten from Linux driver without any tests.
30485587Sobrien */
30585587Sobrienstatic int
30685587Sobrienenable_aui(struct cs_softc *sc)
30785587Sobrien{
30885587Sobrien	int unit = sc->arpcom.ac_if.if_unit;
30985587Sobrien
31085587Sobrien	control_dc_dc(sc, 0);
31185587Sobrien	cs_writereg(sc->nic_addr, PP_LineCTL,
31285587Sobrien		(sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY);
31385587Sobrien
31485587Sobrien	if (!send_test_pkt(sc)) {
31585587Sobrien		printf(CS_NAME"%1d failed to enable AUI\n", unit);
31685587Sobrien		return EINVAL;
31785587Sobrien        }
31885587Sobrien        return 0;
31985587Sobrien}
32085587Sobrien
32185587Sobrien/*
32285587Sobrien * XXX This was rewritten from Linux driver without any tests.
32385587Sobrien */
32485587Sobrienstatic int
32585587Sobrienenable_bnc(struct cs_softc *sc)
32685587Sobrien{
32785587Sobrien	int unit = sc->arpcom.ac_if.if_unit;
32885587Sobrien
32985587Sobrien	control_dc_dc(sc, 1);
33085587Sobrien	cs_writereg(sc->nic_addr, PP_LineCTL,
33185587Sobrien		(sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY);
33285587Sobrien
33385587Sobrien	if (!send_test_pkt(sc)) {
33485587Sobrien		printf(CS_NAME"%1d failed to enable BNC\n", unit);
33585587Sobrien		return EINVAL;
33685587Sobrien        }
33785587Sobrien        return 0;
33885587Sobrien}
33985587Sobrien
34085587Sobrienstatic int
34185587Sobriencs_cs89x0_probe(struct cs_softc *sc, u_int *dev_irq,
34285587Sobrien			int *dev_drq, int iobase, int unit, int flags)
34385587Sobrien{
34485587Sobrien	unsigned rev_type = 0;
34585587Sobrien	int i, irq=0, result;
34685587Sobrien	int eeprom_buff[CHKSUM_LEN];
34785587Sobrien	int chip_type, pp_isaint, pp_isadma;
34885587Sobrien	char chip_revision;
34985587Sobrien
35085587Sobrien	if ((inw(iobase+ADD_PORT) & ADD_MASK) != ADD_SIG) {
35185587Sobrien		/* Chip not detected. Let's try to reset it */
35285587Sobrien		if (bootverbose)
35385587Sobrien			printf(CS_NAME"%1d: trying to reset the chip.\n", unit);
35485587Sobrien		outw(iobase+ADD_PORT, PP_SelfCTL);
35585587Sobrien		i = inw(iobase+DATA_PORT);
35685587Sobrien		outw(iobase+ADD_PORT, PP_SelfCTL);
35785587Sobrien		outw(iobase+DATA_PORT, i | POWER_ON_RESET);
35885587Sobrien		if ((inw(iobase+ADD_PORT) & ADD_MASK) != ADD_SIG)
35985587Sobrien			return 0;
36085587Sobrien	}
36185587Sobrien
36285587Sobrien	outw(iobase+ADD_PORT, PP_ChipID);
36385587Sobrien	if (inw(iobase+DATA_PORT) != CHIP_EISA_ID_SIG)
36485587Sobrien		return 0;
36585587Sobrien
36685587Sobrien	rev_type = cs_readreg(iobase, PRODUCT_ID_ADD);
36785587Sobrien	chip_type = rev_type & ~REVISON_BITS;
36885587Sobrien	chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
36985587Sobrien
37085587Sobrien	sc->nic_addr = iobase;
37185587Sobrien	sc->chip_type = chip_type;
37285587Sobrien	if(chip_type==CS8900) {
37385587Sobrien		pp_isaint = PP_CS8900_ISAINT;
37485587Sobrien		pp_isadma = PP_CS8900_ISADMA;
37585587Sobrien		sc->send_cmd = TX_CS8900_AFTER_ALL;
37685587Sobrien	} else {
37785587Sobrien		pp_isaint = PP_CS8920_ISAINT;
37885587Sobrien		pp_isadma = PP_CS8920_ISADMA;
37985587Sobrien		sc->send_cmd = TX_CS8920_AFTER_ALL;
38085587Sobrien	}
38185587Sobrien
38285587Sobrien        /*
38385587Sobrien         * Clear some fields so that fail of EEPROM will left them clean
38485587Sobrien         */
38585587Sobrien        sc->auto_neg_cnf = 0;
38685587Sobrien        sc->adapter_cnf  = 0;
38785587Sobrien        sc->isa_config   = 0;
38885587Sobrien
38985587Sobrien	/*
39085587Sobrien	 * EEPROM
39185587Sobrien	 */
39285587Sobrien	if((cs_readreg(iobase, PP_SelfST) & EEPROM_PRESENT) == 0) {
39385587Sobrien		printf(CS_NAME"%1d: No EEPROM, assuming defaults.\n",
39485587Sobrien			unit);
39585587Sobrien	} else {
39685587Sobrien		if (get_eeprom_data(sc,START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) {
39785587Sobrien			 printf(CS_NAME"%1d: EEPROM read failed, "
39885587Sobrien				"assuming defaults..\n", unit);
39985587Sobrien		} else {
40085587Sobrien			if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) {
40185587Sobrien				printf( CS_NAME"%1d: EEPROM cheksum bad, "
40285587Sobrien					"assuming defaults..\n", unit );
40385587Sobrien			} else {
40485587Sobrien                                sc->auto_neg_cnf =
40585587Sobrien                                        eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
40685587Sobrien                                sc->adapter_cnf =
40785587Sobrien                                        eeprom_buff[ADAPTER_CNF_OFFSET/2];
40885587Sobrien                                sc->isa_config =
40985587Sobrien                                        eeprom_buff[ISA_CNF_OFFSET/2];
41085587Sobrien
41185587Sobrien                                for (i=0; i<ETHER_ADDR_LEN/2; i++) {
41285587Sobrien                                        sc->arpcom.ac_enaddr[i*2]=
41385587Sobrien                                                eeprom_buff[i];
41485587Sobrien                                        sc->arpcom.ac_enaddr[i*2+1]=
41585587Sobrien                                                eeprom_buff[i] >> 8;
41685587Sobrien                                }
41785587Sobrien
41885587Sobrien                                /*
41985587Sobrien                                 * If no interrupt specified (or "?"),
42085587Sobrien                                 * use what the board tells us.
42185587Sobrien                                 */
42285587Sobrien                                if (*dev_irq <= 0) {
42385587Sobrien                                        irq = sc->isa_config & INT_NO_MASK;
42485587Sobrien                                        if (chip_type==CS8900) {
42585587Sobrien						switch(irq) {
42685587Sobrien                                                case 0: irq=10; break;
42785587Sobrien                                                case 1: irq=11; break;
42885587Sobrien                                                case 2: irq=12; break;
42985587Sobrien                                                case 3: irq=5;  break;
43085587Sobrien                                                default: printf(CS_NAME"%1d: invalid irq in EEPROM.\n",unit);
43185587Sobrien						}
43285587Sobrien						if (irq!=0)
43385587Sobrien							*dev_irq=(u_short)(1<<irq);
43485587Sobrien					} else {
43585587Sobrien						if (irq!=0 && irq<=CS8920_NO_INTS)
43685587Sobrien							*dev_irq=(u_short)(1<<irq);
43785587Sobrien					}
43885587Sobrien                                }
43985587Sobrien			}
44085587Sobrien                }
44185587Sobrien        }
44285587Sobrien
44385587Sobrien        if ((irq=ffs(*dev_irq))) {
44485587Sobrien                irq--;
44585587Sobrien                if (chip_type == CS8900) {
44685587Sobrien			switch(irq) {
44785587Sobrien                        case  5: irq = 3; break;
44885587Sobrien                        case 10: irq = 0; break;
44985587Sobrien                        case 11: irq = 1; break;
45085587Sobrien                        case 12: irq = 2; break;
45185587Sobrien                        default: printf(CS_NAME"%1d: invalid irq\n", unit);
45285587Sobrien                                return 0;
45385587Sobrien			}
45485587Sobrien                } else {
45585587Sobrien                        if (irq > CS8920_NO_INTS) {
45685587Sobrien                                printf(CS_NAME"%1d: invalid irq\n", unit);
45785587Sobrien                                return 0;
45885587Sobrien                        }
45985587Sobrien                }
46085587Sobrien                cs_writereg(iobase, pp_isaint, irq);
46185587Sobrien	} else {
46285587Sobrien	       	printf(CS_NAME"%1d: invalid irq\n", unit);
46385587Sobrien                return 0;
46485587Sobrien        }
46585587Sobrien
46685587Sobrien        /*
46785587Sobrien         * Temporary disabled
46885587Sobrien         *
46985587Sobrien        if (drq>0)
47085587Sobrien		cs_writereg(iobase, pp_isadma, drq);
47185587Sobrien	else {
47285587Sobrien		printf( CS_NAME"%1d: incorrect drq\n", unit );
47385587Sobrien		return 0;
47485587Sobrien	}
47585587Sobrien        */
47685587Sobrien
47785587Sobrien	if (bootverbose)
47885587Sobrien		 printf(CS_NAME"%1d: model CS89%c0%s rev %c\n"
47985587Sobrien			CS_NAME"%1d: media%s%s%s\n"
48085587Sobrien			CS_NAME"%1d: irq %d drq %d\n",
48185587Sobrien			unit,
48285587Sobrien			chip_type==CS8900 ? '0' : '2',
48385587Sobrien			chip_type==CS8920M ? "M" : "",
48485587Sobrien			chip_revision,
48585587Sobrien			unit,
48685587Sobrien			(sc->adapter_cnf & A_CNF_10B_T) ? " TP"  : "",
48785587Sobrien			(sc->adapter_cnf & A_CNF_AUI)   ? " AUI" : "",
48885587Sobrien			(sc->adapter_cnf & A_CNF_10B_2) ? " BNC" : "",
48985587Sobrien			unit, (int)*dev_irq, (int)*dev_drq);
49085587Sobrien
49185587Sobrien        if ((sc->adapter_cnf & A_CNF_EXTND_10B_2) &&
49285587Sobrien            (sc->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
49385587Sobrien                sc->line_ctl = LOW_RX_SQUELCH;
49485587Sobrien        else
49585587Sobrien                sc->line_ctl = 0;
49685587Sobrien
49785587Sobrien
49885587Sobrien	return PP_ISAIOB;
49985587Sobrien}
50085587Sobrien
50185587Sobrien/*
50285587Sobrien * Determine if the device is present
50385587Sobrien *
50485587Sobrien *   on entry:
50585587Sobrien * 	a pointer to an isa_device struct
50685587Sobrien *   on exit:
50785587Sobrien *	NULL if device not found
50885587Sobrien *	or # of i/o addresses used (if found)
50985587Sobrien */
51085587Sobrienstatic int
51185587Sobriencs_probe(struct isa_device *dev)
51285587Sobrien{
51385587Sobrien	int nports;
51485587Sobrien
51585587Sobrien	struct cs_softc *sc=&cs_softc[dev->id_unit];
51685587Sobrien
51785587Sobrien	nports=cs_cs89x0_probe(sc, &(dev->id_irq), &(dev->id_drq),
51885587Sobrien			(dev->id_iobase), (dev->id_unit), (dev->id_flags));
51985587Sobrien
52085587Sobrien	if (nports)
52185587Sobrien		return (nports);
52285587Sobrien
52385587Sobrien	return (0);
52485587Sobrien}
52585587Sobrien
52685587Sobrien/*
52785587Sobrien * Install the interface into kernel networking data structures
52885587Sobrien */
52985587Sobrienstatic int
53085587Sobriencs_attach(struct cs_softc *sc, int unit, int flags)
53185587Sobrien{
53285587Sobrien        int media=0;
53385587Sobrien/*	struct cs_softc *sc = &cs_softc[dev->id_unit]; */
53485587Sobrien	struct ifnet *ifp = &(sc->arpcom.ac_if);
53585587Sobrien
53685587Sobrien	if (!ifp->if_name) {
53785587Sobrien		ifp->if_softc=sc;
53885587Sobrien		ifp->if_unit=unit;
53985587Sobrien		ifp->if_name=csdriver.name;
54085587Sobrien		ifp->if_output=ether_output;
54185587Sobrien		ifp->if_start=cs_start;
54285587Sobrien		ifp->if_ioctl=cs_ioctl;
54385587Sobrien		ifp->if_watchdog=cs_watchdog;
54485587Sobrien		ifp->if_init=cs_init;
54585587Sobrien		ifp->if_snd.ifq_maxlen= IFQ_MAXLEN;
54685587Sobrien		/*
54785587Sobrien                 *  MIB DATA
54885587Sobrien                 */
54985587Sobrien                /*
55085587Sobrien		ifp->if_linkmib=&sc->mibdata;
55185587Sobrien		ifp->if_linkmiblen=sizeof sc->mibdata;
55285587Sobrien                */
55385587Sobrien
55485587Sobrien		ifp->if_flags=(IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST );
55585587Sobrien
55685587Sobrien		/*
55785587Sobrien		 * this code still in progress (DMA support)
55885587Sobrien		 *
55985587Sobrien
56085587Sobrien		sc->recv_ring=malloc(CS_DMA_BUFFER_SIZE<<1, M_DEVBUF, M_NOWAIT);
56185587Sobrien		if (sc->recv_ring == NULL) {
56285587Sobrien			log(LOG_ERR,CS_NAME
56385587Sobrien			"%d: Couldn't allocate memory for NIC\n", unit);
56485587Sobrien			return(0);
56585587Sobrien		}
56685587Sobrien		if ((sc->recv_ring-(sc->recv_ring & 0x1FFFF))
56785587Sobrien		    < (128*1024-CS_DMA_BUFFER_SIZE))
56885587Sobrien		    sc->recv_ring+=16*1024;
56985587Sobrien
57085587Sobrien		*/
57185587Sobrien
57285587Sobrien		sc->buffer=malloc(ETHER_MAX_LEN-ETHER_CRC_LEN,M_DEVBUF,M_NOWAIT);
57385587Sobrien		if (sc->buffer == NULL) {
57485587Sobrien                        printf(CS_NAME"%d: Couldn't allocate memory for NIC\n",
57585587Sobrien                               unit);
57685587Sobrien                        return(0);
57785587Sobrien		}
57885587Sobrien
57985587Sobrien		/*
58085587Sobrien		 * Initialize the media structures.
58185587Sobrien		 */
58285587Sobrien		ifmedia_init(&sc->media, 0, cs_mediachange, cs_mediastatus);
58385587Sobrien
58485587Sobrien		if (sc->adapter_cnf & A_CNF_10B_T) {
58585587Sobrien			ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T, 0, NULL);
58685587Sobrien			if (sc->chip_type != CS8900) {
58785587Sobrien				ifmedia_add(&sc->media,
58885587Sobrien					IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
58985587Sobrien				ifmedia_add(&sc->media,
59085587Sobrien					IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
59185587Sobrien			}
59285587Sobrien		}
59385587Sobrien
59485587Sobrien		if (sc->adapter_cnf & A_CNF_10B_2)
59585587Sobrien			ifmedia_add(&sc->media, IFM_ETHER|IFM_10_2, 0, NULL);
59685587Sobrien
59785587Sobrien		if (sc->adapter_cnf & A_CNF_AUI)
59885587Sobrien			ifmedia_add(&sc->media, IFM_ETHER|IFM_10_5, 0, NULL);
59985587Sobrien
60085587Sobrien                if (sc->adapter_cnf & A_CNF_MEDIA)
60185587Sobrien                        ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
60285587Sobrien
60385587Sobrien                /* Set default media from EEPROM */
60485587Sobrien                switch (sc->adapter_cnf & A_CNF_MEDIA_TYPE) {
60585587Sobrien                case A_CNF_MEDIA_AUTO:  media = IFM_ETHER|IFM_AUTO; break;
60685587Sobrien                case A_CNF_MEDIA_10B_T: media = IFM_ETHER|IFM_10_T; break;
60785587Sobrien                case A_CNF_MEDIA_10B_2: media = IFM_ETHER|IFM_10_2; break;
60885587Sobrien                case A_CNF_MEDIA_AUI:   media = IFM_ETHER|IFM_10_5; break;
60985587Sobrien                default: printf(CS_NAME"%d: adapter has no media\n", unit);
61085587Sobrien                }
61185587Sobrien                ifmedia_set(&sc->media, media);
61285587Sobrien		cs_mediaset(sc, media);
61385587Sobrien
61485587Sobrien		if_attach(ifp);
61585587Sobrien		cs_stop( sc );
61685587Sobrien		ether_ifattach(ifp);
61785587Sobrien	}
61885587Sobrien
61985587Sobrien	if (bootverbose)
62085587Sobrien		printf(CS_NAME"%d: ethernet address %6D\n",
62185587Sobrien		       ifp->if_unit, sc->arpcom.ac_enaddr, ":");
62285587Sobrien
62385587Sobrien#if NBPFILTER > 0
62485587Sobrien	bpfattach(ifp, DLT_EN10MB, sizeof (struct ether_header));
62585587Sobrien#endif
62685587Sobrien	return 1;
62785587Sobrien}
62885587Sobrien
62985587Sobrienstatic int
63085587Sobriencs_attach_isa(struct isa_device *dev)
63185587Sobrien{
63285587Sobrien        int unit=dev->id_unit;
63385587Sobrien        struct cs_softc *sc=&cs_softc[unit];
63485587Sobrien        int flags=dev->id_flags;
63585587Sobrien
63685587Sobrien        return cs_attach(sc, unit, flags);
63785587Sobrien}
63885587Sobrien
63985587Sobrien/*
64085587Sobrien * Initialize the board
64185587Sobrien */
64285587Sobrienstatic void
64385587Sobriencs_init(void *xsc)
64485587Sobrien{
64585587Sobrien	struct cs_softc *sc=(struct cs_softc *)xsc;
64685587Sobrien	struct ifnet *ifp = &sc->arpcom.ac_if;
64785587Sobrien	int i, s, result, rx_cfg;
64885587Sobrien
64985587Sobrien	/* address not known */
65085587Sobrien	if (TAILQ_EMPTY(&ifp->if_addrhead)) /* unlikely? XXX */
65185587Sobrien		return;
65285587Sobrien
65385587Sobrien	/*
65485587Sobrien	 * reset whatchdog timer
65585587Sobrien	 */
65685587Sobrien	ifp->if_timer=0;
65785587Sobrien	sc->buf_len = 0;
65885587Sobrien
65985587Sobrien	s=splimp();
66085587Sobrien
66185587Sobrien	/*
66285587Sobrien	 * Hardware initialization of cs
66385587Sobrien	 */
66485587Sobrien
66585587Sobrien	/* Enable receiver and transmitter */
66685587Sobrien	cs_writereg(sc->nic_addr, PP_LineCTL,
66785587Sobrien		cs_readreg( sc->nic_addr, PP_LineCTL ) |
66885587Sobrien		SERIAL_RX_ON | SERIAL_TX_ON);
66985587Sobrien
67085587Sobrien	/* Configure the receiver mode */
67185587Sobrien	cs_setmode(sc);
67285587Sobrien
67385587Sobrien	/*
67485587Sobrien	 * This defines what type of frames will cause interrupts
67585587Sobrien	 * Bad frames should generate interrupts so that the driver
67685587Sobrien	 * could track statistics of discarded packets
67785587Sobrien	 */
67885587Sobrien        rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL | RX_RUNT_ENBL |
67985587Sobrien		 RX_EXTRA_DATA_ENBL;
68085587Sobrien	if (sc->isa_config & STREAM_TRANSFER)
68185587Sobrien		rx_cfg |= RX_STREAM_ENBL;
68285587Sobrien	cs_writereg(sc->nic_addr, PP_RxCFG, rx_cfg);
68385587Sobrien
68485587Sobrien	cs_writereg(sc->nic_addr, PP_TxCFG, TX_LOST_CRS_ENBL |
68585587Sobrien		    TX_SQE_ERROR_ENBL | TX_OK_ENBL | TX_LATE_COL_ENBL |
68685587Sobrien		    TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
68785587Sobrien
68885587Sobrien	cs_writereg(sc->nic_addr, PP_BufCFG, READY_FOR_TX_ENBL |
68985587Sobrien		    RX_MISS_COUNT_OVRFLOW_ENBL | TX_COL_COUNT_OVRFLOW_ENBL |
69085587Sobrien		    TX_UNDERRUN_ENBL /*| RX_DMA_ENBL*/);
69185587Sobrien
69285587Sobrien        /* Write MAC address into IA filter */
69385587Sobrien        for (i=0; i<ETHER_ADDR_LEN/2; i++)
69485587Sobrien                cs_writereg(sc->nic_addr, PP_IA+i*2,
69585587Sobrien                            sc->arpcom.ac_enaddr[i*2] |
69685587Sobrien                            (sc->arpcom.ac_enaddr[i*2+1] << 8) );
69785587Sobrien
69885587Sobrien	/*
69985587Sobrien	 * Now enable everything
70085587Sobrien	 */
70185587Sobrien/*
70285587Sobrien#ifdef	CS_USE_64K_DMA
70385587Sobrien	cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ | RX_DMA_SIZE_64K);
70485587Sobrien        #else
70585587Sobrien
70685587Sobrien        cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ);
70785587Sobrien#endif
70885587Sobrien*/
70985587Sobrien	cs_writereg(sc->nic_addr, PP_BusCTL, ENABLE_IRQ);
71085587Sobrien
71185587Sobrien	/*
71285587Sobrien	 * Set running and clear output active flags
71385587Sobrien	 */
71485587Sobrien	sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
71585587Sobrien	sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
71685587Sobrien
71785587Sobrien	/*
71885587Sobrien	 * Start sending process
71985587Sobrien	 */
72085587Sobrien	cs_start(ifp);
72185587Sobrien
72285587Sobrien	(void) splx(s);
72385587Sobrien}
72485587Sobrien
72585587Sobrien/*
72685587Sobrien * Get the packet from the board and send it to the upper layer
72785587Sobrien * via ether_input().
72885587Sobrien */
72985587Sobrienstatic int
73085587Sobriencs_get_packet(struct cs_softc *sc)
73185587Sobrien{
73285587Sobrien	struct ifnet *ifp = &(sc->arpcom.ac_if);
73385587Sobrien	int iobase = sc->nic_addr, status, length;
73485587Sobrien	struct ether_header *eh;
73585587Sobrien	struct mbuf *m;
73685587Sobrien
73785587Sobrien#ifdef CS_DEBUG
73885587Sobrien	int i;
73985587Sobrien#endif
74085587Sobrien
74185587Sobrien	status = inw(iobase + RX_FRAME_PORT);
74285587Sobrien	length = inw(iobase + RX_FRAME_PORT);
74385587Sobrien
74485587Sobrien#ifdef CS_DEBUG
74585587Sobrien	printf(CS_NAME"%1d: rcvd: stat %x, len %d\n",
74685587Sobrien		ifp->if_unit, status, length);
74785587Sobrien#endif
74885587Sobrien
74985587Sobrien	if (!(status & RX_OK)) {
75085587Sobrien#ifdef CS_DEBUG
75185587Sobrien		printf(CS_NAME"%1d: bad pkt stat %x\n", ifp->if_unit, status);
75285587Sobrien#endif
75385587Sobrien		ifp->if_ierrors++;
75485587Sobrien		return -1;
75585587Sobrien	}
75685587Sobrien
75785587Sobrien	MGETHDR(m, M_DONTWAIT, MT_DATA);
75885587Sobrien	if (m==NULL)
75985587Sobrien		return -1;
76085587Sobrien
76185587Sobrien	if (length > MHLEN) {
76285587Sobrien		MCLGET(m, M_DONTWAIT);
76385587Sobrien		if (!(m->m_flags & M_EXT)) {
76485587Sobrien			m_freem(m);
76585587Sobrien			return -1;
76685587Sobrien		}
76785587Sobrien	}
76885587Sobrien
76985587Sobrien	/* Initialize packet's header info */
77085587Sobrien	m->m_pkthdr.rcvif = ifp;
77185587Sobrien	m->m_pkthdr.len = length;
77285587Sobrien	m->m_len = length;
77385587Sobrien
77485587Sobrien	/* Get the data */
77585587Sobrien	insw(iobase + RX_FRAME_PORT, m->m_data, (length+1)>>1);
77685587Sobrien
77785587Sobrien	eh = mtod(m, struct ether_header *);
77885587Sobrien
77985587Sobrien#if NBPFILTER > 0
78085587Sobrien	if (ifp->if_bpf)
78185587Sobrien		bpf_mtap(ifp, m);
78285587Sobrien#endif
78385587Sobrien
78485587Sobrien#ifdef CS_DEBUG
78585587Sobrien	for (i=0;i<length;i++)
78685587Sobrien	     printf(" %02x",(unsigned char)*((char *)(m->m_data+i)));
78785587Sobrien	printf( "\n" );
78885587Sobrien#endif
78985587Sobrien
79085587Sobrien	if (status & (RX_IA | RX_BROADCAST) ||
79185587Sobrien	    (ifp->if_flags & IFF_MULTICAST && status & RX_HASHED)) {
79285587Sobrien		m->m_pkthdr.len -= sizeof(struct ether_header);
79385587Sobrien		m->m_len -= sizeof(struct ether_header);
79485587Sobrien		m->m_data += sizeof(struct ether_header);
79585587Sobrien
79685587Sobrien		/* Feed the packet to the upper layer */
79785587Sobrien		ether_input(ifp, eh, m);
79885587Sobrien
79985587Sobrien		ifp->if_ipackets++;
80085587Sobrien
80185587Sobrien		if (length==ETHER_MAX_LEN-ETHER_CRC_LEN)
80285587Sobrien                        DELAY( CS_WAIT_NEXT_PACKET );
80385587Sobrien	} else {
80485587Sobrien		m_freem(m);
80585587Sobrien	}
80685587Sobrien
80785587Sobrien	return 0;
80885587Sobrien}
80985587Sobrien
81085587Sobrien/*
81185587Sobrien * Software calls interrupt handler
81285587Sobrien */
81385587Sobrienstatic void
81485587Sobriencsintr_sc(struct cs_softc *sc, int unit)
81585587Sobrien{
81685587Sobrien	struct ifnet *ifp = &(sc->arpcom.ac_if);
81785587Sobrien	int status, s;
81885587Sobrien
81985587Sobrien#ifdef CS_DEBUG
82085587Sobrien	printf(CS_NAME"%1d: Interrupt.\n", unit);
82185587Sobrien#endif
82285587Sobrien
82385587Sobrien	while ((status=cs_readword(sc->nic_addr, ISQ_PORT))) {
82485587Sobrien
82585587Sobrien#ifdef CS_DEBUG
82685587Sobrien		printf( CS_NAME"%1d:from ISQ: %04x\n", unit, status );
82785587Sobrien#endif
82885587Sobrien
82985587Sobrien		switch (status & ISQ_EVENT_MASK) {
83085587Sobrien                case ISQ_RECEIVER_EVENT:
83185587Sobrien                        cs_get_packet(sc);
83285587Sobrien                        break;
83385587Sobrien
83485587Sobrien                case ISQ_TRANSMITTER_EVENT:
83585587Sobrien                        if (status & TX_OK)
83685587Sobrien                                ifp->if_opackets++;
83785587Sobrien                        else
83885587Sobrien                                ifp->if_oerrors++;
83985587Sobrien                        ifp->if_flags &= ~IFF_OACTIVE;
84085587Sobrien                        ifp->if_timer = 0;
84185587Sobrien                        break;
84285587Sobrien
84385587Sobrien                case ISQ_BUFFER_EVENT:
84485587Sobrien                        if (status & READY_FOR_TX) {
84585587Sobrien                                ifp->if_flags &= ~IFF_OACTIVE;
84685587Sobrien                                ifp->if_timer = 0;
84785587Sobrien                        }
84885587Sobrien
84985587Sobrien                        if (status & TX_UNDERRUN) {
85085587Sobrien                                ifp->if_flags &= ~IFF_OACTIVE;
85185587Sobrien                                ifp->if_timer = 0;
85285587Sobrien                                ifp->if_oerrors++;
85385587Sobrien                        }
85485587Sobrien                        break;
85585587Sobrien
85685587Sobrien                case ISQ_RX_MISS_EVENT:
85785587Sobrien                        ifp->if_ierrors+=(status>>6);
858                        break;
859
860                case ISQ_TX_COL_EVENT:
861                        ifp->if_collisions+=(status>>6);
862                        break;
863                }
864        }
865
866        if (!(ifp->if_flags & IFF_OACTIVE)) {
867                cs_start(ifp);
868        }
869}
870
871/*
872 * Handle interrupts
873 */
874void
875csintr(int unit)
876{
877	struct cs_softc *sc = &cs_softc[unit];
878
879	csintr_sc(sc, unit);
880}
881
882/*
883 * Save the data in buffer
884 */
885
886static void
887cs_write_mbufs( struct cs_softc *sc, struct mbuf *m )
888{
889	int len;
890	struct mbuf *mp;
891	unsigned char *data, *buf;
892
893	for (mp=m, buf=sc->buffer, sc->buf_len=0; mp != NULL; mp=mp->m_next) {
894		len = mp->m_len;
895
896		/*
897		 * Ignore empty parts
898		 */
899		if (!len)
900		continue;
901
902		/*
903		 * Find actual data address
904		 */
905		data = mtod(mp, caddr_t);
906
907		bcopy((caddr_t) data, (caddr_t) buf, len);
908		buf += len;
909		sc->buf_len += len;
910	}
911}
912
913
914static void
915cs_xmit_buf( struct cs_softc *sc )
916{
917	outsw(sc->nic_addr+TX_FRAME_PORT, sc->buffer, (sc->buf_len+1)>>1);
918	sc->buf_len = 0;
919}
920
921static void
922cs_start(struct ifnet *ifp)
923{
924	int s, length;
925	struct mbuf *m, *mp;
926	struct cs_softc *sc = ifp->if_softc;
927
928	s = splimp();
929
930	for (;;) {
931		if (sc->buf_len)
932			length = sc->buf_len;
933		else {
934			IF_DEQUEUE( &ifp->if_snd, m );
935
936			if (m==NULL) {
937				(void) splx(s);
938				return;
939			}
940
941			for (length=0, mp=m; mp != NULL; mp=mp->m_next)
942				length += mp->m_len;
943
944			/* Skip zero-length packets */
945			if (length == 0) {
946				m_freem(m);
947				continue;
948			}
949
950			cs_write_mbufs(sc, m);
951
952#if NBPFILTER > 0
953			if (ifp->if_bpf) {
954				bpf_mtap(ifp, m);
955			}
956#endif
957
958			m_freem(m);
959		}
960
961		/*
962		 * Issue a SEND command
963		 */
964		outw(sc->nic_addr+TX_CMD_PORT, sc->send_cmd);
965		outw(sc->nic_addr+TX_LEN_PORT, length );
966
967		/*
968		 * If there's no free space in the buffer then leave
969		 * this packet for the next time: indicate output active
970		 * and return.
971		 */
972		if (!(cs_readreg(sc->nic_addr, PP_BusST) & READY_FOR_TX_NOW)) {
973			ifp->if_timer = sc->buf_len;
974			(void) splx(s);
975			ifp->if_flags |= IFF_OACTIVE;
976			return;
977		}
978
979               	cs_xmit_buf(sc);
980
981		/*
982		 * Set the watchdog timer in case we never hear
983		 * from board again. (I don't know about correct
984		 * value for this timeout)
985		 */
986		ifp->if_timer = length;
987
988		(void) splx(s);
989		ifp->if_flags |= IFF_OACTIVE;
990		return;
991	}
992}
993
994/*
995 * Stop everything on the interface
996 */
997static void
998cs_stop(struct cs_softc *sc)
999{
1000	int s = splimp();
1001
1002	cs_writereg(sc->nic_addr, PP_RxCFG, 0);
1003	cs_writereg(sc->nic_addr, PP_TxCFG, 0);
1004	cs_writereg(sc->nic_addr, PP_BufCFG, 0);
1005	cs_writereg(sc->nic_addr, PP_BusCTL, 0);
1006
1007	sc->arpcom.ac_if.if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
1008	sc->arpcom.ac_if.if_timer = 0;
1009
1010	(void) splx(s);
1011}
1012
1013/*
1014 * Reset the interface
1015 */
1016static void
1017cs_reset(struct cs_softc *sc)
1018{
1019	cs_stop(sc);
1020	cs_init(sc);
1021}
1022
1023static void
1024cs_setmode(struct cs_softc *sc)
1025{
1026	struct ifnet *ifp = &(sc->arpcom.ac_if);
1027	int rx_ctl;
1028
1029	/* Stop the receiver while changing filters */
1030	cs_writereg(sc->nic_addr, PP_LineCTL,
1031			cs_readreg(sc->nic_addr, PP_LineCTL) & ~SERIAL_RX_ON);
1032
1033	if (ifp->if_flags & IFF_PROMISC) {
1034		/* Turn on promiscuous mode. */
1035		rx_ctl = RX_OK_ACCEPT | RX_PROM_ACCEPT;
1036	} else {
1037		if (ifp->if_flags & IFF_MULTICAST) {
1038			/* Allow receiving frames with multicast addresses */
1039			rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
1040				 RX_OK_ACCEPT | RX_MULTCAST_ACCEPT;
1041			/*
1042			 * Here the reconfiguration of chip's multicast
1043			 * filters should be done but I've no idea about
1044			 * hash transformation in this chip. If you can
1045			 * add this code or describe me the transformation
1046			 * I'd be very glad.
1047			 */
1048		} else {
1049			/*
1050			 * Receive only good frames addressed for us and
1051			 * good broadcasts.
1052			 */
1053			rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
1054				 RX_OK_ACCEPT;
1055		}
1056	}
1057
1058	/* Set up the filter */
1059	cs_writereg(sc->nic_addr, PP_RxCTL, RX_DEF_ACCEPT | RX_MULTCAST_ACCEPT);
1060
1061	/* Turn on receiver */
1062	cs_writereg(sc->nic_addr, PP_LineCTL,
1063			cs_readreg(sc->nic_addr, PP_LineCTL) | SERIAL_RX_ON);
1064}
1065
1066static int
1067cs_ioctl(register struct ifnet *ifp, u_long command, caddr_t data)
1068{
1069	struct cs_softc *sc=ifp->if_softc;
1070	struct ifreq *ifr = (struct ifreq *)data;
1071	int s,error=0;
1072
1073#ifdef CS_DEBUG
1074	printf(CS_NAME"%d: ioctl(%x)\n",sc->arpcom.ac_if.if_unit,command);
1075#endif
1076
1077	s=splimp();
1078
1079	switch (command) {
1080	case SIOCSIFADDR:
1081	case SIOCGIFADDR:
1082	case SIOCSIFMTU:
1083		ether_ioctl(ifp, command, data);
1084		break;
1085
1086	case SIOCSIFFLAGS:
1087		/*
1088		 * Switch interface state between "running" and
1089		 * "stopped", reflecting the UP flag.
1090                 */
1091                if (sc->arpcom.ac_if.if_flags & IFF_UP) {
1092                        if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)==0) {
1093                                cs_init(sc);
1094                        }
1095                } else {
1096                        if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)!=0) {
1097                                cs_stop(sc);
1098                        }
1099		}
1100		/*
1101		 * Promiscuous and/or multicast flags may have changed,
1102		 * so reprogram the multicast filter and/or receive mode.
1103		 *
1104		 * See note about multicasts in cs_setmode
1105		 */
1106		cs_setmode(sc);
1107		break;
1108
1109	case SIOCADDMULTI:
1110	case SIOCDELMULTI:
1111	    /*
1112	     * Multicast list has changed; set the hardware filter
1113	     * accordingly.
1114	     *
1115	     * See note about multicasts in cs_setmode
1116	     */
1117	    cs_setmode(sc);
1118	    error = 0;
1119	    break;
1120
1121        case SIOCSIFMEDIA:
1122        case SIOCGIFMEDIA:
1123                error = ifmedia_ioctl(ifp, ifr, &sc->media, command);
1124                break;
1125
1126        default:
1127		error = EINVAL;
1128        }
1129
1130	(void) splx(s);
1131	return error;
1132}
1133
1134/*
1135 * Device timeout/watchdog routine. Entered if the device neglects to
1136 * generate an interrupt after a transmit has been started on it.
1137 */
1138static void
1139cs_watchdog(struct ifnet *ifp)
1140{
1141	struct cs_softc *sc = &cs_softc[ifp->if_unit];
1142
1143	ifp->if_oerrors++;
1144	log(LOG_ERR, CS_NAME"%d: device timeout\n", ifp->if_unit);
1145
1146	/* Reset the interface */
1147	if (ifp->if_flags & IFF_UP)
1148		cs_reset(sc);
1149	else
1150		cs_stop(sc);
1151}
1152
1153static int
1154cs_mediachange(struct ifnet *ifp)
1155{
1156	struct cs_softc *sc = ifp->if_softc;
1157	struct ifmedia *ifm = &sc->media;
1158
1159	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
1160		return EINVAL;
1161
1162	return cs_mediaset(sc, ifm->ifm_media);
1163}
1164
1165static void
1166cs_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
1167{
1168	int line_status;
1169	struct cs_softc *sc = ifp->if_softc;
1170
1171	ifmr->ifm_active = IFM_ETHER;
1172	line_status = cs_readreg(sc->nic_addr, PP_LineST);
1173	if (line_status & TENBASET_ON) {
1174		ifmr->ifm_active |= IFM_10_T;
1175		if (sc->chip_type != CS8900) {
1176			if (cs_readreg(sc->nic_addr, PP_AutoNegST) & FDX_ACTIVE)
1177				ifmr->ifm_active |= IFM_FDX;
1178			if (cs_readreg(sc->nic_addr, PP_AutoNegST) & HDX_ACTIVE)
1179				ifmr->ifm_active |= IFM_HDX;
1180		}
1181		ifmr->ifm_status = IFM_AVALID;
1182		if (line_status & LINK_OK)
1183			ifmr->ifm_status |= IFM_ACTIVE;
1184	} else {
1185		if (line_status & AUI_ON) {
1186			cs_writereg(sc->nic_addr, PP_SelfCTL,
1187				    cs_readreg(sc->nic_addr, PP_SelfCTL) |
1188				    HCB1_ENBL);
1189			if (((sc->adapter_cnf & A_CNF_DC_DC_POLARITY)!=0)^
1190			    (cs_readreg(sc->nic_addr, PP_SelfCTL)&HCB1))
1191				ifmr->ifm_active |= IFM_10_2;
1192			else
1193				ifmr->ifm_active |= IFM_10_5;
1194		}
1195	}
1196}
1197
1198static int
1199cs_mediaset(struct cs_softc *sc, int media)
1200{
1201        int error;
1202
1203	/* Stop the receiver & transmitter */
1204	cs_writereg(sc->nic_addr, PP_LineCTL,
1205                    cs_readreg(sc->nic_addr, PP_LineCTL) &
1206		    ~(SERIAL_RX_ON | SERIAL_TX_ON));
1207
1208#ifdef CS_DEBUG
1209	printf(CS_NAME"%d: cs_setmedia(%x)\n",sc->arpcom.ac_if.if_unit,media);
1210#endif
1211
1212	switch (IFM_SUBTYPE(media)) {
1213	default:
1214	case IFM_AUTO:
1215		if (error=enable_tp(sc))
1216       		if (error=enable_bnc(sc))
1217		    error=enable_aui(sc);
1218		break;
1219	case IFM_10_T:
1220		if (error=enable_tp(sc))
1221			break;
1222		if (media & IFM_FDX)
1223			cs_duplex_full(sc);
1224		else if (media & IFM_HDX)
1225			cs_duplex_half(sc);
1226		else
1227			error = cs_duplex_auto(sc);
1228		break;
1229	case IFM_10_2:
1230		error = enable_bnc(sc);
1231		break;
1232	case IFM_10_5:
1233		error = enable_aui(sc);
1234		break;
1235	}
1236
1237	/*
1238	 * Turn the transmitter & receiver back on
1239	 */
1240	cs_writereg(sc->nic_addr, PP_LineCTL,
1241		    cs_readreg( sc->nic_addr, PP_LineCTL ) |
1242		    SERIAL_RX_ON | SERIAL_TX_ON);
1243
1244	return error;
1245}
1246
1247
1248#if NPNP > 0
1249
1250static struct cspnp_ids {
1251	u_long	vend_id;
1252	char 	*id_str;
1253} cspnp_ids[]= {
1254	{ 0x4060630e, "CSC6040" },
1255	{ 0x10104d24, "IBM EtherJet" },
1256	{ 0 }
1257};
1258
1259static char *cs_pnp_probe(u_long, u_long);
1260static void cs_pnp_attach(u_long, u_long, char *, struct isa_device *);
1261
1262struct pnp_device cs_pnp = {
1263	"CS8920 based PnP Ethernet",
1264	cs_pnp_probe,
1265	cs_pnp_attach,
1266	&cs_unit,
1267	&net_imask	/* imask */
1268};
1269
1270DATA_SET (pnpdevice_set, cs_pnp);
1271
1272struct csintr_list {
1273	struct cs_softc *sc;
1274	int unit;
1275	struct csintr_list *next;
1276};
1277
1278static struct csintr_list *csintr_head;
1279
1280static void csintr_pnp_add(struct cs_softc *sc, int unit);
1281static void csintr_pnp(int unit);
1282
1283static void
1284csintr_pnp_add(struct cs_softc *sc, int unit)
1285{
1286    struct csintr_list *intr;
1287
1288    if (!sc) return;
1289
1290    intr = malloc (sizeof (*intr), M_DEVBUF, M_WAITOK);
1291    if (!intr) return;
1292
1293    intr->sc = sc;
1294    intr->unit = unit;
1295    intr->next = csintr_head;
1296    csintr_head = intr;
1297}
1298
1299/*
1300 * Interrupt handler for PNP installed card
1301 * We have to find the number of the card.
1302 */
1303static void
1304csintr_pnp(int unit)
1305{
1306    struct cs_softc *sc;
1307    struct csintr_list *intr;
1308
1309    for (intr=csintr_head; intr; intr=intr->next) {
1310	    if (intr->unit == unit)
1311		csintr_sc(intr->sc, unit);
1312		break;
1313	}
1314}
1315
1316static char *
1317cs_pnp_probe(u_long csn, u_long vend_id)
1318{
1319    struct cspnp_ids *ids;
1320    char	     *s=NULL;
1321
1322    for(ids = cspnp_ids; ids->vend_id != 0; ids++) {
1323	if (vend_id == ids->vend_id) {
1324	    s = ids->id_str;
1325	    break;
1326	}
1327    }
1328
1329    if (s) {
1330	struct pnp_cinfo d;
1331	int ldn = 0;
1332
1333	read_pnp_parms(&d, ldn);
1334	if (d.enable == 0) {
1335	    printf("This is a %s, but LDN %d is disabled\n", s, ldn);
1336	    return NULL ;
1337	}
1338	return s;
1339    }
1340
1341    return NULL ;
1342}
1343
1344static void
1345cs_pnp_attach(u_long csn, u_long vend_id, char *name,
1346	struct isa_device *dev)
1347{
1348
1349    struct pnp_cinfo d;
1350    int	ldn = 0;
1351    int iobase, unit, flags;
1352    u_short irq;
1353    short drq;
1354    struct isa_device *dvp;
1355    struct cs_softc *sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);
1356
1357    if (read_pnp_parms ( &d , ldn ) == 0 ) {
1358	printf("failed to read pnp parms\n");
1359	return;
1360    }
1361
1362    write_pnp_parms( &d, ldn );
1363    enable_pnp_card();
1364
1365    iobase = dev->id_iobase = d.port[0];
1366    irq = dev->id_irq = (1 << d.irq[0] );
1367    drq = dev->id_drq = d.drq[0];
1368    dev->id_maddr = 0;
1369    dev->id_intr = csintr_pnp;
1370    flags = dev->id_flags = 0;
1371    unit = dev->id_unit;
1372
1373    if (dev->id_driver == NULL) {
1374	dev->id_driver = &csdriver;
1375	dvp = find_isadev(isa_devtab_net, &csdriver, 0);
1376	if (dvp != NULL)
1377	dev->id_id = dvp->id_id;
1378    }
1379
1380    if (!sc) return;
1381
1382    bzero(sc, sizeof *sc);
1383    if (cs_cs89x0_probe(sc, &irq, &drq, iobase, unit, flags) == 0
1384	|| cs_attach(sc, unit, flags) == 0) {
1385	    free(sc, M_DEVBUF);
1386    } else {
1387	if ((irq != dev->id_irq)
1388	    || (drq != dev->id_drq)
1389	    || (iobase != dev->id_iobase)
1390	    || (unit != dev->id_unit)
1391	    || (flags != dev->id_flags)
1392		) {
1393		printf("failed to pnp card parametars\n");
1394	}
1395    }
1396    csintr_pnp_add(sc, dev->id_unit);
1397}
1398#endif /* NPNP */
1399