diff options
author | Jordan K. Hubbard <jkh@FreeBSD.org> | 1998-01-21 18:33:00 +0000 |
---|---|---|
committer | Jordan K. Hubbard <jkh@FreeBSD.org> | 1998-01-21 18:33:00 +0000 |
commit | 0aed583274aafde19ee4e59a503c3a2c53500f54 (patch) | |
tree | 7e84baeef6728bfdcb0a75697c3314924b70a0fe /sys/dev/tx/if_tx.c | |
parent | d8f643d7e10314a3de63d62149a56fbe2b934def (diff) | |
download | src-0aed583274aafde19ee4e59a503c3a2c53500f54.tar.gz src-0aed583274aafde19ee4e59a503c3a2c53500f54.zip |
Driver for the new SMC 9432TX cards.
Submitted by: Ustimenko Semen <semen@iclub.nsu.ru>
Notes
Notes:
svn path=/head/; revision=32679
Diffstat (limited to 'sys/dev/tx/if_tx.c')
-rw-r--r-- | sys/dev/tx/if_tx.c | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/sys/dev/tx/if_tx.c b/sys/dev/tx/if_tx.c new file mode 100644 index 000000000000..c2882cb6ebf2 --- /dev/null +++ b/sys/dev/tx/if_tx.c @@ -0,0 +1,1051 @@ +/*- + * Copyright (c) 1997 Semen Ustimenko (semen@iclub.nsu.ru) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * version: stable-163 + * + */ + +/* + * EtherPower II 10/100 Fast Ethernet (tx0) + * (aka SMC9432TX based on SMC83c170 EPIC chip) + * + * Written by Semen Ustimenko. + * + * TODO: + * Add IFF_MULTICAST support + * Fix serious collision counter behaviour + * Fix RX_TO_MBUF option + * + * stable-140: + * first stable version + * + * stable-160: + * added BPF support + * fixed several bugs + * + * stable-161: + * fixed BPF support + * fixed several bugs + * + * stable-162: + * fixed IFF_PROMISC mode support + * added speed info displayed at startup (MII info) + * + * stable-163: + * added media control code + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/ioctl.h> /* makes problem in FreeBSD 3.x */ +#include <machine/clock.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_mib.h> +#include <net/if_types.h> +#include <net/route.h> +#include <net/netisr.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_kern.h> +#include <vm/pmap.h> + +#include "pci.h" +#if NPCI > 0 +#include <pci/pcivar.h> +#include <pci/smc83c170.h> +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +/* + * Global variables + */ +static u_long epic_pci_count; +static epic_softc_t * epics[EPIC_MAX_DEVICES]; +struct pci_device txdevice = { + "tx", + epic_pci_probe, + epic_pci_attach, + &epic_pci_count, + NULL }; + +/* + * Append this driver to pci drivers list + */ +DATA_SET ( pcidevice_set, txdevice ); + +static int +epic_ifioctl(register struct ifnet * ifp, int command, caddr_t data){ + epic_softc_t *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *) data; + int x, error = 0; + u_int32_t media=0; + u_int32_t rxcon=0; + + x = splimp(); + + switch (command) { + + case SIOCSIFADDR: + case SIOCGIFADDR: + ether_ioctl(ifp, command, data); + break; + + case SIOCSIFFLAGS: + /* + * If the interface is marked up and stopped, then start it. + * If it is marked down and running, then stop it. + */ + if (ifp->if_flags & IFF_UP) { + if ((ifp->if_flags & IFF_RUNNING) == 0) + epic_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) { + epic_stop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } + } + + /* Set broadcast mode */ + if( ifp->if_flags & IFF_BROADCAST ) + rxcon |= RXCON_RECEIVE_BROADCAST_FRAMES; + else + rxcon &= ~RXCON_RECEIVE_BROADCAST_FRAMES; + + /* Set media */ + + if( ifp->if_flags & IFF_NOAUTONEG ){ + if( ifp->if_flags & IFF_100MB ) media |= 0x2000; + else media &= ~0x2000; + + if( ifp->if_flags & IFF_FULLDUPLEX ) + media |= 0x0100; + else + media &= ~0x0100; + + } else { + media |= 0x1200; /* Set and restart autoneg */ + } + +#if NBPFILTER > 0 + /* Set promisc mode */ + if( ifp->if_flags & IFF_PROMISC ) + sc->rxcon |= RXCON_PROMISCUOUS_MODE; + else + sc->rxcon &= ~RXCON_PROMISCUOUS_MODE; + +#endif + /* + * Update hardware if media changed + */ + if( sc->media != media ){ + if( media & 0x1000 ){ + printf("tx%d: autonegotiation started\n",sc->unit); + epic_write_phy_register( sc->iobase, 0, (sc->media=media) ); + ifp->if_timer=3; + sc->pending_txs++; + } else { + epic_write_phy_register( sc->iobase, 0, (sc->media=media) ); + epic_update_if_media_flags(sc); + printf("tx%d: %dMbps %s\n",sc->unit, + (ifp->if_flags&IFF_100MB)?100:10, + (ifp->if_flags&IFF_FULLDUPLEX)?"full-duplex":"half-duplex" ); + } + } + + if( sc->rxcon != rxcon ) + outl( sc->iobase + RXCON, (sc->rxcon = rxcon) ); + + break; + +#if 0 /* XXXXXXXX: no multicast filtering */ + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Update out multicast list. + */ + error = (command == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->arpcom) : + ether_delmulti(ifr, &sc->arpcom); + + if (error == ENETRESET) { + + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + ed_setrcr(sc); + error = 0; + } + break; +#endif + + case SIOCSIFMTU: + /* + * Set the interface MTU. + */ + if (ifr->ifr_mtu > ETHERMTU) { + error = EINVAL; + } else { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + + default: + error = EINVAL; + } + splx(x); + + return error; +} + +/* + * IFSTART function + */ +static void +epic_ifstart(struct ifnet * const ifp){ + epic_softc_t *sc = ifp->if_softc; + + while( sc->pending_txs < TX_RING_SIZE ){ + int entry = sc->cur_tx % TX_RING_SIZE; + struct epic_tx_buffer * buf = sc->tx_buffer + entry; + struct mbuf *m,*m0; + int len; + + if( buf->desc.status ) break; + + IF_DEQUEUE( &(sc->epic_if.if_snd), m ); + + if( NULL == m ) return; + + m0 = m; + + for (len = 0; m != 0; m = m->m_next) { + bcopy(mtod(m, caddr_t), buf->data + len, m->m_len); + len += m->m_len; + } + + buf->desc.txlength = max(len,ETHER_MIN_LEN-ETHER_CRC_LEN); + buf->desc.control = 0x14; /* Interrupt when done */ + buf->desc.status = 0x8000; /* Pass ownership to the chip */ + + /* Set watchdog timer */ + ifp->if_timer = 3; + +#if NBPFILTER > 0 + if( ifp->if_bpf ) bpf_mtap( ifp, m0 ); +#endif + + m_freem( m0 ); + + /* Trigger an immediate transmit demand. */ + outl(sc->iobase + COMMAND, 0x0004); + + sc->cur_tx = ( sc->cur_tx + 1 ) % TX_RING_SIZE; + sc->pending_txs++; + } + +#if defined(EPIC_DEBUG) + printf("tx%d: txring overflowed\n",sc->unit); +#endif + + sc->epic_if.if_flags |= IFF_OACTIVE; + + return; + +} + +/* + * IFWATCHDOG function + */ +static void +epic_ifwatchdog( + struct ifnet *ifp) +{ + epic_softc_t *sc = ifp->if_softc; + int x; + int i; + + x = splimp(); + if( sc->media & 0x0200 ){ + i=epic_read_phy_register( sc->iobase, 0 ); + if( i & 0x0200 ) { + printf("tx%d: autoneg failed\n",sc->unit); + sc->media=0xFFFFFFFF; + } else { + epic_update_if_media_flags(sc); + printf("tx%d: autonegotiated: %dMbps %s\n",sc->unit, + (ifp->if_flags&IFF_100MB)?100:10, + (ifp->if_flags&IFF_FULLDUPLEX)?"full-duplex":"half-duplex" ); + } + sc->pending_txs--; + sc->media &= ~0x0200; + } else { + printf("tx%d: device timeout %d packets\n", + sc->unit,sc->pending_txs); + + ifp->if_oerrors+=sc->pending_txs; + + epic_stop(sc); + epic_init(sc); + + epic_ifstart(&sc->epic_if); + } + + splx(x); +} + +/* + * Interrupt function + */ +static void +epic_intr_normal( + void *arg) +{ + epic_softc_t * sc = (epic_softc_t *) arg; + int iobase = sc->iobase; + int status; + int i; + + status = inl(iobase + INTSTAT); + + /* Acknowledge all of the current interrupt sources ASAP. */ + outl( iobase + INTSTAT, status & 0x00007fff); + + if( status & (INTSTAT_RQE|INTSTAT_RCC|INTSTAT_OVW) ) + epic_rx_done( sc ); + + if( status & (INTSTAT_TQE|INTSTAT_TCC|INTSTAT_TXU) ) + epic_tx_done( sc ); + /* + * UPDATE statistics + */ + if (status & (INTSTAT_CNT | INTSTAT_TXU | INTSTAT_OVW | INTSTAT_RXE)) { + /* + * update dot3 Rx statistics + */ + sc->dot3stats.dot3StatsMissedFrames += inb(iobase + MPCNT); + sc->dot3stats.dot3StatsFrameTooLongs += inb(iobase + ALICNT); + sc->dot3stats.dot3StatsFCSErrors += inb(iobase + CRCCNT); + + /* + * update if Rx statistics + */ + if (status & (INTSTAT_OVW | INTSTAT_RXE)) + sc->epic_if.if_ierrors++; + + /* Tx FIFO underflow. */ + if (status & INTSTAT_TXU) { + sc->dot3stats.dot3StatsInternalMacTransmitErrors++; + sc->epic_if.if_oerrors++; +#if defined(EPIC_DEBUG) + printf("tx%d: restart transmit\n",sc->unit); +#endif + /* Restart the transmit process. */ + outl(iobase + COMMAND, COMMAND_TXUGO); + } + + /* Clear all error sources. */ + outl(iobase + INTSTAT, status & 0x7f18); + } + + /* If no packets are pending, thus no timeouts */ + if( sc->pending_txs == 0 ){ + sc->epic_if.if_timer = 0; + if( sc->cur_tx != sc->dirty_tx ){ + printf("tx%d: algorithm error1\n",sc->unit); + } + } + + /* We should clear all interrupt sources. */ + outl(iobase + INTSTAT, 0x0001ffff ); + + return; +} + +void +epic_rx_done( + epic_softc_t *sc ) +{ + int i = 0; + u_int16_t len; + struct epic_rx_buffer * buf; + struct mbuf *m; + struct ether_header *eh; + + while( !(sc->rx_buffer[sc->cur_rx].desc.status & 0x8000) && \ + i++ < RX_RING_SIZE ){ + int stt; + + buf = sc->rx_buffer + sc->cur_rx; + stt = buf->desc.status; + +#if defined(EPIC_DEBUG) + printf("tx%d: ",sc->unit); + if(stt&1)printf("rsucc"); + else printf(" "); + if(stt&2)printf(" faer"); + else printf(" "); + if(stt&4)printf(" crer"); + else printf(" "); + if(stt&8)printf(" mpac"); + else printf(" "); + if(stt&16)printf(" mcas"); + else printf(" "); + if(stt&32)printf(" bcas\n"); + else printf(" \n"); +#endif + + if( !(buf->desc.status&1) ){ + sc->epic_if.if_ierrors++; + goto rxerror; + } + + len = buf->desc.rxlength - ETHER_CRC_LEN; + +#if !defined(RX_TO_MBUF) + /* + * Copy data to new allocated mbuf + */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if( NULL == m ) goto rxerror; + if( (len+2) > MHLEN ){ + MCLGET(m,M_DONTWAIT); + if( NULL == (m->m_flags & M_EXT) ){ + m_freem( m ); + goto rxerror; + } + } + m->m_data += 2; + + memcpy( mtod(m,void*), buf->data, len ); +#else + m = buf->mbuf; + + buf->mbuf = NULL; + + MGETHDR(buf->mbuf,M_DONTWAIT,MT_DATA); + if( NULL == buf->mbuf ) /* XXXXX: to panic */ + panic("tx: low mbufs"); /* or not to panic?*/ + MCLGET(buf->mbuf,M_DONTWAIT); + if( NULL == buf->mbuf ) + panic("tx: low mbufs"); + + buf->data = mtod( buf->mbuf, caddr_t ); + buf->desc.bufaddr = vtophys( buf->data ); + buf->desc.status = 0x8000; +#endif + + /* + * First mbuf in packet holds the + * ethernet and packet headers + */ + eh = mtod( m, struct ether_header * ); + m->m_pkthdr.rcvif = &(sc->epic_if); + m->m_pkthdr.len = len; + m->m_len = len; + +#if NBPFILTER > 0 + if( sc->epic_if.if_bpf ) bpf_mtap( &sc->epic_if, m ); +#if 0 + /* Accept only our packets */ + if( (eh->ether_dhost[0] & 1) == 0 && + bcmp(eh->ether_dhost,sc->epic_ac.ac_enaddr,ETHER_ADDR_LEN)){ + m_freem(m); + goto rxerror; + } +#endif +#endif + m->m_pkthdr.len = len - sizeof(struct ether_header); + m->m_len = len - sizeof( struct ether_header ); + m->m_data += sizeof( struct ether_header ); + +#if defined(EPIC_DEBUG) + printf("tx%d: received %d bytes\n",sc->unit,len); +#endif + + ether_input(&sc->epic_if, eh, m); + + sc->epic_if.if_ipackets++; + +rxerror: + /* + * Mark descriptor as free + */ + buf->desc.rxlength = 0; + buf->desc.status = 0x8000; + + sc->cur_rx = (sc->cur_rx+1) % RX_RING_SIZE; + } + + epic_ifstart( &sc->epic_if ); + + outl( sc->iobase + INTSTAT, INTSTAT_RCC ); +} + +void +epic_tx_done( epic_softc_t *sc ){ + int i = 0; + u_int32_t if_flags=~0; + int coll; + u_int16_t stt; + + while( i++ < TX_RING_SIZE ){ + struct epic_tx_buffer *buf = sc->tx_buffer + sc->dirty_tx; + u_int16_t len = buf->desc.txlength; + stt = buf->desc.status; + + if( stt & 0x8000 ) + break; /* following packets are not Txed yet */ + + if( stt == 0 ){ + if( sc->pending_txs != 0 || + sc->dirty_tx != sc->cur_tx ) + printf("tx%d: algoritm error\n",sc->unit); + if_flags = ~IFF_OACTIVE; + break; + } + +#if defined(EPIC_DEBUG) + printf("tx%d: ",sc->unit); + if(stt&1) printf(" succ"); + else printf(" "); + if(stt&2) printf(" ndef"); + else printf(" "); + if(stt&4) printf(" coll"); + else printf(" "); + if(stt&8) printf(" urun"); + else printf(" "); + if(stt&16) printf(" cdhb"); + else printf(" "); + if(stt&32) printf(" oowc"); + else printf(" "); + if(stt&64) printf(" deff"); + else printf(" "); + printf(" %d\n",(stt>>8)&0xF); +#endif + + sc->pending_txs--; /* packet is finished */ + sc->dirty_tx = (sc->dirty_tx + 1) % TX_RING_SIZE; + + coll = (stt >> 8) & 0xF; /* number of collisions*/ + + if( stt & 0x0001 ){ + sc->epic_if.if_opackets++; + } else { + if(stt & 0x0008) + sc->dot3stats.dot3StatsCarrierSenseErrors++; + + if(stt & 0x1050) + sc->dot3stats.dot3StatsInternalMacTransmitErrors++; + + if(stt & 0x1000) coll = 16; + + sc->epic_if.if_oerrors++; + } + + if(stt & 0x0002) /* What does it mean? */ + sc->dot3stats.dot3StatsDeferredTransmissions++; + + sc->epic_if.if_collisions += coll; + + switch( coll ){ + case 0: + break; + case 16: + sc->dot3stats.dot3StatsExcessiveCollisions++; + sc->dot3stats.dot3StatsCollFrequencies[15]++; + break; + case 1: + sc->dot3stats.dot3StatsSingleCollisionFrames++; + sc->dot3stats.dot3StatsCollFrequencies[0]++; + break; + default: + sc->dot3stats.dot3StatsMultipleCollisionFrames++; + sc->dot3stats.dot3StatsCollFrequencies[coll-1]++; + break; + } + + buf->desc.status = 0; + + if_flags = ~IFF_OACTIVE; + } + + sc->epic_if.if_flags &= if_flags; + + outl( sc->iobase + INTSTAT, INTSTAT_TCC ); + + if( !(sc->epic_if.if_flags & IFF_OACTIVE) ) + epic_ifstart( &sc->epic_if ); +} + +/* + * Probe function + */ +static char* +epic_pci_probe( + pcici_t config_id, + pcidi_t device_id) +{ + if (PCI_VENDORID(device_id) != SMC_VENDORID) + return NULL; + + if (PCI_CHIPID(device_id) == CHIPID_83C170) + return "SMC 83c170"; + + return NULL; +} + +/* + * PCI_Attach function + */ +static void +epic_pci_attach( + pcici_t config_id, + int unit) +{ + struct ifnet * ifp; + epic_softc_t *sc; + u_int32_t iobase; + u_int32_t irq; + int i; + int s; + int phy, phy_idx; + + /* + * Get iobase and irq level + */ + irq = PCI_CONF_READ(PCI_CFIT) & (0xFF); + if (!pci_map_port(config_id, PCI_CBIO,(u_short *) &iobase)) + return; + + /* + * Allocate and preinitialize softc structure + */ + sc = (epic_softc_t *) malloc(sizeof(epic_softc_t), M_DEVBUF, M_NOWAIT); + if (sc == NULL) return; + epics[ unit ] = sc; + + /* + * Zero softc structure + */ + bzero(sc, sizeof(epic_softc_t)); + + /* + * Initialize softc + */ + sc->unit = unit; + sc->iobase = iobase; + sc->irq = irq; + + /* Bring the chip out of low-power mode. */ + outl( iobase + GENCTL, 0x0000 ); + + /* Magic?! If we don't set this bit the MII interface won't work. */ + outl( iobase + TEST1, 0x0008 ); + + /* Read mac address */ + for (i = 0; i < ETHER_ADDR_LEN / sizeof( u_int16_t); i++) + ((u_int16_t *)sc->epic_macaddr)[i] = inw(iobase + LAN0 + i*4); + + printf("tx%d:",sc->unit); + printf(" address %02x:%02x:%02x:%02x:%02x:%02x,",sc->epic_macaddr[0],sc->epic_macaddr[1],sc->epic_macaddr[2],sc->epic_macaddr[3],sc->epic_macaddr[4],sc->epic_macaddr[5]); + printf(" type SMC9432TX ["); + + i = epic_read_phy_register(iobase,0); + if( i & 0x1000 ) printf("Auto-Neg."); + if( i & 0x2000 ) printf(" 100 Mb/s"); + else printf(" 10 Mb/s"); + if( i & 0x100 ) printf(" Full-duplex"); + else printf(" Half-duplex"); + printf("]\n"); + + /* + * Map interrupt + */ + if (!pci_map_int(config_id, epic_intr_normal, (void*) sc, &net_imask)) { + printf("tx%d: couldn't map interrupt\n",unit); + return; + } + + /* + * Fill ifnet structure + */ + s = splimp(); + + ifp = &sc->epic_if; + + ifp->if_unit = unit; + ifp->if_name = "tx"; + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST|IFF_SIMPLEX; /*IFF_MULTICAST*/ + ifp->if_ioctl = epic_ifioctl; + ifp->if_start = epic_ifstart; + ifp->if_watchdog = epic_ifwatchdog; + ifp->if_init = (if_init_f_t*)epic_init; + ifp->if_timer = 0; + ifp->if_output = ether_output; + ifp->if_linkmib = &sc->dot3stats; + ifp->if_linkmiblen = sizeof(struct ifmib_iso_8802_3); + + sc->dot3stats.dot3StatsEtherChipSet = + DOT3CHIPSET(dot3VendorSMC, + dot3ChipSetSMC83c170); + + sc->dot3stats.dot3Compliance = DOT3COMPLIANCE_COLLS; + + /* + * Attach to if manager + */ + if_attach(ifp); + ether_ifattach(ifp); + +#if NBPFILTER > 0 + bpfattach(ifp,DLT_EN10MB, sizeof(struct ether_header)); +#endif + + splx(s); + + return; +} + +/* + * IFINIT function + */ +static void +epic_init( + epic_softc_t * sc) +{ + struct ifnet *ifp = &sc->epic_if; + int iobase = sc->iobase; + int i; + + /* Soft reset the chip. */ + outl(iobase + GENCTL, GENCTL_SOFT_RESET ); + + /* Reset takes 15 ticks */ + for(i=0;i<0x100;i++); + + /* Wake up */ + outl( iobase + GENCTL, 0 ); + + /* ?????? */ + outl( iobase + TEST1, 0x0008); + + /* Initialize rings or reinitialize */ + epic_init_rings( sc ); + + epic_update_if_media_flags( sc ); + + /* Put node address to EPIC */ + outl( iobase + LAN0 + 0x0, ((u_int16_t *)sc->epic_macaddr)[0] ); + outl( iobase + LAN0 + 0x4, ((u_int16_t *)sc->epic_macaddr)[1] ); + outl( iobase + LAN0 + 0x8, ((u_int16_t *)sc->epic_macaddr)[2] ); + + /* Enable interrupts, set for PCI read multiple and etc */ + sc->genctl = GENCTL_ENABLE_INTERRUPT | GENCTL_MEMORY_READ_MULTIPLE | \ + GENCTL_ONECOPY | GENCTL_RECEIVE_FIFO_THRESHOLD128; + + outl( iobase + GENCTL, sc->genctl ); + + /* Set transmit threshold */ + outl( iobase + ETXTHR, 0x40 ); + + /* Compute rx mode. */ + if( sc->epic_if.if_flags & IFF_BROADCAST ) + sc->rxcon |= RXCON_RECEIVE_BROADCAST_FRAMES; + else + sc->rxcon &= ~RXCON_RECEIVE_BROADCAST_FRAMES; + + if( sc->epic_if.if_flags & IFF_PROMISC ) + sc->rxcon |= RXCON_PROMISCUOUS_MODE; + else + sc->rxcon &= ~RXCON_PROMISCUOUS_MODE; + + /* Set rx and tx modes */ + outl( iobase + RXCON, sc->rxcon ); + outl( iobase + TXCON, sc->txcon ); + + /* Enable interrupts by setting the interrupt mask. */ + outl( iobase + INTMASK, INTSTAT_CNT | INTSTAT_TXU | INTSTAT_TCC | + INTSTAT_RXE | INTSTAT_OVW | INTSTAT_RQE | INTSTAT_RCC ); + + /* Start rx process */ + outl( iobase + COMMAND, COMMAND_RXQUEUED | COMMAND_START_RX ); + + /* Mark interface running ... */ + if( ifp->if_flags & IFF_UP ) ifp->if_flags |= IFF_RUNNING; + else ifp->if_flags &= ~IFF_RUNNING; + + /* ... and free */ + ifp->if_flags &= ~IFF_OACTIVE; + +} + +static void +epic_stop( + epic_softc_t * sc) +{ + int iobase = sc->iobase; + + sc->genctl &= ~GENCTL_ENABLE_INTERRUPT; + outl( iobase + INTMASK, 0 ); + outl( iobase + COMMAND, COMMAND_STOP_RX | COMMAND_STOP_TDMA | \ + COMMAND_STOP_TDMA ); + + sc->epic_if.if_timer = 0; + +} + +static void +epic_update_if_media_flags( + epic_softc_t * sc) +{ + struct ifnet *ifp = &sc->epic_if; + + /* Read media control */ + sc->media = epic_read_phy_register(sc->iobase, 0); + + /* AutoNegotiation */ + if( sc->media & 0x1000 ) ifp->if_flags &= ~IFF_NOAUTONEG; + else ifp->if_flags |= IFF_NOAUTONEG; + + /* fullduplex */ + if( sc->media & 0x0100 ) { + ifp->if_flags |= IFF_FULLDUPLEX; + sc->txcon |= TXCON_LOOPBACK_MODE_FULL_DUPLEX; + } else { + ifp->if_flags &= ~IFF_FULLDUPLEX; + sc->txcon &= ~TXCON_LOOPBACK_MODE_FULL_DUPLEX; + } + outl( sc->iobase + TXCON, sc->txcon ); + + /* 10/100 MBit */ + if( sc->media & 0x2000 ) { + ifp->if_flags |= IFF_100MB; + ifp->if_baudrate = 100000000; + } else { + ifp->if_flags &= ~IFF_100MB; + ifp->if_baudrate = 10000000; + } + + return; +} + +/* + * Initialize Rx ad Tx rings and give them to EPIC + * + * If RX_TO_MBUF option is enabled, mbuf cluster is allocated instead of + * static buffer. + */ +static void +epic_init_rings(epic_softc_t * sc){ + int i; + struct mbuf *m; + + sc->cur_rx = sc->cur_tx = sc->dirty_tx = sc->pending_txs = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + struct epic_rx_buffer *buf = sc->rx_buffer + i; + + buf->desc.status = 0x0000; /* Owned by Epic chip */ + buf->desc.buflength = 0; + buf->desc.bufaddr = 0; + buf->desc.next = vtophys(&(sc->rx_buffer[(i+1)%RX_RING_SIZE].desc) ); + + buf->data = NULL; + +#if !defined(RX_TO_MBUF) + if( buf->pool ){ + free( buf->pool, M_DEVBUF ); + buf->pool = buf->data = 0; + } + buf->pool = malloc(ETHER_MAX_FRAME_LEN, M_DEVBUF, M_NOWAIT); + if( buf->pool == NULL ){ + printf("tx%d: malloc failed\n",sc->unit); + continue; + } + buf->data = (caddr_t)((u_int32_t)(buf->pool + 3) & (~0x3)); +#else + if( buf->mbuf ){ + m_freem( buf->mbuf ); + buf->mbuf = NULL; + } + MGETHDR(buf->mbuf,M_DONTWAIT,MT_DATA); + if( NULL == buf->mbuf ) continue; + MCLGET(buf->mbuf,M_DONTWAIT); + if( NULL == (buf->mbuf->m_flags & M_EXT) ){ + m_freem( buf->mbuf ); + continue; + } + + buf->data = mtod( buf->mbuf, caddr_t ); +#endif + buf->desc.bufaddr = vtophys( buf->data ); + buf->desc.buflength = ETHER_MAX_FRAME_LEN; + buf->desc.status = 0x8000; + + } + + for (i = 0; i < TX_RING_SIZE; i++) { + struct epic_tx_buffer *buf = sc->tx_buffer + i; + + buf->desc.status = 0x0000; + buf->desc.buflength = 0; + buf->desc.bufaddr = 0; + buf->desc.control = 0; + buf->desc.next = vtophys(&(sc->tx_buffer[(i+1)%TX_RING_SIZE].desc) ); + + if( buf->pool ){ + free( buf->pool, M_DEVBUF ); + buf->pool = buf->data = 0; + } + + buf->pool = malloc(ETHER_MAX_FRAME_LEN, M_DEVBUF, M_NOWAIT); + + if( buf->pool == NULL ){ + printf("tx%d: malloc failed\n",sc->unit); + continue; + } + + /* align on 4 bytes */ + buf->data = (caddr_t)((u_int32_t)(buf->pool + 3) & (~0x3)); + + buf->desc.bufaddr = vtophys( buf->data ); + buf->desc.buflength = ETHER_MAX_FRAME_LEN; + } + + /* Give rings to EPIC */ + outl( sc->iobase + PRCDAR, vtophys(&(sc->rx_buffer[0].desc)) ); + outl( sc->iobase + PTCDAR, vtophys(&(sc->tx_buffer[0].desc)) ); + +} + +/* + * EEPROM operation functions + */ +static void epic_write_eepromreg(u_int16_t regaddr, u_int8_t val){ + u_int16_t i; + + outb( regaddr, val ); + + for( i=0;i<0xFF; i++) + if( !(inb( regaddr ) & 0x20) ) break; + + return; +} + +static u_int8_t epic_read_eepromreg(u_int16_t regaddr){ + return inb( regaddr ); +} + +static u_int8_t epic_eeprom_clock( u_int16_t ioaddr, u_int8_t val ){ + + epic_write_eepromreg( ioaddr + EECTL, val ); + epic_write_eepromreg( ioaddr + EECTL, (val | 0x4) ); + epic_write_eepromreg( ioaddr + EECTL, val ); + + return epic_read_eepromreg( ioaddr + EECTL ); +} + +static void epic_output_eepromw(u_int16_t ioaddr, u_int16_t val){ + int i; + for( i = 0xF; i >= 0; i--){ + if( (val & (1 << i)) ) epic_eeprom_clock( ioaddr, 0x0B ); + else epic_eeprom_clock( ioaddr, 3); + } +} + +static u_int16_t epic_input_eepromw(u_int16_t ioaddr){ + int i; + int tmp; + u_int16_t retval = 0; + + for( i = 0xF; i >= 0; i--) { + tmp = epic_eeprom_clock( ioaddr, 0x3 ); + if( tmp & 0x10 ){ + retval |= (1 << i); + } + } + return retval; +} + +static int epic_read_eeprom(u_int16_t ioaddr, u_int16_t loc){ + int i; + u_int16_t dataval; + u_int16_t read_cmd; + + epic_write_eepromreg(ioaddr + EECTL , 3); + + if( epic_read_eepromreg(ioaddr + EECTL) & 0x40 ) + read_cmd = ( loc & 0x3F ) | 0x180; + else + read_cmd = ( loc & 0xFF ) | 0x600; + + epic_output_eepromw( ioaddr, read_cmd ); + + dataval = epic_input_eepromw( ioaddr ); + + epic_write_eepromreg( ioaddr + EECTL, 1 ); + + return dataval; +} + +static int epic_read_phy_register(u_int16_t iobase, u_int16_t loc){ + int i; + + outl( iobase + MIICTL, ((loc << 4) | 0x0601) ); + + for( i=0;i<0x1000;i++) if( !(inl( iobase + MIICTL )&1) ) break; + + return inl( iobase + MIIDATA ); +} + +static void epic_write_phy_register(u_int16_t iobase, u_int16_t loc,u_int16_t val){ + int i; + + outl( iobase + MIIDATA, val ); + outl( iobase + MIICTL, ((loc << 4) | 0x0602) ); + + for( i=0;i<0x1000;i++) if( !(inl( iobase + MIICTL )&2) ) break; + + return; +} |