1139749Simp/*-
25436Sdg * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
35436Sdg * All rights reserved.
45436Sdg *
5963Sats * Redistribution and use in source and binary forms, with or without
65436Sdg * modification, are permitted provided that the following conditions
75436Sdg * are met:
85436Sdg * 1. Redistributions of source code must retain the above copyright
95436Sdg *    notice, this list of conditions and the following disclaimer.
105436Sdg * 2. Redistributions in binary form must reproduce the above copyright
115436Sdg *    notice, this list of conditions and the following disclaimer in the
125436Sdg *    documentation and/or other materials provided with the distribution.
135436Sdg * 3. All advertising materials mentioning features or use of this software
145436Sdg *    must display the following acknowledgement:
155436Sdg *      This product includes software developed by Herb Peyerl.
165436Sdg * 4. The name of Herb Peyerl may not be used to endorse or promote products
175436Sdg *    derived from this software without specific prior written permission.
185436Sdg *
195436Sdg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
205436Sdg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
215436Sdg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
225436Sdg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
235436Sdg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
245436Sdg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
255436Sdg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
265436Sdg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
275436Sdg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
285436Sdg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29963Sats */
301444Sats
31119418Sobrien#include <sys/cdefs.h>
32119418Sobrien__FBSDID("$FreeBSD$");
33119418Sobrien
345863Sdg/*
355863Sdg *	Modified from the FreeBSD 1.1.5.1 version by:
368876Srgrimes *		 	Andres Vega Garcia
375863Sdg *			INRIA - Sophia Antipolis, France
385863Sdg *			avega@sophia.inria.fr
395863Sdg */
405863Sdg
417510Sjkh/*
427510Sjkh *  Promiscuous mode added and interrupt logic slightly changed
437510Sjkh *  to reduce the number of adapter failures. Transceiver select
447510Sjkh *  logic changed to use value from EEPROM. Autoconfiguration
457510Sjkh *  features added.
467510Sjkh *  Done by:
477510Sjkh *          Serge Babkin
487510Sjkh *          Chelindbank (Chelyabinsk, Russia)
497510Sjkh *          babkin@hq.icb.chel.su
507510Sjkh */
517510Sjkh
5216374Snate/*
5316374Snate * Pccard support for 3C589 by:
5416374Snate *		HAMADA Naoki
5516374Snate *		nao@tom-yam.or.jp
5616374Snate */
5716374Snate
5852549Smdodd/*
5952549Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net>
6052549Smdodd *                             <mdodd@FreeBSD.org>
6152549Smdodd */
621017Snate
631444Sats#include <sys/param.h>
641444Sats#include <sys/systm.h>
65199559Sjhb#include <sys/kernel.h>
661444Sats#include <sys/mbuf.h>
671444Sats#include <sys/socket.h>
6824204Sbde#include <sys/sockio.h>
6952472Simp#include <sys/bus.h>
70963Sats
7152549Smdodd#include <machine/bus.h>
7252549Smdodd#include <machine/resource.h>
73117700Smarkm#include <sys/rman.h>
7452549Smdodd
7552549Smdodd#include <net/if.h>
76152315Sru#include <net/if_dl.h>
77117700Smarkm#include <net/if_media.h>
78147256Sbrooks#include <net/if_types.h>
7950026Smdodd#include <net/ethernet.h>
8052549Smdodd#include <net/bpf.h>
8152549Smdodd
8251673Smdodd#include <dev/ep/if_epreg.h>
8351673Smdodd#include <dev/ep/if_epvar.h>
84963Sats
8514259Sgibbs/* Exported variables */
8652472Simpdevclass_t ep_devclass;
8712854Sbde
88117700Smarkmstatic int ep_media2if_media[] =
89117700Smarkm{IFM_10_T, IFM_10_5, IFM_NONE, IFM_10_2, IFM_NONE};
90963Sats
9152549Smdodd/* if functions */
92121492Simpstatic void epinit(void *);
93121492Simpstatic int epioctl(struct ifnet *, u_long, caddr_t);
94121492Simpstatic void epstart(struct ifnet *);
954435Sgibbs
96199559Sjhbstatic void ep_intr_locked(struct ep_softc *);
97121515Simpstatic void epstart_locked(struct ifnet *);
98121515Simpstatic void epinit_locked(struct ep_softc *);
99199559Sjhbstatic void eptick(void *);
100199559Sjhbstatic void epwatchdog(struct ep_softc *);
101121492Simp
10252549Smdodd/* if_media functions */
103117700Smarkmstatic int ep_ifmedia_upd(struct ifnet *);
104117700Smarkmstatic void ep_ifmedia_sts(struct ifnet *, struct ifmediareq *);
10552549Smdodd
106117700Smarkmstatic void epstop(struct ep_softc *);
107117700Smarkmstatic void epread(struct ep_softc *);
108117700Smarkmstatic int eeprom_rdy(struct ep_softc *);
10952549Smdodd
11052549Smdodd#define EP_FTST(sc, f)	(sc->stat &   (f))
11152549Smdodd#define EP_FSET(sc, f)	(sc->stat |=  (f))
11252549Smdodd#define EP_FRST(sc, f)	(sc->stat &= ~(f))
11352549Smdodd
11416374Snatestatic int
115117700Smarkmeeprom_rdy(struct ep_softc *sc)
1164435Sgibbs{
117117700Smarkm	int i;
1182478Sats
119121206Simp	for (i = 0; is_eeprom_busy(sc) && i < MAX_EEPROMBUSY; i++)
120117700Smarkm		DELAY(100);
121117700Smarkm
122117700Smarkm	if (i >= MAX_EEPROMBUSY) {
123147715Simp		device_printf(sc->dev, "eeprom failed to come ready.\n");
124121903Simp 		return (ENXIO);
125117700Smarkm	}
126117700Smarkm
127117700Smarkm	return (0);
1284435Sgibbs}
1292478Sats
1304435Sgibbs/*
1314435Sgibbs * get_e: gets a 16 bits word from the EEPROM. we must have set the window
1324435Sgibbs * before
1334435Sgibbs */
134112822Smdoddint
135147607Simpep_get_e(struct ep_softc *sc, uint16_t offset, uint16_t *result)
1364435Sgibbs{
137112822Smdodd
138112822Smdodd	if (eeprom_rdy(sc))
139112822Smdodd		return (ENXIO);
140117700Smarkm
141121492Simp	CSR_WRITE_2(sc, EP_W0_EEPROM_COMMAND,
142117700Smarkm	    (EEPROM_CMD_RD << sc->epb.cmd_off) | offset);
143117700Smarkm
144112822Smdodd	if (eeprom_rdy(sc))
145112822Smdodd		return (ENXIO);
146117700Smarkm
147121492Simp	(*result) = CSR_READ_2(sc, EP_W0_EEPROM_DATA);
148112822Smdodd
14956017Smdodd	return (0);
1504435Sgibbs}
1512478Sats
152147715Simpstatic int
153117700Smarkmep_get_macaddr(struct ep_softc *sc, u_char *addr)
15414259Sgibbs{
155117700Smarkm	int i;
156140523Simp	uint16_t result;
157117700Smarkm	int error;
158140523Simp	uint16_t *macaddr;
15914259Sgibbs
160140523Simp	macaddr = (uint16_t *) addr;
161112822Smdodd
162121588Simp	GO_WINDOW(sc, 0);
163117700Smarkm	for (i = EEPROM_NODE_ADDR_0; i <= EEPROM_NODE_ADDR_2; i++) {
164147607Simp		error = ep_get_e(sc, i, &result);
165112822Smdodd		if (error)
166112822Smdodd			return (error);
167112822Smdodd		macaddr[i] = htons(result);
168117700Smarkm	}
169112822Smdodd	return (0);
17052549Smdodd}
17114259Sgibbs
17252549Smdoddint
17352549Smdoddep_alloc(device_t dev)
17452549Smdodd{
175117700Smarkm	struct ep_softc *sc = device_get_softc(dev);
176117700Smarkm	int rid;
177117700Smarkm	int error = 0;
178140523Simp	uint16_t result;
17952549Smdodd
180117700Smarkm	rid = 0;
181127135Snjl	sc->iobase = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
182127135Snjl	    RF_ACTIVE);
183117700Smarkm	if (!sc->iobase) {
184117700Smarkm		device_printf(dev, "No I/O space?!\n");
18552549Smdodd		error = ENXIO;
186117700Smarkm		goto bad;
187117700Smarkm	}
188117700Smarkm	rid = 0;
189127135Snjl	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
190117700Smarkm	if (!sc->irq) {
191117700Smarkm		device_printf(dev, "No irq?!\n");
19252549Smdodd		error = ENXIO;
193117700Smarkm		goto bad;
194117700Smarkm	}
195117700Smarkm	sc->dev = dev;
196117700Smarkm	sc->stat = 0;		/* 16 bit access */
19752549Smdodd
198121206Simp	sc->bst = rman_get_bustag(sc->iobase);
199121206Simp	sc->bsh = rman_get_bushandle(sc->iobase);
20052549Smdodd
20152549Smdodd	sc->ep_connectors = 0;
20252549Smdodd	sc->ep_connector = 0;
20352549Smdodd
204121588Simp	GO_WINDOW(sc, 0);
20552549Smdodd
206147607Simp	error = ep_get_e(sc, EEPROM_PROD_ID, &result);
207112822Smdodd	if (error)
208112822Smdodd		goto bad;
209112822Smdodd	sc->epb.prod_id = result;
210112822Smdodd
211147607Simp	error = ep_get_e(sc, EEPROM_RESOURCE_CFG, &result);
212112822Smdodd	if (error)
213112822Smdodd		goto bad;
214112822Smdodd	sc->epb.res_cfg = result;
215112822Smdodd
21652549Smdoddbad:
217121903Simp	if (error != 0)
218121903Simp		ep_free(dev);
21952549Smdodd	return (error);
22014259Sgibbs}
22114259Sgibbs
22214259Sgibbsvoid
223117700Smarkmep_get_media(struct ep_softc *sc)
22414259Sgibbs{
225140523Simp	uint16_t config;
22652549Smdodd
227121588Simp	GO_WINDOW(sc, 0);
228121492Simp	config = CSR_READ_2(sc, EP_W0_CONFIG_CTRL);
229117700Smarkm	if (config & IS_AUI)
230117700Smarkm		sc->ep_connectors |= AUI;
231117700Smarkm	if (config & IS_BNC)
232117700Smarkm		sc->ep_connectors |= BNC;
233117700Smarkm	if (config & IS_UTP)
234117700Smarkm		sc->ep_connectors |= UTP;
235117700Smarkm
236117700Smarkm	if (!(sc->ep_connectors & 7))
23752549Smdodd		if (bootverbose)
238117700Smarkm			device_printf(sc->dev, "no connectors!\n");
23952549Smdodd
24052549Smdodd	/*
24152549Smdodd	 * This works for most of the cards so we'll do it here.
24252549Smdodd	 * The cards that require something different can override
24352549Smdodd	 * this later on.
24452549Smdodd	 */
245121492Simp	sc->ep_connector = CSR_READ_2(sc, EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS;
24614259Sgibbs}
24714259Sgibbs
24852549Smdoddvoid
24952549Smdoddep_free(device_t dev)
25052549Smdodd{
251117700Smarkm	struct ep_softc *sc = device_get_softc(dev);
25252549Smdodd
253112829Smdodd	if (sc->ep_intrhand)
254112829Smdodd		bus_teardown_intr(dev, sc->irq, sc->ep_intrhand);
25552549Smdodd	if (sc->iobase)
25652549Smdodd		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->iobase);
25752549Smdodd	if (sc->irq)
25852549Smdodd		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
259148581Simp	sc->ep_intrhand = 0;
260148581Simp	sc->iobase = 0;
261148581Simp	sc->irq = 0;
262117700Smarkm}
26352549Smdodd
264147715Simpstatic void
265147715Simpep_setup_station(struct ep_softc *sc, u_char *enaddr)
266147715Simp{
267147715Simp	int i;
268147715Simp
269147715Simp	/*
270147715Simp	 * Setup the station address
271147715Simp	 */
272147715Simp	GO_WINDOW(sc, 2);
273147715Simp	for (i = 0; i < ETHER_ADDR_LEN; i++)
274147715Simp		CSR_WRITE_1(sc, EP_W2_ADDR_0 + i, enaddr[i]);
275147715Simp}
276147715Simp
2774435Sgibbsint
278117700Smarkmep_attach(struct ep_softc *sc)
27914259Sgibbs{
280117700Smarkm	struct ifnet *ifp = NULL;
281117700Smarkm	struct ifmedia *ifm = NULL;
282117700Smarkm	int error;
28314259Sgibbs
28452549Smdodd	sc->gone = 0;
285121515Simp	EP_LOCK_INIT(sc);
286147715Simp	if (! (sc->stat & F_ENADDR_SKIP)) {
287147715Simp		error = ep_get_macaddr(sc, sc->eaddr);
288147715Simp		if (error) {
289147715Simp			device_printf(sc->dev, "Unable to get MAC address!\n");
290148164Simp			EP_LOCK_DESTROY(sc);
291147715Simp			return (ENXIO);
292147715Simp		}
293112822Smdodd	}
294147715Simp	ep_setup_station(sc, sc->eaddr);
295147256Sbrooks	ifp = sc->ifp = if_alloc(IFT_ETHER);
296147256Sbrooks	if (ifp == NULL) {
297147717Simp		device_printf(sc->dev, "if_alloc() failed\n");
298148164Simp		EP_LOCK_DESTROY(sc);
299147256Sbrooks		return (ENOSPC);
300147256Sbrooks	}
30114259Sgibbs
30252549Smdodd	ifp->if_softc = sc;
303121816Sbrooks	if_initname(ifp, device_get_name(sc->dev), device_get_unit(sc->dev));
30452549Smdodd	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
305121492Simp	ifp->if_start = epstart;
306121492Simp	ifp->if_ioctl = epioctl;
307121492Simp	ifp->if_init = epinit;
308207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
309207554Ssobomax	ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
310164810Smlaier	IFQ_SET_READY(&ifp->if_snd);
3111017Snate
312199559Sjhb	callout_init_mtx(&sc->watchdog_timer, &sc->sc_mtx, 0);
31352549Smdodd	if (!sc->epb.mii_trans) {
31452549Smdodd		ifmedia_init(&sc->ifmedia, 0, ep_ifmedia_upd, ep_ifmedia_sts);
3151017Snate
31652549Smdodd		if (sc->ep_connectors & AUI)
317119572Smarkm			ifmedia_add(&sc->ifmedia,
318119572Smarkm			    IFM_ETHER | IFM_10_5, 0, NULL);
31952549Smdodd		if (sc->ep_connectors & UTP)
320119572Smarkm			ifmedia_add(&sc->ifmedia,
321119572Smarkm			    IFM_ETHER | IFM_10_T, 0, NULL);
32252549Smdodd		if (sc->ep_connectors & BNC)
323119572Smarkm			ifmedia_add(&sc->ifmedia,
324119572Smarkm			    IFM_ETHER | IFM_10_2, 0, NULL);
32552549Smdodd		if (!sc->ep_connectors)
326119572Smarkm			ifmedia_add(&sc->ifmedia,
327119572Smarkm			    IFM_ETHER | IFM_NONE, 0, NULL);
328117700Smarkm
329119572Smarkm		ifmedia_set(&sc->ifmedia,
330119572Smarkm		    IFM_ETHER | ep_media2if_media[sc->ep_connector]);
331117700Smarkm
33252549Smdodd		ifm = &sc->ifmedia;
33352549Smdodd		ifm->ifm_media = ifm->ifm_cur->ifm_media;
33452549Smdodd		ep_ifmedia_upd(ifp);
33552549Smdodd	}
336147715Simp	ether_ifattach(ifp, sc->eaddr);
33752549Smdodd
3384435Sgibbs#ifdef EP_LOCAL_STATS
33952549Smdodd	sc->rx_no_first = sc->rx_no_mbuf = sc->rx_bpf_disc =
340117700Smarkm	    sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0;
3414435Sgibbs#endif
34252549Smdodd	EP_FSET(sc, F_RX_FIRST);
34352549Smdodd	sc->top = sc->mcur = 0;
3444435Sgibbs
34557981Smdodd	epstop(sc);
34657981Smdodd
347117700Smarkm	return (0);
348963Sats}
349963Sats
350112829Smdoddint
351112829Smdoddep_detach(device_t dev)
352112829Smdodd{
353112829Smdodd	struct ep_softc *sc;
354112829Smdodd	struct ifnet *ifp;
355112829Smdodd
356112829Smdodd	sc = device_get_softc(dev);
357150395Simp	ifp = sc->ifp;
358121515Simp	EP_ASSERT_UNLOCKED(sc);
359150395Simp	EP_LOCK(sc);
360119125Scognet	if (bus_child_present(dev))
361119125Scognet		epstop(sc);
362150395Simp	sc->gone = 1;
363148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
364150395Simp	EP_UNLOCK(sc);
365112829Smdodd	ether_ifdetach(ifp);
366199559Sjhb	callout_drain(&sc->watchdog_timer);
367150395Simp	ep_free(dev);
368112829Smdodd
369150306Simp	if_free(ifp);
370148164Simp	EP_LOCK_DESTROY(sc);
371112829Smdodd
372112829Smdodd	return (0);
373112829Smdodd}
374112829Smdodd
375121492Simpstatic void
376121492Simpepinit(void *xsc)
377121492Simp{
378121492Simp	struct ep_softc *sc = xsc;
379121492Simp	EP_LOCK(sc);
380121515Simp	epinit_locked(sc);
381121492Simp	EP_UNLOCK(sc);
382121492Simp}
383121492Simp
3841017Snate/*
3851017Snate * The order in here seems important. Otherwise we may not receive
3861017Snate * interrupts. ?!
3871017Snate */
38812724Sphkstatic void
389121515Simpepinit_locked(struct ep_softc *sc)
390963Sats{
391147256Sbrooks	struct ifnet *ifp = sc->ifp;
392121492Simp	int i;
393963Sats
394117700Smarkm	if (sc->gone)
395117700Smarkm		return;
39616374Snate
397121515Simp	EP_ASSERT_LOCKED(sc);
398121515Simp	EP_BUSY_WAIT(sc);
399963Sats
400121588Simp	GO_WINDOW(sc, 0);
401121492Simp	CSR_WRITE_2(sc, EP_COMMAND, STOP_TRANSCEIVER);
402121588Simp	GO_WINDOW(sc, 4);
403121492Simp	CSR_WRITE_2(sc, EP_W4_MEDIA_TYPE, DISABLE_UTP);
404121588Simp	GO_WINDOW(sc, 0);
405963Sats
406117700Smarkm	/* Disable the card */
407121492Simp	CSR_WRITE_2(sc, EP_W0_CONFIG_CTRL, 0);
408963Sats
409117700Smarkm	/* Enable the card */
410121492Simp	CSR_WRITE_2(sc, EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ);
411963Sats
412121588Simp	GO_WINDOW(sc, 2);
413117700Smarkm	/* Reload the ether_addr. */
414152315Sru	ep_setup_station(sc, IF_LLADDR(sc->ifp));
415963Sats
416121492Simp	CSR_WRITE_2(sc, EP_COMMAND, RX_RESET);
417121492Simp	CSR_WRITE_2(sc, EP_COMMAND, TX_RESET);
418121515Simp	EP_BUSY_WAIT(sc);
4191018Snate
420117700Smarkm	/* Window 1 is operating window */
421121588Simp	GO_WINDOW(sc, 1);
422117700Smarkm	for (i = 0; i < 31; i++)
423121492Simp		CSR_READ_1(sc, EP_W1_TX_STATUS);
424963Sats
425117700Smarkm	/* get rid of stray intr's */
426121492Simp	CSR_WRITE_2(sc, EP_COMMAND, ACK_INTR | 0xff);
427963Sats
428121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_RD_0_MASK | S_5_INTS);
429121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_INTR_MASK | S_5_INTS);
430963Sats
431117700Smarkm	if (ifp->if_flags & IFF_PROMISC)
432121492Simp		CSR_WRITE_2(sc, EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
433121492Simp		    FIL_MULTICAST | FIL_BRDCST | FIL_PROMISC);
434117700Smarkm	else
435121492Simp		CSR_WRITE_2(sc, EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL |
436121492Simp		    FIL_MULTICAST | FIL_BRDCST);
437963Sats
438117700Smarkm	if (!sc->epb.mii_trans)
439117700Smarkm		ep_ifmedia_upd(ifp);
4404435Sgibbs
441190908Simp	if (sc->stat & F_HAS_TX_PLL)
442190908Simp		CSR_WRITE_2(sc, EP_COMMAND, TX_PLL_ENABLE);
443121492Simp	CSR_WRITE_2(sc, EP_COMMAND, RX_ENABLE);
444121492Simp	CSR_WRITE_2(sc, EP_COMMAND, TX_ENABLE);
4457510Sjkh
446148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
447148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;	/* just in case */
448963Sats
4494435Sgibbs#ifdef EP_LOCAL_STATS
450117700Smarkm	sc->rx_no_first = sc->rx_no_mbuf =
451117700Smarkm	    sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0;
4524435Sgibbs#endif
453117700Smarkm	EP_FSET(sc, F_RX_FIRST);
454117700Smarkm	if (sc->top) {
455117700Smarkm		m_freem(sc->top);
456117700Smarkm		sc->top = sc->mcur = 0;
457117700Smarkm	}
458121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
459121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_TX_START_THRESH | 16);
4607749Sjkh
461121588Simp	GO_WINDOW(sc, 1);
462121515Simp	epstart_locked(ifp);
463199559Sjhb	callout_reset(&sc->watchdog_timer, hz, eptick, sc);
464963Sats}
465963Sats
46612724Sphkstatic void
467121492Simpepstart(struct ifnet *ifp)
468963Sats{
469117700Smarkm	struct ep_softc *sc;
470121492Simp	sc = ifp->if_softc;
471121492Simp	EP_LOCK(sc);
472121515Simp	epstart_locked(ifp);
473121492Simp	EP_UNLOCK(sc);
474121492Simp}
475121492Simp
476121492Simpstatic void
477121515Simpepstart_locked(struct ifnet *ifp)
478121492Simp{
479121492Simp	struct ep_softc *sc;
480117700Smarkm	u_int len;
481117700Smarkm	struct mbuf *m, *m0;
482190908Simp	int pad, started;
483963Sats
484117700Smarkm	sc = ifp->if_softc;
485117700Smarkm	if (sc->gone)
486117700Smarkm		return;
487121515Simp	EP_ASSERT_LOCKED(sc);
488121515Simp	EP_BUSY_WAIT(sc);
489148887Srwatson	if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
490117700Smarkm		return;
491190908Simp	started = 0;
492963Satsstartagain:
493117700Smarkm	/* Sneak a peek at the next packet */
494164810Smlaier	IFQ_DRV_DEQUEUE(&ifp->if_snd, m0);
495117700Smarkm	if (m0 == NULL)
496117700Smarkm		return;
497190908Simp	if (!started && (sc->stat & F_HAS_TX_PLL))
498190908Simp		CSR_WRITE_2(sc, EP_COMMAND, TX_PLL_ENABLE);
499190908Simp	started++;
500117700Smarkm	for (len = 0, m = m0; m != NULL; m = m->m_next)
501117700Smarkm		len += m->m_len;
502963Sats
503117700Smarkm	pad = (4 - len) & 3;
5041017Snate
505117700Smarkm	/*
506119572Smarkm	 * The 3c509 automatically pads short packets to minimum
507119572Smarkm	 * ethernet length, but we drop packets that are too large.
508119572Smarkm	 * Perhaps we should truncate them instead?
509119572Smarkm	 */
510117700Smarkm	if (len + pad > ETHER_MAX_LEN) {
511117700Smarkm		/* packet is obviously too large: toss it */
512117700Smarkm		ifp->if_oerrors++;
513117700Smarkm		m_freem(m0);
514117700Smarkm		goto readcheck;
515117700Smarkm	}
516121492Simp	if (CSR_READ_2(sc, EP_W1_FREE_TX) < len + pad + 4) {
517117700Smarkm		/* no room in FIFO */
518121492Simp		CSR_WRITE_2(sc, EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4));
519117700Smarkm		/* make sure */
520121492Simp		if (CSR_READ_2(sc, EP_W1_FREE_TX) < len + pad + 4) {
521148887Srwatson			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
522164810Smlaier			IFQ_DRV_PREPEND(&ifp->if_snd, m0);
523121492Simp			goto done;
524117700Smarkm		}
525117700Smarkm	} else
526121492Simp		CSR_WRITE_2(sc, EP_COMMAND,
527119572Smarkm		    SET_TX_AVAIL_THRESH | EP_THRESH_DISABLE);
52855834Smdodd
529121492Simp	/* XXX 4.x and earlier would splhigh here */
53055834Smdodd
531121492Simp	CSR_WRITE_2(sc, EP_W1_TX_PIO_WR_1, len);
532119572Smarkm	/* Second dword meaningless */
533121492Simp	CSR_WRITE_2(sc, EP_W1_TX_PIO_WR_1, 0x0);
534963Sats
535117700Smarkm	if (EP_FTST(sc, F_ACCESS_32_BITS)) {
536117700Smarkm		for (m = m0; m != NULL; m = m->m_next) {
537117700Smarkm			if (m->m_len > 3)
538121492Simp				CSR_WRITE_MULTI_4(sc, EP_W1_TX_PIO_WR_1,
539121206Simp				    mtod(m, uint32_t *), m->m_len / 4);
540117700Smarkm			if (m->m_len & 3)
541121492Simp				CSR_WRITE_MULTI_1(sc, EP_W1_TX_PIO_WR_1,
542121206Simp				    mtod(m, uint8_t *)+(m->m_len & (~3)),
543119572Smarkm				    m->m_len & 3);
544117700Smarkm		}
545117700Smarkm	} else {
546117700Smarkm		for (m = m0; m != NULL; m = m->m_next) {
547117700Smarkm			if (m->m_len > 1)
548121492Simp				CSR_WRITE_MULTI_2(sc, EP_W1_TX_PIO_WR_1,
549121206Simp				    mtod(m, uint16_t *), m->m_len / 2);
550117700Smarkm			if (m->m_len & 1)
551121492Simp				CSR_WRITE_1(sc, EP_W1_TX_PIO_WR_1,
552121206Simp				    *(mtod(m, uint8_t *)+m->m_len - 1));
553117700Smarkm		}
55455834Smdodd	}
555963Sats
556117700Smarkm	while (pad--)
557121492Simp		CSR_WRITE_1(sc, EP_W1_TX_PIO_WR_1, 0);	/* Padding */
5584435Sgibbs
559121492Simp	/* XXX and drop splhigh here */
56055834Smdodd
561117700Smarkm	BPF_MTAP(ifp, m0);
562963Sats
563199559Sjhb	sc->tx_timer = 2;
564117700Smarkm	ifp->if_opackets++;
565117700Smarkm	m_freem(m0);
5661017Snate
567117700Smarkm	/*
568119572Smarkm	 * Is another packet coming in? We don't want to overflow
569119572Smarkm	 * the tiny RX fifo.
570119572Smarkm	 */
5714435Sgibbsreadcheck:
572121492Simp	if (CSR_READ_2(sc, EP_W1_RX_STATUS) & RX_BYTES_MASK) {
573117700Smarkm		/*
574119572Smarkm		 * we check if we have packets left, in that case
575119572Smarkm		 * we prepare to come back later
576117700Smarkm		 */
577164810Smlaier		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
578121492Simp			CSR_WRITE_2(sc, EP_COMMAND, SET_TX_AVAIL_THRESH | 8);
579121492Simp		goto done;
580117700Smarkm	}
581117700Smarkm	goto startagain;
582121492Simpdone:;
583121492Simp	return;
584963Sats}
585963Sats
58614259Sgibbsvoid
587117700Smarkmep_intr(void *arg)
58814259Sgibbs{
589117700Smarkm	struct ep_softc *sc;
590199559Sjhb
591199559Sjhb	sc = (struct ep_softc *) arg;
592199559Sjhb	EP_LOCK(sc);
593199559Sjhb	ep_intr_locked(sc);
594199559Sjhb	EP_UNLOCK(sc);
595199559Sjhb}
596199559Sjhb
597199559Sjhbstatic void
598199559Sjhbep_intr_locked(struct ep_softc *sc)
599199559Sjhb{
600117700Smarkm	int status;
601117700Smarkm	struct ifnet *ifp;
602963Sats
603121492Simp	/* XXX 4.x splbio'd here to reduce interruptability */
60414259Sgibbs
605117700Smarkm	/*
606119572Smarkm	 * quick fix: Try to detect an interrupt when the card goes away.
607119572Smarkm	 */
608199559Sjhb	if (sc->gone || CSR_READ_2(sc, EP_STATUS) == 0xffff)
609117700Smarkm		return;
610147256Sbrooks	ifp = sc->ifp;
61152472Simp
612121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_INTR_MASK);	/* disable all Ints */
61314091Sgibbs
6147267Sdgrescan:
6154435Sgibbs
616121492Simp	while ((status = CSR_READ_2(sc, EP_STATUS)) & S_5_INTS) {
6177510Sjkh
618117700Smarkm		/* first acknowledge all interrupt sources */
619121492Simp		CSR_WRITE_2(sc, EP_COMMAND, ACK_INTR | (status & S_MASK));
6207510Sjkh
621117700Smarkm		if (status & (S_RX_COMPLETE | S_RX_EARLY))
622117700Smarkm			epread(sc);
623117700Smarkm		if (status & S_TX_AVAIL) {
624117700Smarkm			/* we need ACK */
625199559Sjhb			sc->tx_timer = 0;
626148887Srwatson			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
627121588Simp			GO_WINDOW(sc, 1);
628121492Simp			CSR_READ_2(sc, EP_W1_FREE_TX);
629121515Simp			epstart_locked(ifp);
630117700Smarkm		}
631117700Smarkm		if (status & S_CARD_FAILURE) {
632199559Sjhb			sc->tx_timer = 0;
6334435Sgibbs#ifdef EP_LOCAL_STATS
634147715Simp			device_printf(sc->dev, "\n\tStatus: %x\n", status);
635121588Simp			GO_WINDOW(sc, 4);
636119572Smarkm			printf("\tFIFO Diagnostic: %x\n",
637121492Simp			    CSR_READ_2(sc, EP_W4_FIFO_DIAG));
638117700Smarkm			printf("\tStat: %x\n", sc->stat);
639117700Smarkm			printf("\tIpackets=%d, Opackets=%d\n",
640117700Smarkm			    ifp->if_ipackets, ifp->if_opackets);
641117700Smarkm			printf("\tNOF=%d, NOMB=%d, RXOF=%d, RXOL=%d, TXU=%d\n",
642117700Smarkm			    sc->rx_no_first, sc->rx_no_mbuf, sc->rx_overrunf,
643117700Smarkm			    sc->rx_overrunl, sc->tx_underrun);
6444435Sgibbs#else
64513692Sgibbs
64613692Sgibbs#ifdef DIAGNOSTIC
647147715Simp			device_printf(sc->dev,
648147715Simp			    "Status: %x (input buffer overflow)\n", status);
64913692Sgibbs#else
650117700Smarkm			++ifp->if_ierrors;
6514435Sgibbs#endif
65213692Sgibbs
65313692Sgibbs#endif
654121515Simp			epinit_locked(sc);
655117700Smarkm			return;
656117700Smarkm		}
657117700Smarkm		if (status & S_TX_COMPLETE) {
658199559Sjhb			sc->tx_timer = 0;
659117700Smarkm			/*
660119572Smarkm			 * We need ACK. We do it at the end.
661119572Smarkm			 *
662119572Smarkm		         * We need to read TX_STATUS until we get a
663119572Smarkm			 * 0 status in order to turn off the interrupt flag.
664117700Smarkm		         */
665121492Simp			while ((status = CSR_READ_1(sc, EP_W1_TX_STATUS)) &
666119572Smarkm			    TXS_COMPLETE) {
667144996Smdodd				if (status & TXS_SUCCES_INTR_REQ)
668144996Smdodd					;	/* nothing */
669119572Smarkm				else if (status &
670119572Smarkm				    (TXS_UNDERRUN | TXS_JABBER |
671119572Smarkm				    TXS_MAX_COLLISION)) {
672121492Simp					CSR_WRITE_2(sc, EP_COMMAND, TX_RESET);
673117700Smarkm					if (status & TXS_UNDERRUN) {
6744435Sgibbs#ifdef EP_LOCAL_STATS
675117700Smarkm						sc->tx_underrun++;
6764435Sgibbs#endif
677117700Smarkm					}
678196984Simp					if (status & TXS_MAX_COLLISION) {
679196984Simp						/*
680196984Simp						 * TXS_MAX_COLLISION we
681196984Simp						 * shouldn't get here
682196984Simp						 */
683196984Simp						++ifp->if_collisions;
684196984Simp					}
685117700Smarkm					++ifp->if_oerrors;
686121492Simp					CSR_WRITE_2(sc, EP_COMMAND, TX_ENABLE);
687117700Smarkm					/*
688119572Smarkm				         * To have a tx_avail_int but giving
689119572Smarkm					 * the chance to the Reception
690117700Smarkm				         */
691164810Smlaier					if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
692121492Simp						CSR_WRITE_2(sc, EP_COMMAND,
693119572Smarkm						    SET_TX_AVAIL_THRESH | 8);
694117700Smarkm				}
695119572Smarkm				/* pops up the next status */
696121492Simp				CSR_WRITE_1(sc, EP_W1_TX_STATUS, 0x0);
697117700Smarkm			}	/* while */
698148887Srwatson			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
699121588Simp			GO_WINDOW(sc, 1);
700121492Simp			CSR_READ_2(sc, EP_W1_FREE_TX);
701121515Simp			epstart_locked(ifp);
702119572Smarkm		}	/* end TX_COMPLETE */
703117700Smarkm	}
7047267Sdg
705121492Simp	CSR_WRITE_2(sc, EP_COMMAND, C_INTR_LATCH);	/* ACK int Latch */
7067267Sdg
707121492Simp	if ((status = CSR_READ_2(sc, EP_STATUS)) & S_5_INTS)
708117700Smarkm		goto rescan;
7097635Sjkh
710117700Smarkm	/* re-enable Ints */
711121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_INTR_MASK | S_5_INTS);
712963Sats}
713963Sats
71412724Sphkstatic void
715117700Smarkmepread(struct ep_softc *sc)
716963Sats{
717117700Smarkm	struct mbuf *top, *mcur, *m;
718117700Smarkm	struct ifnet *ifp;
719117700Smarkm	int lenthisone;
720117700Smarkm	short rx_fifo2, status;
721117700Smarkm	short rx_fifo;
722963Sats
723121492Simp/* XXX Must be called with sc locked */
724121492Simp
725147256Sbrooks	ifp = sc->ifp;
726121492Simp	status = CSR_READ_2(sc, EP_W1_RX_STATUS);
7274435Sgibbs
7284435Sgibbsread_again:
7294435Sgibbs
730117700Smarkm	if (status & ERR_RX) {
731117700Smarkm		++ifp->if_ierrors;
732117700Smarkm		if (status & ERR_RX_OVERRUN) {
733117700Smarkm			/*
734119572Smarkm		         * We can think the rx latency is actually
735119572Smarkm			 * greather than we expect
736117700Smarkm		         */
7374435Sgibbs#ifdef EP_LOCAL_STATS
738117700Smarkm			if (EP_FTST(sc, F_RX_FIRST))
739117700Smarkm				sc->rx_overrunf++;
740117700Smarkm			else
741117700Smarkm				sc->rx_overrunl++;
7424435Sgibbs#endif
743117700Smarkm		}
744117700Smarkm		goto out;
7451017Snate	}
746117700Smarkm	rx_fifo = rx_fifo2 = status & RX_BYTES_MASK;
747963Sats
748117700Smarkm	if (EP_FTST(sc, F_RX_FIRST)) {
749243857Sglebius		MGETHDR(m, M_NOWAIT, MT_DATA);
750117700Smarkm		if (!m)
751117700Smarkm			goto out;
752117700Smarkm		if (rx_fifo >= MINCLSIZE)
753243857Sglebius			MCLGET(m, M_NOWAIT);
754117700Smarkm		sc->top = sc->mcur = top = m;
7551017Snate#define EROUND  ((sizeof(struct ether_header) + 3) & ~3)
7561017Snate#define EOFF    (EROUND - sizeof(struct ether_header))
757117700Smarkm		top->m_data += EOFF;
7584435Sgibbs
759117700Smarkm		/* Read what should be the header. */
760121492Simp		CSR_READ_MULTI_2(sc, EP_W1_RX_PIO_RD_1,
761121206Simp		    mtod(top, uint16_t *), sizeof(struct ether_header) / 2);
762117700Smarkm		top->m_len = sizeof(struct ether_header);
763117700Smarkm		rx_fifo -= sizeof(struct ether_header);
764117700Smarkm		sc->cur_len = rx_fifo2;
765963Sats	} else {
766117700Smarkm		/* come here if we didn't have a complete packet last time */
767117700Smarkm		top = sc->top;
768117700Smarkm		m = sc->mcur;
769117700Smarkm		sc->cur_len += rx_fifo2;
770963Sats	}
7711017Snate
772117700Smarkm	/* Reads what is left in the RX FIFO */
773117700Smarkm	while (rx_fifo > 0) {
774117700Smarkm		lenthisone = min(rx_fifo, M_TRAILINGSPACE(m));
775117700Smarkm		if (lenthisone == 0) {	/* no room in this one */
776117700Smarkm			mcur = m;
777243857Sglebius			MGET(m, M_NOWAIT, MT_DATA);
778117700Smarkm			if (!m)
779117700Smarkm				goto out;
780117700Smarkm			if (rx_fifo >= MINCLSIZE)
781243857Sglebius				MCLGET(m, M_NOWAIT);
782117700Smarkm			m->m_len = 0;
783117700Smarkm			mcur->m_next = m;
784117700Smarkm			lenthisone = min(rx_fifo, M_TRAILINGSPACE(m));
785117700Smarkm		}
786119572Smarkm		if (EP_FTST(sc, F_ACCESS_32_BITS)) {
787119572Smarkm			/* default for EISA configured cards */
788121492Simp			CSR_READ_MULTI_4(sc, EP_W1_RX_PIO_RD_1,
789121388Simp			    (uint32_t *)(mtod(m, caddr_t)+m->m_len),
790117700Smarkm			    lenthisone / 4);
791117700Smarkm			m->m_len += (lenthisone & ~3);
792117700Smarkm			if (lenthisone & 3)
793121492Simp				CSR_READ_MULTI_1(sc, EP_W1_RX_PIO_RD_1,
794119572Smarkm				    mtod(m, caddr_t)+m->m_len, lenthisone & 3);
795117700Smarkm			m->m_len += (lenthisone & 3);
796117700Smarkm		} else {
797121492Simp			CSR_READ_MULTI_2(sc, EP_W1_RX_PIO_RD_1,
798121399Simp			    (uint16_t *)(mtod(m, caddr_t)+m->m_len),
799121388Simp			    lenthisone / 2);
800117700Smarkm			m->m_len += lenthisone;
801117700Smarkm			if (lenthisone & 1)
802119572Smarkm				*(mtod(m, caddr_t)+m->m_len - 1) =
803121492Simp				    CSR_READ_1(sc, EP_W1_RX_PIO_RD_1);
804117700Smarkm		}
805117700Smarkm		rx_fifo -= lenthisone;
806117700Smarkm	}
807117700Smarkm
808119572Smarkm	if (status & ERR_RX_INCOMPLETE) {
809119572Smarkm		/* we haven't received the complete packet */
810117700Smarkm		sc->mcur = m;
8114435Sgibbs#ifdef EP_LOCAL_STATS
812119572Smarkm		/* to know how often we come here */
813119572Smarkm		sc->rx_no_first++;
8144435Sgibbs#endif
815117700Smarkm		EP_FRST(sc, F_RX_FIRST);
816121492Simp		status = CSR_READ_2(sc, EP_W1_RX_STATUS);
817201794Strasz		if (!(status & ERR_RX_INCOMPLETE)) {
818117700Smarkm			/*
819119572Smarkm			 * We see if by now, the packet has completly
820117700Smarkm			 * arrived
821117700Smarkm			 */
822117700Smarkm			goto read_again;
823117700Smarkm		}
824121492Simp		CSR_WRITE_2(sc, EP_COMMAND,
825119572Smarkm		    SET_RX_EARLY_THRESH | RX_NEXT_EARLY_THRESH);
826117700Smarkm		return;
8274435Sgibbs	}
828121492Simp	CSR_WRITE_2(sc, EP_COMMAND, RX_DISCARD_TOP_PACK);
829117700Smarkm	++ifp->if_ipackets;
830117700Smarkm	EP_FSET(sc, F_RX_FIRST);
831147256Sbrooks	top->m_pkthdr.rcvif = sc->ifp;
832117700Smarkm	top->m_pkthdr.len = sc->cur_len;
833117700Smarkm
834121492Simp	/*
835121492Simp	 * Drop locks before calling if_input() since it may re-enter
836121492Simp	 * ep_start() in the netisr case.  This would result in a
837121492Simp	 * lock reversal.  Better performance might be obtained by
838121492Simp	 * chaining all packets received, dropping the lock, and then
839121492Simp	 * calling if_input() on each one.
840121492Simp	 */
841121492Simp	EP_UNLOCK(sc);
842117700Smarkm	(*ifp->if_input) (ifp, top);
843121492Simp	EP_LOCK(sc);
844117700Smarkm	sc->top = 0;
845121515Simp	EP_BUSY_WAIT(sc);
846121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
8474435Sgibbs	return;
8484435Sgibbs
8494435Sgibbsout:
850121492Simp	CSR_WRITE_2(sc, EP_COMMAND, RX_DISCARD_TOP_PACK);
851117700Smarkm	if (sc->top) {
852117700Smarkm		m_freem(sc->top);
853117700Smarkm		sc->top = 0;
8544435Sgibbs#ifdef EP_LOCAL_STATS
855117700Smarkm		sc->rx_no_mbuf++;
8564435Sgibbs#endif
857117700Smarkm	}
858117700Smarkm	EP_FSET(sc, F_RX_FIRST);
859121515Simp	EP_BUSY_WAIT(sc);
860121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH);
861963Sats}
862963Sats
863117700Smarkmstatic int
864117700Smarkmep_ifmedia_upd(struct ifnet *ifp)
865963Sats{
866117700Smarkm	struct ep_softc *sc = ifp->if_softc;
867117700Smarkm	int i = 0, j;
8681017Snate
869121588Simp	GO_WINDOW(sc, 0);
870121492Simp	CSR_WRITE_2(sc, EP_COMMAND, STOP_TRANSCEIVER);
871121588Simp	GO_WINDOW(sc, 4);
872121492Simp	CSR_WRITE_2(sc, EP_W4_MEDIA_TYPE, DISABLE_UTP);
873121588Simp	GO_WINDOW(sc, 0);
8749404Sdg
87552549Smdodd	switch (IFM_SUBTYPE(sc->ifmedia.ifm_media)) {
876117700Smarkm	case IFM_10_T:
877117700Smarkm		if (sc->ep_connectors & UTP) {
878117700Smarkm			i = ACF_CONNECTOR_UTP;
879121588Simp			GO_WINDOW(sc, 4);
880121492Simp			CSR_WRITE_2(sc, EP_W4_MEDIA_TYPE, ENABLE_UTP);
881117700Smarkm		}
882117700Smarkm		break;
883117700Smarkm	case IFM_10_2:
884117700Smarkm		if (sc->ep_connectors & BNC) {
885117700Smarkm			i = ACF_CONNECTOR_BNC;
886121492Simp			CSR_WRITE_2(sc, EP_COMMAND, START_TRANSCEIVER);
887117700Smarkm			DELAY(DELAY_MULTIPLE * 1000);
888117700Smarkm		}
889117700Smarkm		break;
890117700Smarkm	case IFM_10_5:
891117700Smarkm		if (sc->ep_connectors & AUI)
892117700Smarkm			i = ACF_CONNECTOR_AUI;
893117700Smarkm		break;
894117700Smarkm	default:
895117700Smarkm		i = sc->ep_connector;
896117700Smarkm		device_printf(sc->dev,
897117700Smarkm		    "strange connector type in EEPROM: assuming AUI\n");
89852549Smdodd	}
89913692Sgibbs
900121588Simp	GO_WINDOW(sc, 0);
901121492Simp	j = CSR_READ_2(sc, EP_W0_ADDRESS_CFG) & 0x3fff;
902121492Simp	CSR_WRITE_2(sc, EP_W0_ADDRESS_CFG, j | (i << ACF_CONNECTOR_BITS));
90313692Sgibbs
90452549Smdodd	return (0);
90552549Smdodd}
9067510Sjkh
90752549Smdoddstatic void
908117700Smarkmep_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
90952549Smdodd{
910117700Smarkm	struct ep_softc *sc = ifp->if_softc;
911190907Simp	uint16_t ms;
91252549Smdodd
913190907Simp	switch (IFM_SUBTYPE(sc->ifmedia.ifm_media)) {
914190907Simp	case IFM_10_T:
915190907Simp		GO_WINDOW(sc, 4);
916190907Simp		ms = CSR_READ_2(sc, EP_W4_MEDIA_TYPE);
917190907Simp		GO_WINDOW(sc, 0);
918190907Simp		ifmr->ifm_status = IFM_AVALID;
919190907Simp		if (ms & MT_LB) {
920190907Simp			ifmr->ifm_status |= IFM_ACTIVE;
921190907Simp			ifmr->ifm_active = IFM_ETHER | IFM_10_T;
922190907Simp		} else {
923190907Simp			ifmr->ifm_active = IFM_ETHER | IFM_NONE;
924190907Simp		}
925192158Sbrueffer		break;
926190907Simp	default:
927190907Simp		ifmr->ifm_active = sc->ifmedia.ifm_media;
928190907Simp		break;
929190907Simp	}
93052549Smdodd}
93152549Smdodd
93252549Smdoddstatic int
933121492Simpepioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
93452549Smdodd{
935117700Smarkm	struct ep_softc *sc = ifp->if_softc;
936117700Smarkm	struct ifreq *ifr = (struct ifreq *) data;
937121492Simp	int error = 0;
93852549Smdodd
93952549Smdodd	switch (cmd) {
94052549Smdodd	case SIOCSIFFLAGS:
941121492Simp		EP_LOCK(sc);
94252549Smdodd		if (((ifp->if_flags & IFF_UP) == 0) &&
943148887Srwatson		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
94452549Smdodd			epstop(sc);
945117700Smarkm		} else
94652549Smdodd			/* reinitialize card on any parameter change */
947121515Simp			epinit_locked(sc);
948121492Simp		EP_UNLOCK(sc);
94952549Smdodd		break;
95013692Sgibbs	case SIOCADDMULTI:
95113692Sgibbs	case SIOCDELMULTI:
95252549Smdodd		/*
95352549Smdodd		 * The Etherlink III has no programmable multicast
95452549Smdodd		 * filter.  We always initialize the card to be
95552549Smdodd		 * promiscuous to multicast, since we're always a
95652549Smdodd		 * member of the ALL-SYSTEMS group, so there's no
95752549Smdodd		 * need to process SIOC*MULTI requests.
95852549Smdodd		 */
95952549Smdodd		error = 0;
96052549Smdodd		break;
96152549Smdodd	case SIOCSIFMEDIA:
96252549Smdodd	case SIOCGIFMEDIA:
963117700Smarkm		if (!sc->epb.mii_trans)
96456017Smdodd			error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd);
965117700Smarkm		else
96656017Smdodd			error = EINVAL;
96752549Smdodd		break;
96852549Smdodd	default:
969106937Ssam		error = ether_ioctl(ifp, cmd, data);
97052549Smdodd		break;
97152549Smdodd	}
97252549Smdodd	return (error);
973963Sats}
974963Sats
97512724Sphkstatic void
976199559Sjhbeptick(void *arg)
977963Sats{
978199559Sjhb	struct ep_softc *sc;
97916374Snate
980199559Sjhb	sc = arg;
981199559Sjhb	if (sc->tx_timer != 0 && --sc->tx_timer == 0)
982199559Sjhb		epwatchdog(sc);
983199559Sjhb	callout_reset(&sc->watchdog_timer, hz, eptick, sc);
984199559Sjhb}
985199559Sjhb
986199559Sjhbstatic void
987199559Sjhbepwatchdog(struct ep_softc *sc)
988199559Sjhb{
989199559Sjhb	struct ifnet *ifp;
990199559Sjhb
991199559Sjhb	ifp = sc->ifp;
992117700Smarkm	if (sc->gone)
993117700Smarkm		return;
994148887Srwatson	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
995199559Sjhb	epstart_locked(ifp);
996199559Sjhb	ep_intr_locked(sc);
997963Sats}
998963Sats
99912724Sphkstatic void
1000117700Smarkmepstop(struct ep_softc *sc)
1001963Sats{
1002121492Simp	CSR_WRITE_2(sc, EP_COMMAND, RX_DISABLE);
1003121492Simp	CSR_WRITE_2(sc, EP_COMMAND, RX_DISCARD_TOP_PACK);
1004121515Simp	EP_BUSY_WAIT(sc);
100516374Snate
1006121492Simp	CSR_WRITE_2(sc, EP_COMMAND, TX_DISABLE);
1007121492Simp	CSR_WRITE_2(sc, EP_COMMAND, STOP_TRANSCEIVER);
1008117700Smarkm	DELAY(800);
100957983Smdodd
1010121492Simp	CSR_WRITE_2(sc, EP_COMMAND, RX_RESET);
1011121515Simp	EP_BUSY_WAIT(sc);
1012121492Simp	CSR_WRITE_2(sc, EP_COMMAND, TX_RESET);
1013121515Simp	EP_BUSY_WAIT(sc);
101457983Smdodd
1015121492Simp	CSR_WRITE_2(sc, EP_COMMAND, C_INTR_LATCH);
1016121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_RD_0_MASK);
1017121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_INTR_MASK);
1018121492Simp	CSR_WRITE_2(sc, EP_COMMAND, SET_RX_FILTER);
1019199559Sjhb
1020199559Sjhb	sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
1021199559Sjhb	callout_stop(&sc->watchdog_timer);
1022963Sats}
1023