147133Sobrien/*-
2121099Srsm * Copyright (c) 1998, 1999, 2003  Scott Mitchell
347133Sobrien * All rights reserved.
447133Sobrien *
547133Sobrien * Redistribution and use in source and binary forms, with or without
647133Sobrien * modification, are permitted provided that the following conditions
747133Sobrien * are met:
847133Sobrien * 1. Redistributions of source code must retain the above copyright
947133Sobrien *    notice, this list of conditions and the following disclaimer.
1047133Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1147133Sobrien *    notice, this list of conditions and the following disclaimer in the
1247133Sobrien *    documentation and/or other materials provided with the distribution.
1347133Sobrien *
1447133Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1547133Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1647133Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1747133Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1847133Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1947133Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2047133Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2147133Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2247133Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2347133Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2447133Sobrien * SUCH DAMAGE.
2547133Sobrien */
26139749Simp/*-
2747133Sobrien * Portions of this software were derived from Werner Koch's xirc2ps driver
2847133Sobrien * for Linux under the terms of the following license (from v1.30 of the
2947133Sobrien * xirc2ps driver):
3047133Sobrien *
3147133Sobrien * Copyright (c) 1997 by Werner Koch (dd9jn)
3247133Sobrien *
3347133Sobrien * Redistribution and use in source and binary forms, with or without
3447133Sobrien * modification, are permitted provided that the following conditions
3547133Sobrien * are met:
3647133Sobrien * 1. Redistributions of source code must retain the above copyright
3747133Sobrien *    notice, and the entire permission notice in its entirety,
3847133Sobrien *    including the disclaimer of warranties.
3947133Sobrien * 2. Redistributions in binary form must reproduce the above copyright
4047133Sobrien *    notice, this list of conditions and the following disclaimer in the
4147133Sobrien *    documentation and/or other materials provided with the distribution.
4247133Sobrien * 3. The name of the author may not be used to endorse or promote
4347133Sobrien *    products derived from this software without specific prior
4447133Sobrien *    written permission.
4547133Sobrien *
4647133Sobrien * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
4747133Sobrien * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
4847133Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
4947133Sobrien * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
5047133Sobrien * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
5147133Sobrien * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
5247133Sobrien * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5347133Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
5447133Sobrien * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
5547133Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
5647133Sobrien * OF THE POSSIBILITY OF SUCH DAMAGE.
5747133Sobrien */
5847133Sobrien
59122625Sobrien#include <sys/cdefs.h>
60122625Sobrien__FBSDID("$FreeBSD$");
61122625Sobrien
6247133Sobrien/*
6347145Sobrien * FreeBSD device driver for Xircom CreditCard PCMCIA Ethernet adapters.  The
6447151Sobrien * following cards are currently known to work with the driver:
6547151Sobrien *   Xircom CreditCard 10/100 (CE3)
6648117Sobrien *   Xircom CreditCard Ethernet + Modem 28 (CEM28)
6747151Sobrien *   Xircom CreditCard Ethernet 10/100 + Modem 56 (CEM56)
6848117Sobrien *   Xircom RealPort Ethernet 10
6947151Sobrien *   Xircom RealPort Ethernet 10/100
7047151Sobrien *   Xircom RealPort Ethernet 10/100 + Modem 56 (REM56, REM56G)
7147151Sobrien *   Intel EtherExpress Pro/100 PC Card Mobile Adapter 16 (Pro/100 M16A)
7247151Sobrien *   Compaq Netelligent 10/100 PC Card (CPQ-10/100)
7347133Sobrien *
7447151Sobrien * Some other cards *should* work, but support for them is either broken or in
7547151Sobrien * an unknown state at the moment.  I'm always interested in hearing from
7647151Sobrien * people who own any of these cards:
7747151Sobrien *   Xircom CreditCard 10Base-T (PS-CE2-10)
7847151Sobrien *   Xircom CreditCard Ethernet + ModemII (CEM2)
7947151Sobrien *   Xircom CEM28 and CEM33 Ethernet/Modem cards (may be variants of CEM2?)
8047151Sobrien *
8147145Sobrien * Thanks to all who assisted with the development and testing of the driver,
8247151Sobrien * especially: Werner Koch, Duke Kamstra, Duncan Barclay, Jason George, Dru
8347151Sobrien * Nelson, Mike Kephart, Bill Rainey and Douglas Rand.  Apologies if I've left
8447151Sobrien * out anyone who deserves a mention here.
8547133Sobrien *
8647151Sobrien * Special thanks to Ade Lovett for both hosting the mailing list and doing
8747151Sobrien * the CEM56/REM56 support code; and the FreeBSD UK Users' Group for hosting
8847151Sobrien * the web pages.
8947136Sobrien *
90121099Srsm * Author email: <scott@uk.freebsd.org>
9147151Sobrien * Driver web page: http://ukug.uk.freebsd.org/~scott/xe_drv/
9247133Sobrien */
9347133Sobrien
9447151Sobrien
9547133Sobrien#include <sys/param.h>
9647133Sobrien#include <sys/cdefs.h>
9747133Sobrien#include <sys/errno.h>
9847133Sobrien#include <sys/kernel.h>
9947133Sobrien#include <sys/mbuf.h>
10047133Sobrien#include <sys/socket.h>
10147133Sobrien#include <sys/sockio.h>
10247133Sobrien#include <sys/systm.h>
10347133Sobrien#include <sys/uio.h>
104122081Srsm#include <sys/sysctl.h>
10547133Sobrien
10655723Simp#include <sys/module.h>
10755723Simp#include <sys/bus.h>
10855723Simp
10955723Simp#include <machine/bus.h>
11055723Simp#include <machine/resource.h>
11155723Simp#include <sys/rman.h>
11255723Simp
11347133Sobrien#include <net/ethernet.h>
11447133Sobrien#include <net/if.h>
11547133Sobrien#include <net/if_arp.h>
11647133Sobrien#include <net/if_dl.h>
11747133Sobrien#include <net/if_media.h>
11847133Sobrien#include <net/if_mib.h>
11947133Sobrien#include <net/bpf.h>
120147256Sbrooks#include <net/if_types.h>
12147133Sobrien
12255723Simp#include <dev/xe/if_xereg.h>
12355723Simp#include <dev/xe/if_xevar.h>
12455723Simp
12547133Sobrien/*
12647136Sobrien * MII command structure
12747136Sobrien */
12847136Sobrienstruct xe_mii_frame {
129179551Sjhb	uint8_t		mii_stdelim;
130179551Sjhb	uint8_t		mii_opcode;
131179551Sjhb	uint8_t		mii_phyaddr;
132179551Sjhb	uint8_t		mii_regaddr;
133179551Sjhb	uint8_t		mii_turnaround;
134179551Sjhb	uint16_t	mii_data;
13547136Sobrien};
13647133Sobrien
13747133Sobrien/*
13847136Sobrien * Media autonegotiation progress constants
13947133Sobrien */
140179551Sjhb#define	XE_AUTONEG_NONE		0	/* No autonegotiation in progress */
141179551Sjhb#define	XE_AUTONEG_WAITING	1	/* Waiting for transmitter to go idle */
142179551Sjhb#define	XE_AUTONEG_STARTED	2	/* Waiting for autonegotiation to complete */
143179551Sjhb#define	XE_AUTONEG_100TX	3	/* Trying to force 100baseTX link */
144179551Sjhb#define	XE_AUTONEG_FAIL		4	/* Autonegotiation failed */
14547133Sobrien
146121099Srsm/*
14747136Sobrien * Prototypes start here
14847133Sobrien */
149179551Sjhbstatic void	xe_init(void *xscp);
150179551Sjhbstatic void	xe_init_locked(struct xe_softc *scp);
151179551Sjhbstatic void	xe_start(struct ifnet *ifp);
152179551Sjhbstatic void	xe_start_locked(struct ifnet *ifp);
153179551Sjhbstatic int	xe_ioctl(struct ifnet *ifp, u_long command, caddr_t data);
154179551Sjhbstatic void	xe_watchdog(void *arg);
155179551Sjhbstatic void	xe_intr(void *xscp);
156179551Sjhbstatic void	xe_txintr(struct xe_softc *scp, uint8_t txst1);
157179551Sjhbstatic void	xe_macintr(struct xe_softc *scp, uint8_t rst0, uint8_t txst0,
158179551Sjhb		    uint8_t txst1);
159179551Sjhbstatic void	xe_rxintr(struct xe_softc *scp, uint8_t rst0);
160179551Sjhbstatic int	xe_media_change(struct ifnet *ifp);
161179551Sjhbstatic void	xe_media_status(struct ifnet *ifp, struct ifmediareq *mrp);
162179551Sjhbstatic void	xe_setmedia(void *arg);
163179551Sjhbstatic void	xe_reset(struct xe_softc *scp);
164179551Sjhbstatic void	xe_enable_intr(struct xe_softc *scp);
165179551Sjhbstatic void	xe_disable_intr(struct xe_softc *scp);
166179551Sjhbstatic void	xe_set_multicast(struct xe_softc *scp);
167179551Sjhbstatic void	xe_set_addr(struct xe_softc *scp, uint8_t* addr, unsigned idx);
168179551Sjhbstatic void	xe_mchash(struct xe_softc *scp, const uint8_t *addr);
169179551Sjhbstatic int	xe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp);
17047133Sobrien
17147133Sobrien/*
17247136Sobrien * MII functions
17347133Sobrien */
174179551Sjhbstatic void	xe_mii_sync(struct xe_softc *scp);
175179551Sjhbstatic int	xe_mii_init(struct xe_softc *scp);
176179551Sjhbstatic void	xe_mii_send(struct xe_softc *scp, uint32_t bits, int cnt);
177179551Sjhbstatic int	xe_mii_readreg(struct xe_softc *scp,
178179551Sjhb		    struct xe_mii_frame *frame);
179179551Sjhbstatic int	xe_mii_writereg(struct xe_softc *scp,
180179551Sjhb		    struct xe_mii_frame *frame);
181179551Sjhbstatic uint16_t	xe_phy_readreg(struct xe_softc *scp, uint16_t reg);
182179551Sjhbstatic void	xe_phy_writereg(struct xe_softc *scp, uint16_t reg,
183179551Sjhb		    uint16_t data);
18447133Sobrien
185122081Srsm/*
186122081Srsm * Debugging functions
187122081Srsm */
188179551Sjhbstatic void	xe_mii_dump(struct xe_softc *scp);
189122170Srsm#if 0
190179551Sjhbstatic void	xe_reg_dump(struct xe_softc *scp);
191122170Srsm#endif
192121099Srsm
19347133Sobrien/*
194122081Srsm * Debug logging levels - set with hw.xe.debug sysctl
195122081Srsm * 0 = None
196122081Srsm * 1 = More hardware details, probe/attach progress
197122081Srsm * 2 = Most function calls, ioctls and media selection progress
198122081Srsm * 3 = Everything - interrupts, packets in/out and multicast address setup
19947133Sobrien */
200179551Sjhb#define	XE_DEBUG
201122081Srsm#ifdef XE_DEBUG
202121099Srsm
203122081Srsm/* sysctl vars */
204227309Sedstatic SYSCTL_NODE(_hw, OID_AUTO, xe, CTLFLAG_RD, 0, "if_xe parameters");
205122081Srsm
206122081Srsmint xe_debug = 0;
207179551SjhbSYSCTL_INT(_hw_xe, OID_AUTO, debug, CTLFLAG_RW, &xe_debug, 0,
208179551Sjhb    "if_xe debug level");
209122081Srsm
210179551Sjhb#define	DEVPRINTF(level, arg)	if (xe_debug >= (level)) device_printf arg
211179551Sjhb#define	DPRINTF(level, arg)	if (xe_debug >= (level)) printf arg
212179551Sjhb#define	XE_MII_DUMP(scp)	if (xe_debug >= 3) xe_mii_dump(scp)
213122170Srsm#if 0
214179551Sjhb#define	XE_REG_DUMP(scp)	if (xe_debug >= 3) xe_reg_dump(scp)
215122170Srsm#endif
21647136Sobrien#else
217179551Sjhb#define	DEVPRINTF(level, arg)
218179551Sjhb#define	DPRINTF(level, arg)
219179551Sjhb#define	XE_MII_DUMP(scp)
220122170Srsm#if 0
221179551Sjhb#define	XE_REG_DUMP(scp)
22247133Sobrien#endif
223122170Srsm#endif
22447133Sobrien
22547133Sobrien/*
22690962Sshiba * Attach a device.
22747148Sobrien */
22890962Sshibaint
229179551Sjhbxe_attach(device_t dev)
23047148Sobrien{
231179551Sjhb	struct xe_softc *scp = device_get_softc(dev);
232179551Sjhb	int err;
23355723Simp
234179551Sjhb	DEVPRINTF(2, (dev, "attach\n"));
23555723Simp
236179551Sjhb	/* Initialise stuff... */
237179551Sjhb	scp->dev = dev;
238179551Sjhb	scp->ifp = if_alloc(IFT_ETHER);
239179551Sjhb	if (scp->ifp == NULL)
240179551Sjhb		return (ENOSPC);
241179551Sjhb	scp->ifm = &scp->ifmedia;
242179551Sjhb	scp->autoneg_status = XE_AUTONEG_NONE;
243179551Sjhb	mtx_init(&scp->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
244179551Sjhb	    MTX_DEF);
245179551Sjhb	callout_init_mtx(&scp->wdog_timer, &scp->lock, 0);
24647136Sobrien
247179551Sjhb	/* Initialise the ifnet structure */
248179551Sjhb	scp->ifp->if_softc = scp;
249179551Sjhb	if_initname(scp->ifp, device_get_name(dev), device_get_unit(dev));
250179551Sjhb	scp->ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
251179551Sjhb	scp->ifp->if_linkmib = &scp->mibdata;
252179551Sjhb	scp->ifp->if_linkmiblen = sizeof(scp->mibdata);
253179551Sjhb	scp->ifp->if_start = xe_start;
254179551Sjhb	scp->ifp->if_ioctl = xe_ioctl;
255179551Sjhb	scp->ifp->if_init = xe_init;
256179551Sjhb	scp->ifp->if_baudrate = 100000000;
257207554Ssobomax	IFQ_SET_MAXLEN(&scp->ifp->if_snd, ifqmaxlen);
25847136Sobrien
259179551Sjhb	/* Initialise the ifmedia structure */
260179551Sjhb	ifmedia_init(scp->ifm, 0, xe_media_change, xe_media_status);
261179551Sjhb	callout_init_mtx(&scp->media_timer, &scp->lock, 0);
26247136Sobrien
263179551Sjhb	/* Add supported media types */
264179551Sjhb	if (scp->mohawk) {
265179551Sjhb		ifmedia_add(scp->ifm, IFM_ETHER|IFM_100_TX, 0, NULL);
266179551Sjhb		ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
267179551Sjhb		ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
268179551Sjhb	}
269179551Sjhb	ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T, 0, NULL);
270179551Sjhb	if (scp->ce2)
271179551Sjhb		ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_2, 0, NULL);
272179551Sjhb	ifmedia_add(scp->ifm, IFM_ETHER|IFM_AUTO, 0, NULL);
27347136Sobrien
274179551Sjhb	/* Default is to autoselect best supported media type */
275179551Sjhb	ifmedia_set(scp->ifm, IFM_ETHER|IFM_AUTO);
27647136Sobrien
277179551Sjhb	/* Get the hardware into a known state */
278179551Sjhb	XE_LOCK(scp);
279179551Sjhb	xe_reset(scp);
280179551Sjhb	XE_UNLOCK(scp);
281121099Srsm
282179551Sjhb	/* Get hardware version numbers */
283179551Sjhb	XE_SELECT_PAGE(4);
284179551Sjhb	scp->version = XE_INB(XE_BOV);
285179551Sjhb	if (scp->mohawk)
286179551Sjhb		scp->srev = (XE_INB(XE_BOV) & 0x70) >> 4;
287179551Sjhb	else
288179551Sjhb		scp->srev = (XE_INB(XE_BOV) & 0x30) >> 4;
289121099Srsm
290179551Sjhb	/* Print some useful information */
291179551Sjhb	device_printf(dev, "version 0x%02x/0x%02x%s%s\n", scp->version,
292179551Sjhb	    scp->srev, scp->mohawk ? ", 100Mbps capable" : "",
293179551Sjhb	    scp->modem ?  ", with modem" : "");
294179551Sjhb	if (scp->mohawk) {
295179551Sjhb		XE_SELECT_PAGE(0x10);
296179551Sjhb		DEVPRINTF(1, (dev,
297179551Sjhb		    "DingoID=0x%04x, RevisionID=0x%04x, VendorID=0x%04x\n",
298179551Sjhb		    XE_INW(XE_DINGOID), XE_INW(XE_RevID), XE_INW(XE_VendorID)));
299179551Sjhb	}
300179551Sjhb	if (scp->ce2) {
301179551Sjhb		XE_SELECT_PAGE(0x45);
302179551Sjhb		DEVPRINTF(1, (dev, "CE2 version = 0x%02x\n", XE_INB(XE_REV)));
303179551Sjhb	}
30447133Sobrien
305179551Sjhb	/* Attach the interface */
306179551Sjhb	ether_ifattach(scp->ifp, scp->enaddr);
30747133Sobrien
308179551Sjhb	err = bus_setup_intr(dev, scp->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
309179551Sjhb	    NULL, xe_intr, scp, &scp->intrhand);
310179551Sjhb	if (err) {
311179551Sjhb		ether_ifdetach(scp->ifp);
312179551Sjhb		mtx_destroy(&scp->lock);
313179551Sjhb		return (err);
314179551Sjhb	}
315179492Sjhb
316179551Sjhb	/* Done */
317179551Sjhb	return (0);
31847133Sobrien}
31947133Sobrien
32047133Sobrien/*
321121099Srsm * Complete hardware intitialisation and enable output.  Exits without doing
322121099Srsm * anything if there's no address assigned to the card, or if media selection
323121099Srsm * is in progress (the latter implies we've already run this function).
32447133Sobrien */
32547133Sobrienstatic void
326179551Sjhbxe_init(void *xscp)
327179551Sjhb{
328179551Sjhb	struct xe_softc *scp = xscp;
329179492Sjhb
330179551Sjhb	XE_LOCK(scp);
331179551Sjhb	xe_init_locked(scp);
332179551Sjhb	XE_UNLOCK(scp);
333179492Sjhb}
334179492Sjhb
335179492Sjhbstatic void
336179551Sjhbxe_init_locked(struct xe_softc *scp)
337179551Sjhb{
338179551Sjhb	unsigned i;
33947133Sobrien
340179551Sjhb	if (scp->autoneg_status != XE_AUTONEG_NONE)
341179551Sjhb		return;
342121099Srsm
343179551Sjhb	DEVPRINTF(2, (scp->dev, "init\n"));
34447133Sobrien
345179551Sjhb	/* Reset transmitter flags */
346179551Sjhb	scp->tx_queued = 0;
347179551Sjhb	scp->tx_tpr = 0;
348179551Sjhb	scp->tx_timeouts = 0;
349179551Sjhb	scp->tx_thres = 64;
350179551Sjhb	scp->tx_min = ETHER_MIN_LEN - ETHER_CRC_LEN;
351179551Sjhb	scp->tx_timeout = 0;
35247133Sobrien
353179551Sjhb	/* Soft reset the card */
354179551Sjhb	XE_SELECT_PAGE(0);
355179551Sjhb	XE_OUTB(XE_CR, XE_CR_SOFT_RESET);
356179551Sjhb	DELAY(40000);
357179551Sjhb	XE_OUTB(XE_CR, 0);
358179551Sjhb	DELAY(40000);
35947133Sobrien
360179551Sjhb	if (scp->mohawk) {
361179551Sjhb		/*
362179551Sjhb		 * set GP1 and GP2 as outputs (bits 2 & 3)
363179551Sjhb		 * set GP1 low to power on the ML6692 (bit 0)
364179551Sjhb		 * set GP2 high to power on the 10Mhz chip (bit 1)
365179551Sjhb		 */
366179551Sjhb		XE_SELECT_PAGE(4);
367179551Sjhb		XE_OUTB(XE_GPR0, XE_GPR0_GP2_SELECT | XE_GPR0_GP1_SELECT |
368179551Sjhb		    XE_GPR0_GP2_OUT);
369179551Sjhb	}
370121099Srsm
371179551Sjhb	/* Shut off interrupts */
372179551Sjhb	xe_disable_intr(scp);
373121099Srsm
374179551Sjhb	/* Wait for everything to wake up */
375179551Sjhb	DELAY(500000);
376121099Srsm
377179551Sjhb	/* Check for PHY */
378179551Sjhb	if (scp->mohawk)
379179551Sjhb		scp->phy_ok = xe_mii_init(scp);
380121099Srsm
381179551Sjhb	/* Disable 'source insertion' (not sure what that means) */
382179551Sjhb	XE_SELECT_PAGE(0x42);
383179551Sjhb	XE_OUTB(XE_SWC0, XE_SWC0_NO_SRC_INSERT);
38447133Sobrien
385179551Sjhb	/* Set 8K/24K Tx/Rx buffer split */
386179551Sjhb	if (scp->srev != 1) {
387179551Sjhb		XE_SELECT_PAGE(2);
388179551Sjhb		XE_OUTW(XE_RBS, 0x2000);
389179551Sjhb	}
39047133Sobrien
391179551Sjhb	/* Enable early transmit mode on Mohawk/Dingo */
392179551Sjhb	if (scp->mohawk) {
393179551Sjhb		XE_SELECT_PAGE(0x03);
394179551Sjhb		XE_OUTW(XE_TPT, scp->tx_thres);
395179551Sjhb		XE_SELECT_PAGE(0x01);
396179551Sjhb		XE_OUTB(XE_ECR, XE_INB(XE_ECR) | XE_ECR_EARLY_TX);
397179551Sjhb	}
398121099Srsm
399179551Sjhb	/* Put MAC address in first 'individual address' register */
400179551Sjhb	XE_SELECT_PAGE(0x50);
401179551Sjhb	for (i = 0; i < ETHER_ADDR_LEN; i++)
402179551Sjhb		XE_OUTB(0x08 + i, IF_LLADDR(scp->ifp)[scp->mohawk ? 5 - i : i]);
403121099Srsm
404179551Sjhb	/* Set up multicast addresses */
405179551Sjhb	xe_set_multicast(scp);
40647133Sobrien
407179551Sjhb	/* Fix the receive data offset -- reset can leave it off-by-one */
408179551Sjhb	XE_SELECT_PAGE(0);
409179551Sjhb	XE_OUTW(XE_DO, 0x2000);
41047133Sobrien
411179551Sjhb	/* Set interrupt masks */
412179551Sjhb	XE_SELECT_PAGE(1);
413179551Sjhb	XE_OUTB(XE_IMR0, XE_IMR0_TX_PACKET | XE_IMR0_MAC_INTR |
414179551Sjhb	    XE_IMR0_RX_PACKET);
41547133Sobrien
416179551Sjhb	/* Set MAC interrupt masks */
417179551Sjhb	XE_SELECT_PAGE(0x40);
418179551Sjhb	XE_OUTB(XE_RX0Msk,
419179551Sjhb	    ~(XE_RX0M_RX_OVERRUN | XE_RX0M_CRC_ERROR | XE_RX0M_ALIGN_ERROR |
420179551Sjhb	    XE_RX0M_LONG_PACKET));
421179551Sjhb	XE_OUTB(XE_TX0Msk,
422179551Sjhb	    ~(XE_TX0M_SQE_FAIL | XE_TX0M_LATE_COLLISION | XE_TX0M_TX_UNDERRUN |
423179551Sjhb	    XE_TX0M_16_COLLISIONS | XE_TX0M_NO_CARRIER));
42447133Sobrien
425179551Sjhb	/* Clear MAC status registers */
426179551Sjhb	XE_SELECT_PAGE(0x40);
427179551Sjhb	XE_OUTB(XE_RST0, 0x00);
428179551Sjhb	XE_OUTB(XE_TXST0, 0x00);
42947133Sobrien
430179551Sjhb	/* Enable receiver and put MAC online */
431179551Sjhb	XE_SELECT_PAGE(0x40);
432179551Sjhb	XE_OUTB(XE_CMD0, XE_CMD0_RX_ENABLE|XE_CMD0_ONLINE);
43347133Sobrien
434179551Sjhb	/* Set up IMR, enable interrupts */
435179551Sjhb	xe_enable_intr(scp);
436121099Srsm
437179551Sjhb	/* Start media selection */
438179551Sjhb	xe_setmedia(scp);
439121099Srsm
440179551Sjhb	/* Enable output */
441179551Sjhb	scp->ifp->if_drv_flags |= IFF_DRV_RUNNING;
442179551Sjhb	scp->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
443179551Sjhb	callout_reset(&scp->wdog_timer, hz, xe_watchdog, scp);
44447133Sobrien}
44547133Sobrien
44647133Sobrien/*
447121099Srsm * Start output on interface.  Should be called at splimp() priority.  Check
448148887Srwatson * that the output is idle (ie, IFF_DRV_OACTIVE is not set) before calling this
449148887Srwatson * function.  If media selection is in progress we set IFF_DRV_OACTIVE ourselves
450121099Srsm * and return immediately.
45147133Sobrien */
45247133Sobrienstatic void
453179551Sjhbxe_start(struct ifnet *ifp)
454179551Sjhb{
455179551Sjhb	struct xe_softc *scp = ifp->if_softc;
456179492Sjhb
457179551Sjhb	XE_LOCK(scp);
458179551Sjhb	xe_start_locked(ifp);
459179551Sjhb	XE_UNLOCK(scp);
460179492Sjhb}
461179492Sjhb
462179492Sjhbstatic void
463179551Sjhbxe_start_locked(struct ifnet *ifp)
464179551Sjhb{
465179551Sjhb	struct xe_softc *scp = ifp->if_softc;
466179551Sjhb	struct mbuf *mbp;
46747133Sobrien
468179551Sjhb	if (scp->autoneg_status != XE_AUTONEG_NONE) {
469179551Sjhb		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
470179551Sjhb		return;
471179551Sjhb	}
472121099Srsm
473179551Sjhb	DEVPRINTF(3, (scp->dev, "start\n"));
474121099Srsm
475179551Sjhb	/*
476179551Sjhb	 * Loop while there are packets to be sent, and space to send
477179551Sjhb	 * them.
478179551Sjhb	 */
479179551Sjhb	for (;;) {
480179551Sjhb		/* Suck a packet off the send queue */
481179551Sjhb		IF_DEQUEUE(&ifp->if_snd, mbp);
48247133Sobrien
483179551Sjhb		if (mbp == NULL) {
484179551Sjhb			/*
485179551Sjhb			 * We are using the !OACTIVE flag to indicate
486179551Sjhb			 * to the outside world that we can accept an
487179551Sjhb			 * additional packet rather than that the
488179551Sjhb			 * transmitter is _actually_ active. Indeed,
489179551Sjhb			 * the transmitter may be active, but if we
490179551Sjhb			 * haven't filled all the buffers with data
491179551Sjhb			 * then we still want to accept more.
492179551Sjhb			 */
493179551Sjhb			ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
494179551Sjhb			return;
495179551Sjhb		}
49647133Sobrien
497179551Sjhb		if (xe_pio_write_packet(scp, mbp) != 0) {
498179551Sjhb			/* Push the packet back onto the queue */
499179551Sjhb			IF_PREPEND(&ifp->if_snd, mbp);
500179551Sjhb			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
501179551Sjhb			return;
502179551Sjhb		}
50347133Sobrien
504179551Sjhb		/* Tap off here if there is a bpf listener */
505179551Sjhb		BPF_MTAP(ifp, mbp);
50647133Sobrien
507179551Sjhb		/* In case we don't hear from the card again... */
508179551Sjhb		scp->tx_timeout = 5;
509179551Sjhb		scp->tx_queued++;
51047133Sobrien
511179551Sjhb		m_freem(mbp);
512179551Sjhb	}
51347133Sobrien}
51447133Sobrien
51547133Sobrien/*
51647133Sobrien * Process an ioctl request.  Adapted from the ed driver.
51747133Sobrien */
51847133Sobrienstatic int
519179551Sjhbxe_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
520179551Sjhb{
521179551Sjhb	struct xe_softc *scp;
522179551Sjhb	int error;
52347133Sobrien
524179551Sjhb	scp = ifp->if_softc;
525179551Sjhb	error = 0;
52647133Sobrien
527179551Sjhb	switch (command) {
528179551Sjhb	case SIOCSIFFLAGS:
529179551Sjhb		DEVPRINTF(2, (scp->dev, "ioctl: SIOCSIFFLAGS: 0x%04x\n",
530179551Sjhb			ifp->if_flags));
531179551Sjhb		/*
532179551Sjhb		 * If the interface is marked up and stopped, then
533179551Sjhb		 * start it.  If it is marked down and running, then
534179551Sjhb		 * stop it.
535179551Sjhb		 */
536179551Sjhb		XE_LOCK(scp);
537179551Sjhb		if (ifp->if_flags & IFF_UP) {
538179551Sjhb			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
539179551Sjhb				xe_reset(scp);
540179551Sjhb				xe_init_locked(scp);
541179551Sjhb			}
542179551Sjhb		} else {
543179551Sjhb			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
544179551Sjhb				xe_stop(scp);
545179551Sjhb		}
54647133Sobrien
547179551Sjhb		/* handle changes to PROMISC/ALLMULTI flags */
548179551Sjhb		xe_set_multicast(scp);
549179551Sjhb		XE_UNLOCK(scp);
550179551Sjhb		error = 0;
551179551Sjhb		break;
552179551Sjhb	case SIOCADDMULTI:
553179551Sjhb	case SIOCDELMULTI:
554179551Sjhb		DEVPRINTF(2, (scp->dev, "ioctl: SIOC{ADD,DEL}MULTI\n"));
555179551Sjhb		/*
556179551Sjhb		 * Multicast list has (maybe) changed; set the
557179551Sjhb		 * hardware filters accordingly.
558179551Sjhb		 */
559179551Sjhb		XE_LOCK(scp);
560179551Sjhb		xe_set_multicast(scp);
561179551Sjhb		XE_UNLOCK(scp);
562179551Sjhb		error = 0;
563179551Sjhb		break;
564179551Sjhb	case SIOCSIFMEDIA:
565179551Sjhb	case SIOCGIFMEDIA:
566179551Sjhb		DEVPRINTF(3, (scp->dev, "ioctl: bounce to ifmedia_ioctl\n"));
567179551Sjhb		/*
568179551Sjhb		 * Someone wants to get/set media options.
569179551Sjhb		 */
570179551Sjhb		error = ifmedia_ioctl(ifp, (struct ifreq *)data, &scp->ifmedia,
571179551Sjhb		    command);
572179551Sjhb		break;
573179551Sjhb	default:
574179551Sjhb		DEVPRINTF(3, (scp->dev, "ioctl: bounce to ether_ioctl\n"));
575179551Sjhb		error = ether_ioctl(ifp, command, data);
576179551Sjhb	}
577121099Srsm
578179551Sjhb	return (error);
57947133Sobrien}
58047133Sobrien
58147133Sobrien/*
58255723Simp * Card interrupt handler.
58347136Sobrien *
58447136Sobrien * This function is probably more complicated than it needs to be, as it
58547136Sobrien * attempts to deal with the case where multiple packets get sent between
58647136Sobrien * interrupts.  This is especially annoying when working out the collision
58747136Sobrien * stats.  Not sure whether this case ever really happens or not (maybe on a
58847136Sobrien * slow/heavily loaded machine?) so it's probably best to leave this like it
58947136Sobrien * is.
59047136Sobrien *
59147136Sobrien * Note that the crappy PIO used to get packets on and off the card means that
59247136Sobrien * you will spend a lot of time in this routine -- I can get my P150 to spend
59347136Sobrien * 90% of its time servicing interrupts if I really hammer the network.  Could
59447136Sobrien * fix this, but then you'd start dropping/losing packets.  The moral of this
59547136Sobrien * story?  If you want good network performance _and_ some cycles left over to
59647136Sobrien * get your work done, don't buy a Xircom card.  Or convince them to tell me
59747136Sobrien * how to do memory-mapped I/O :)
59847136Sobrien */
59955723Simpstatic void
600179551Sjhbxe_txintr(struct xe_softc *scp, uint8_t txst1)
60155723Simp{
602179551Sjhb	struct ifnet *ifp;
603179551Sjhb	uint8_t tpr, sent, coll;
60447136Sobrien
605179551Sjhb	ifp = scp->ifp;
60647136Sobrien
607179551Sjhb	/* Update packet count, accounting for rollover */
608179551Sjhb	tpr = XE_INB(XE_TPR);
609179551Sjhb	sent = -scp->tx_tpr + tpr;
610121099Srsm
611179551Sjhb	/* Update statistics if we actually sent anything */
612179551Sjhb	if (sent > 0) {
613179551Sjhb		coll = txst1 & XE_TXST1_RETRY_COUNT;
614179551Sjhb		scp->tx_tpr = tpr;
615179551Sjhb		scp->tx_queued -= sent;
616179551Sjhb		ifp->if_opackets += sent;
617179551Sjhb		ifp->if_collisions += coll;
61847136Sobrien
619179551Sjhb		/*
620179551Sjhb		 * According to the Xircom manual, Dingo will
621179551Sjhb		 * sometimes manage to transmit a packet with
622179551Sjhb		 * triggering an interrupt.  If this happens, we have
623179551Sjhb		 * sent > 1 and the collision count only reflects
624179551Sjhb		 * collisions on the last packet sent (the one that
625179551Sjhb		 * triggered the interrupt).  Collision stats might
626179551Sjhb		 * therefore be a bit low, but there doesn't seem to
627179551Sjhb		 * be anything we can do about that.
628179551Sjhb		 */
629179551Sjhb		switch (coll) {
630179551Sjhb		case 0:
631179551Sjhb			break;
632179551Sjhb		case 1:
633179551Sjhb			scp->mibdata.dot3StatsSingleCollisionFrames++;
634179551Sjhb			scp->mibdata.dot3StatsCollFrequencies[0]++;
635179551Sjhb			break;
636179551Sjhb		default:
637179551Sjhb			scp->mibdata.dot3StatsMultipleCollisionFrames++;
638179551Sjhb			scp->mibdata.dot3StatsCollFrequencies[coll-1]++;
639179551Sjhb		}
64047136Sobrien	}
641179551Sjhb	scp->tx_timeout = 0;
642179551Sjhb	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
643179543Sjhb}
644121099Srsm
645179543Sjhb/* Handle most MAC interrupts */
646179543Sjhbstatic void
647179551Sjhbxe_macintr(struct xe_softc *scp, uint8_t rst0, uint8_t txst0, uint8_t txst1)
648179543Sjhb{
649179551Sjhb	struct ifnet *ifp;
650121099Srsm
651179551Sjhb	ifp = scp->ifp;
652179543Sjhb
653121099Srsm#if 0
654179551Sjhb	/* Carrier sense lost -- only in 10Mbit HDX mode */
655179551Sjhb	if (txst0 & XE_TXST0_NO_CARRIER || !(txst1 & XE_TXST1_LINK_STATUS)) {
656179551Sjhb		/* XXX - Need to update media status here */
657179551Sjhb		device_printf(scp->dev, "no carrier\n");
658179551Sjhb		ifp->if_oerrors++;
659179551Sjhb		scp->mibdata.dot3StatsCarrierSenseErrors++;
660179551Sjhb	}
661121099Srsm#endif
662179551Sjhb	/* Excessive collisions -- try sending again */
663179551Sjhb	if (txst0 & XE_TXST0_16_COLLISIONS) {
664179551Sjhb		ifp->if_collisions += 16;
665179551Sjhb		ifp->if_oerrors++;
666179551Sjhb		scp->mibdata.dot3StatsExcessiveCollisions++;
667179551Sjhb		scp->mibdata.dot3StatsMultipleCollisionFrames++;
668179551Sjhb		scp->mibdata.dot3StatsCollFrequencies[15]++;
669179551Sjhb		XE_OUTB(XE_CR, XE_CR_RESTART_TX);
670121099Srsm	}
671179551Sjhb
672179551Sjhb	/* Transmit underrun -- increase early transmit threshold */
673179551Sjhb	if (txst0 & XE_TXST0_TX_UNDERRUN && scp->mohawk) {
674179551Sjhb		DEVPRINTF(1, (scp->dev, "transmit underrun"));
675179551Sjhb		if (scp->tx_thres < ETHER_MAX_LEN) {
676179551Sjhb			if ((scp->tx_thres += 64) > ETHER_MAX_LEN)
677179551Sjhb				scp->tx_thres = ETHER_MAX_LEN;
678179551Sjhb			DPRINTF(1, (": increasing transmit threshold to %u",
679179551Sjhb			    scp->tx_thres));
680179551Sjhb			XE_SELECT_PAGE(0x3);
681179551Sjhb			XE_OUTW(XE_TPT, scp->tx_thres);
682179551Sjhb			XE_SELECT_PAGE(0x0);
683179551Sjhb		}
684179551Sjhb		DPRINTF(1, ("\n"));
685179551Sjhb		ifp->if_oerrors++;
686179551Sjhb		scp->mibdata.dot3StatsInternalMacTransmitErrors++;
687179551Sjhb	}
688179551Sjhb
689179551Sjhb	/* Late collision -- just complain about it */
690179551Sjhb	if (txst0 & XE_TXST0_LATE_COLLISION) {
691179551Sjhb		device_printf(scp->dev, "late collision\n");
692179551Sjhb		ifp->if_oerrors++;
693179551Sjhb		scp->mibdata.dot3StatsLateCollisions++;
694179551Sjhb	}
695179551Sjhb
696179551Sjhb	/* SQE test failure -- just complain about it */
697179551Sjhb	if (txst0 & XE_TXST0_SQE_FAIL) {
698179551Sjhb		device_printf(scp->dev, "SQE test failure\n");
699179551Sjhb		ifp->if_oerrors++;
700179551Sjhb		scp->mibdata.dot3StatsSQETestErrors++;
701179551Sjhb	}
702179551Sjhb
703179551Sjhb	/* Packet too long -- what happens to these */
704179551Sjhb	if (rst0 & XE_RST0_LONG_PACKET) {
705179551Sjhb		device_printf(scp->dev, "received giant packet\n");
706179551Sjhb		ifp->if_ierrors++;
707179551Sjhb		scp->mibdata.dot3StatsFrameTooLongs++;
708179551Sjhb	}
709179551Sjhb
710179551Sjhb	/* CRC error -- packet dropped */
711179551Sjhb	if (rst0 & XE_RST0_CRC_ERROR) {
712179551Sjhb		device_printf(scp->dev, "CRC error\n");
713179551Sjhb		ifp->if_ierrors++;
714179551Sjhb		scp->mibdata.dot3StatsFCSErrors++;
715179551Sjhb	}
716179543Sjhb}
71747136Sobrien
718179543Sjhbstatic void
719179551Sjhbxe_rxintr(struct xe_softc *scp, uint8_t rst0)
720179543Sjhb{
721179551Sjhb	struct ifnet *ifp;
722179551Sjhb	uint8_t esr, rsr;
723179543Sjhb
724179551Sjhb	ifp = scp->ifp;
725179543Sjhb
726179551Sjhb	/* Handle received packet(s) */
727179551Sjhb	while ((esr = XE_INB(XE_ESR)) & XE_ESR_FULL_PACKET_RX) {
728179551Sjhb		rsr = XE_INB(XE_RSR);
72947136Sobrien
730179551Sjhb		DEVPRINTF(3, (scp->dev, "intr: ESR=0x%02x, RSR=0x%02x\n", esr,
731179551Sjhb		    rsr));
732121099Srsm
733179551Sjhb		/* Make sure packet is a good one */
734179551Sjhb		if (rsr & XE_RSR_RX_OK) {
735179551Sjhb			struct ether_header *ehp;
736179551Sjhb			struct mbuf *mbp;
737179551Sjhb			uint16_t len;
73847136Sobrien
739179551Sjhb			len = XE_INW(XE_RBC) - ETHER_CRC_LEN;
74047136Sobrien
741179551Sjhb			DEVPRINTF(3, (scp->dev, "intr: receive length = %d\n",
742179551Sjhb			    len));
743121099Srsm
744179551Sjhb			if (len == 0) {
745179551Sjhb				ifp->if_iqdrops++;
746179551Sjhb				continue;
747179551Sjhb			}
74847136Sobrien
749179551Sjhb			/*
750179551Sjhb			 * Allocate mbuf to hold received packet.  If
751179551Sjhb			 * the mbuf header isn't big enough, we attach
752179551Sjhb			 * an mbuf cluster to hold the packet.  Note
753179551Sjhb			 * the +=2 to align the packet data on a
754179551Sjhb			 * 32-bit boundary, and the +3 to allow for
755179551Sjhb			 * the possibility of reading one more byte
756179551Sjhb			 * than the actual packet length (we always
757179551Sjhb			 * read 16-bit words).  XXX - Surely there's a
758179551Sjhb			 * better way to do this alignment?
759179551Sjhb			 */
760243857Sglebius			MGETHDR(mbp, M_NOWAIT, MT_DATA);
761179551Sjhb			if (mbp == NULL) {
762179551Sjhb				ifp->if_iqdrops++;
763179551Sjhb				continue;
764179551Sjhb			}
76547136Sobrien
766179551Sjhb			if (len + 3 > MHLEN) {
767243857Sglebius				MCLGET(mbp, M_NOWAIT);
768179551Sjhb				if ((mbp->m_flags & M_EXT) == 0) {
769179551Sjhb					m_freem(mbp);
770179551Sjhb					ifp->if_iqdrops++;
771179551Sjhb					continue;
772179551Sjhb				}
773179551Sjhb			}
77447136Sobrien
775179551Sjhb			mbp->m_data += 2;
776179551Sjhb			ehp = mtod(mbp, struct ether_header *);
77747136Sobrien
778179551Sjhb			/*
779179551Sjhb			 * Now get the packet in PIO mode, including
780179551Sjhb			 * the Ethernet header but omitting the
781179551Sjhb			 * trailing CRC.
782179551Sjhb			 */
78347136Sobrien
784179551Sjhb			/*
785179551Sjhb			 * Work around a bug in CE2 cards.  There
786179551Sjhb			 * seems to be a problem with duplicated and
787179551Sjhb			 * extraneous bytes in the receive buffer, but
788179551Sjhb			 * without any real documentation for the CE2
789179551Sjhb			 * it's hard to tell for sure.  XXX - Needs
790179551Sjhb			 * testing on CE2 hardware
791179551Sjhb			 */
792179551Sjhb			if (scp->srev == 0) {
793179551Sjhb				u_short rhs;
79447136Sobrien
795179551Sjhb				XE_SELECT_PAGE(5);
796179551Sjhb				rhs = XE_INW(XE_RHSA);
797179551Sjhb				XE_SELECT_PAGE(0);
79847136Sobrien
799179551Sjhb				rhs += 3;	 /* Skip control info */
80047136Sobrien
801179551Sjhb				if (rhs >= 0x8000)
802179551Sjhb					rhs = 0;
80347136Sobrien
804179551Sjhb				if (rhs + len > 0x8000) {
805179551Sjhb					int i;
806121099Srsm
807179551Sjhb					for (i = 0; i < len; i++, rhs++) {
808179551Sjhb						((char *)ehp)[i] =
809179551Sjhb						    XE_INB(XE_EDP);
810179551Sjhb						if (rhs == 0x8000) {
811179551Sjhb							rhs = 0;
812179551Sjhb							i--;
813179551Sjhb						}
814179551Sjhb					}
815179551Sjhb				} else
816179551Sjhb					bus_read_multi_2(scp->port_res, XE_EDP,
817179551Sjhb					    (uint16_t *)ehp, (len + 1) >> 1);
818179551Sjhb			} else
819179551Sjhb				bus_read_multi_2(scp->port_res, XE_EDP,
820179551Sjhb				    (uint16_t *)ehp, (len + 1) >> 1);
82147136Sobrien
822179551Sjhb			/* Deliver packet to upper layers */
823179551Sjhb			mbp->m_pkthdr.rcvif = ifp;
824179551Sjhb			mbp->m_pkthdr.len = mbp->m_len = len;
825179551Sjhb			XE_UNLOCK(scp);
826179551Sjhb			(*ifp->if_input)(ifp, mbp);
827179551Sjhb			XE_LOCK(scp);
828179551Sjhb			ifp->if_ipackets++;
829121099Srsm
830179551Sjhb		} else if (rsr & XE_RSR_ALIGN_ERROR) {
831179551Sjhb			/* Packet alignment error -- drop packet */
832179551Sjhb			device_printf(scp->dev, "alignment error\n");
833179551Sjhb			scp->mibdata.dot3StatsAlignmentErrors++;
834179551Sjhb			ifp->if_ierrors++;
835179551Sjhb		}
836121099Srsm
837179551Sjhb		/* Skip to next packet, if there is one */
838179551Sjhb		XE_OUTW(XE_DO, 0x8000);
839179551Sjhb	}
840121099Srsm
841179551Sjhb	/* Clear receiver overruns now we have some free buffer space */
842179551Sjhb	if (rst0 & XE_RST0_RX_OVERRUN) {
843179551Sjhb		DEVPRINTF(1, (scp->dev, "receive overrun\n"));
844179551Sjhb		ifp->if_ierrors++;
845179551Sjhb		scp->mibdata.dot3StatsInternalMacReceiveErrors++;
846179551Sjhb		XE_OUTB(XE_CR, XE_CR_CLEAR_OVERRUN);
847179551Sjhb	}
848179543Sjhb}
849179543Sjhb
850179543Sjhbstatic void
851179551Sjhbxe_intr(void *xscp)
852179543Sjhb{
853179551Sjhb	struct xe_softc *scp = (struct xe_softc *) xscp;
854179551Sjhb	struct ifnet *ifp;
855179551Sjhb	uint8_t psr, isr, rst0, txst0, txst1;
856179543Sjhb
857179551Sjhb	ifp = scp->ifp;
858179551Sjhb	XE_LOCK(scp);
859179543Sjhb
860179551Sjhb	/* Disable interrupts */
861179551Sjhb	if (scp->mohawk)
862179551Sjhb		XE_OUTB(XE_CR, 0);
863179543Sjhb
864179551Sjhb	/* Cache current register page */
865179551Sjhb	psr = XE_INB(XE_PR);
866179543Sjhb
867179551Sjhb	/* Read ISR to see what caused this interrupt */
868179551Sjhb	while ((isr = XE_INB(XE_ISR)) != 0) {
869179543Sjhb
870179551Sjhb		/* 0xff might mean the card is no longer around */
871179551Sjhb		if (isr == 0xff) {
872179551Sjhb			DEVPRINTF(3, (scp->dev,
873179551Sjhb			    "intr: interrupt received for missing card?\n"));
874179551Sjhb			break;
875179551Sjhb		}
876179543Sjhb
877179551Sjhb		/* Read other status registers */
878179551Sjhb		XE_SELECT_PAGE(0x40);
879179551Sjhb		rst0 = XE_INB(XE_RST0);
880179551Sjhb		XE_OUTB(XE_RST0, 0);
881179551Sjhb		txst0 = XE_INB(XE_TXST0);
882179551Sjhb		txst1 = XE_INB(XE_TXST1);
883179551Sjhb		XE_OUTB(XE_TXST0, 0);
884179551Sjhb		XE_OUTB(XE_TXST1, 0);
885179551Sjhb		XE_SELECT_PAGE(0);
886179543Sjhb
887179551Sjhb		DEVPRINTF(3, (scp->dev,
888179551Sjhb		    "intr: ISR=0x%02x, RST=0x%02x, TXT=0x%02x%02x\n", isr,
889179551Sjhb		    rst0, txst1, txst0));
890179543Sjhb
891179551Sjhb		if (isr & XE_ISR_TX_PACKET)
892179551Sjhb			xe_txintr(scp, txst1);
893179543Sjhb
894179551Sjhb		if (isr & XE_ISR_MAC_INTR)
895179551Sjhb			xe_macintr(scp, rst0, txst0, txst1);
896179543Sjhb
897179551Sjhb		xe_rxintr(scp, rst0);
898179551Sjhb	}
89947136Sobrien
900179551Sjhb	/* Restore saved page */
901179551Sjhb	XE_SELECT_PAGE(psr);
90247136Sobrien
903179551Sjhb	/* Re-enable interrupts */
904179551Sjhb	XE_OUTB(XE_CR, XE_CR_ENABLE_INTR);
90547136Sobrien
906179551Sjhb	XE_UNLOCK(scp);
90747136Sobrien}
90847136Sobrien
90947136Sobrien/*
91047133Sobrien * Device timeout/watchdog routine.  Called automatically if we queue a packet
91147133Sobrien * for transmission but don't get an interrupt within a specified timeout
91247133Sobrien * (usually 5 seconds).  When this happens we assume the worst and reset the
91347133Sobrien * card.
91447133Sobrien */
91547133Sobrienstatic void
916179551Sjhbxe_watchdog(void *arg)
917179551Sjhb{
918179551Sjhb	struct xe_softc *scp = arg;
91947133Sobrien
920179551Sjhb	XE_ASSERT_LOCKED(scp);
921179527Sjhb
922179551Sjhb	if (scp->tx_timeout && --scp->tx_timeout == 0) {
923179551Sjhb   		device_printf(scp->dev, "watchdog timeout: resetting card\n");
924179551Sjhb		scp->tx_timeouts++;
925179551Sjhb		scp->ifp->if_oerrors += scp->tx_queued;
926179551Sjhb		xe_stop(scp);
927179551Sjhb		xe_reset(scp);
928179551Sjhb		xe_init_locked(scp);
929179551Sjhb	}
930179551Sjhb	callout_reset(&scp->wdog_timer, hz, xe_watchdog, scp);
93147133Sobrien}
93247133Sobrien
93347133Sobrien/*
93447136Sobrien * Change media selection.
93547133Sobrien */
93647136Sobrienstatic int
937179551Sjhbxe_media_change(struct ifnet *ifp)
938179551Sjhb{
939179551Sjhb	struct xe_softc *scp = ifp->if_softc;
94047136Sobrien
941179551Sjhb	DEVPRINTF(2, (scp->dev, "media_change\n"));
94247136Sobrien
943179551Sjhb	XE_LOCK(scp);
944179551Sjhb	if (IFM_TYPE(scp->ifm->ifm_media) != IFM_ETHER) {
945179551Sjhb		XE_UNLOCK(scp);
946179551Sjhb		return(EINVAL);
947179551Sjhb	}
94847136Sobrien
949179551Sjhb	/*
950179551Sjhb	 * Some card/media combos aren't always possible -- filter
951179551Sjhb	 * those out here.
952179551Sjhb	 */
953179551Sjhb	if ((IFM_SUBTYPE(scp->ifm->ifm_media) == IFM_AUTO ||
954179551Sjhb	    IFM_SUBTYPE(scp->ifm->ifm_media) == IFM_100_TX) && !scp->phy_ok) {
955179551Sjhb		XE_UNLOCK(scp);
956179551Sjhb		return (EINVAL);
957179551Sjhb	}
95847136Sobrien
959179551Sjhb	xe_setmedia(scp);
960179551Sjhb	XE_UNLOCK(scp);
96147136Sobrien
962179551Sjhb	return (0);
96347136Sobrien}
96447136Sobrien
96547136Sobrien/*
96647136Sobrien * Return current media selection.
96747136Sobrien */
96847133Sobrienstatic void
969179551Sjhbxe_media_status(struct ifnet *ifp, struct ifmediareq *mrp)
970179551Sjhb{
971179551Sjhb	struct xe_softc *scp = ifp->if_softc;
97247133Sobrien
973179551Sjhb	DEVPRINTF(3, (scp->dev, "media_status\n"));
97447133Sobrien
975179551Sjhb	/* XXX - This is clearly wrong.  Will fix once I have CE2 working */
976179551Sjhb	XE_LOCK(scp);
977179551Sjhb	mrp->ifm_status = IFM_AVALID | IFM_ACTIVE;
978179551Sjhb	mrp->ifm_active = ((struct xe_softc *)ifp->if_softc)->media;
979179551Sjhb	XE_UNLOCK(scp);
98047136Sobrien}
98147136Sobrien
98247136Sobrien/*
98347136Sobrien * Select active media.
98447136Sobrien */
985179551Sjhbstatic void
986179551Sjhbxe_setmedia(void *xscp)
987179551Sjhb{
988179551Sjhb	struct xe_softc *scp = xscp;
989179551Sjhb	uint16_t bmcr, bmsr, anar, lpar;
99047136Sobrien
991179551Sjhb	DEVPRINTF(2, (scp->dev, "setmedia\n"));
99247136Sobrien
993179551Sjhb	XE_ASSERT_LOCKED(scp);
994179492Sjhb
995179551Sjhb	/* Cancel any pending timeout */
996179551Sjhb	callout_stop(&scp->media_timer);
997179551Sjhb	xe_disable_intr(scp);
99847136Sobrien
999179551Sjhb	/* Select media */
1000179551Sjhb	scp->media = IFM_ETHER;
1001179551Sjhb	switch (IFM_SUBTYPE(scp->ifm->ifm_media)) {
100247136Sobrien
1003179551Sjhb	case IFM_AUTO:	/* Autoselect media */
1004179551Sjhb		scp->media = IFM_ETHER|IFM_AUTO;
100547136Sobrien
1006179551Sjhb		/*
1007179551Sjhb		 * Autoselection is really awful.  It goes something like this:
1008179551Sjhb		 *
1009179551Sjhb		 * Wait until the transmitter goes idle (2sec timeout).
1010179551Sjhb		 * Reset card
1011179551Sjhb		 *   IF a 100Mbit PHY exists
1012179551Sjhb		 *     Start NWAY autonegotiation (3.5sec timeout)
1013179551Sjhb		 *     IF that succeeds
1014179551Sjhb		 *       Select 100baseTX or 10baseT, whichever was detected
1015179551Sjhb		 *     ELSE
1016179551Sjhb		 *       Reset card
1017179551Sjhb		 *       IF a 100Mbit PHY exists
1018179551Sjhb		 *         Try to force a 100baseTX link (3sec timeout)
1019179551Sjhb		 *         IF that succeeds
1020179551Sjhb		 *           Select 100baseTX
1021179551Sjhb		 *         ELSE
1022179551Sjhb		 *           Disable the PHY
1023179551Sjhb		 *         ENDIF
1024179551Sjhb		 *       ENDIF
1025179551Sjhb		 *     ENDIF
1026179551Sjhb		 *   ENDIF
1027179551Sjhb		 * IF nothing selected so far
1028179551Sjhb		 *   IF a 100Mbit PHY exists
1029179551Sjhb		 *     Select 10baseT
1030179551Sjhb		 *   ELSE
1031179551Sjhb		 *     Select 10baseT or 10base2, whichever is connected
1032179551Sjhb		 *   ENDIF
1033179551Sjhb		 * ENDIF
1034179551Sjhb		 */
1035179551Sjhb		switch (scp->autoneg_status) {
1036179551Sjhb		case XE_AUTONEG_NONE:
1037179551Sjhb			DEVPRINTF(2, (scp->dev,
1038179551Sjhb			    "Waiting for idle transmitter\n"));
1039179551Sjhb			scp->ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1040179551Sjhb			scp->autoneg_status = XE_AUTONEG_WAITING;
1041179551Sjhb			/* FALL THROUGH */
1042179551Sjhb		case XE_AUTONEG_WAITING:
1043179551Sjhb			if (scp->tx_queued != 0) {
1044179551Sjhb				callout_reset(&scp->media_timer, hz / 2,
1045179551Sjhb				    xe_setmedia, scp);
1046179551Sjhb				return;
1047179551Sjhb			}
1048179551Sjhb			if (scp->phy_ok) {
1049179551Sjhb				DEVPRINTF(2, (scp->dev,
1050179551Sjhb					"Starting autonegotiation\n"));
1051179551Sjhb				bmcr = xe_phy_readreg(scp, PHY_BMCR);
1052179551Sjhb				bmcr &= ~(PHY_BMCR_AUTONEGENBL);
1053179551Sjhb				xe_phy_writereg(scp, PHY_BMCR, bmcr);
1054179551Sjhb				anar = xe_phy_readreg(scp, PHY_ANAR);
1055179551Sjhb				anar &= ~(PHY_ANAR_100BT4 |
1056179551Sjhb				    PHY_ANAR_100BTXFULL | PHY_ANAR_10BTFULL);
1057179551Sjhb				anar |= PHY_ANAR_100BTXHALF | PHY_ANAR_10BTHALF;
1058179551Sjhb				xe_phy_writereg(scp, PHY_ANAR, anar);
1059179551Sjhb				bmcr |= PHY_BMCR_AUTONEGENBL |
1060179551Sjhb				    PHY_BMCR_AUTONEGRSTR;
1061179551Sjhb				xe_phy_writereg(scp, PHY_BMCR, bmcr);
1062179551Sjhb				scp->autoneg_status = XE_AUTONEG_STARTED;
1063179551Sjhb				callout_reset(&scp->media_timer, hz * 7/2,
1064179551Sjhb				    xe_setmedia, scp);
1065179551Sjhb				return;
1066179551Sjhb			} else {
1067179551Sjhb				scp->autoneg_status = XE_AUTONEG_FAIL;
1068179551Sjhb			}
1069179551Sjhb			break;
1070179551Sjhb		case XE_AUTONEG_STARTED:
1071179551Sjhb			bmsr = xe_phy_readreg(scp, PHY_BMSR);
1072179551Sjhb			lpar = xe_phy_readreg(scp, PHY_LPAR);
1073179551Sjhb			if (bmsr & (PHY_BMSR_AUTONEGCOMP | PHY_BMSR_LINKSTAT)) {
1074179551Sjhb				DEVPRINTF(2, (scp->dev,
1075179551Sjhb				    "Autonegotiation complete!\n"));
107647136Sobrien
1077179551Sjhb				/*
1078179551Sjhb				 * XXX - Shouldn't have to do this,
1079179551Sjhb				 * but (on my hub at least) the
1080179551Sjhb				 * transmitter won't work after a
1081179551Sjhb				 * successful autoneg.  So we see what
1082179551Sjhb				 * the negotiation result was and
1083179551Sjhb				 * force that mode.  I'm sure there is
1084179551Sjhb				 * an easy fix for this.
1085179551Sjhb				 */
1086179551Sjhb				if (lpar & PHY_LPAR_100BTXHALF) {
1087179551Sjhb					xe_phy_writereg(scp, PHY_BMCR,
1088179551Sjhb					    PHY_BMCR_SPEEDSEL);
1089179551Sjhb					XE_MII_DUMP(scp);
1090179551Sjhb					XE_SELECT_PAGE(2);
1091179551Sjhb					XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08);
1092179551Sjhb					scp->media = IFM_ETHER | IFM_100_TX;
1093179551Sjhb					scp->autoneg_status = XE_AUTONEG_NONE;
1094179551Sjhb				} else {
1095179551Sjhb					/*
1096179551Sjhb					 * XXX - Bit of a hack going
1097179551Sjhb					 * on in here.  This is
1098179551Sjhb					 * derived from Ken Hughes
1099179551Sjhb					 * patch to the Linux driver
1100179551Sjhb					 * to make it work with 10Mbit
1101179551Sjhb					 * _autonegotiated_ links on
1102179551Sjhb					 * CE3B cards.  What's a CE3B
1103179551Sjhb					 * and how's it differ from a
1104179551Sjhb					 * plain CE3?  these are the
1105179551Sjhb					 * things we need to find out.
1106179551Sjhb					 */
1107179551Sjhb					xe_phy_writereg(scp, PHY_BMCR, 0x0000);
1108179551Sjhb					XE_SELECT_PAGE(2);
1109179551Sjhb					/* BEGIN HACK */
1110179551Sjhb					XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08);
1111179551Sjhb					XE_SELECT_PAGE(0x42);
1112179551Sjhb					XE_OUTB(XE_SWC1, 0x80);
1113179551Sjhb					scp->media = IFM_ETHER | IFM_10_T;
1114179551Sjhb					scp->autoneg_status = XE_AUTONEG_NONE;
1115179551Sjhb					/* END HACK */
1116179551Sjhb#if 0
1117179551Sjhb					/* Display PHY? */
1118179551Sjhb					XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08);
1119179551Sjhb					scp->autoneg_status = XE_AUTONEG_FAIL;
1120179551Sjhb#endif
1121179551Sjhb				}
1122179551Sjhb			} else {
1123179551Sjhb				DEVPRINTF(2, (scp->dev,
1124179551Sjhb			    "Autonegotiation failed; trying 100baseTX\n"));
1125179551Sjhb				XE_MII_DUMP(scp);
1126179551Sjhb				if (scp->phy_ok) {
1127179551Sjhb					xe_phy_writereg(scp, PHY_BMCR,
1128179551Sjhb					    PHY_BMCR_SPEEDSEL);
1129179551Sjhb					scp->autoneg_status = XE_AUTONEG_100TX;
1130179551Sjhb					callout_reset(&scp->media_timer, hz * 3,
1131179551Sjhb					    xe_setmedia, scp);
1132179551Sjhb					return;
1133179551Sjhb				} else {
1134179551Sjhb					scp->autoneg_status = XE_AUTONEG_FAIL;
1135179551Sjhb				}
1136179551Sjhb			}
1137179551Sjhb			break;
1138179551Sjhb		case XE_AUTONEG_100TX:
1139179551Sjhb			(void)xe_phy_readreg(scp, PHY_BMSR);
1140179551Sjhb			bmsr = xe_phy_readreg(scp, PHY_BMSR);
1141179551Sjhb			if (bmsr & PHY_BMSR_LINKSTAT) {
1142179551Sjhb				DEVPRINTF(2, (scp->dev,
1143179551Sjhb				    "Got 100baseTX link!\n"));
1144179551Sjhb				XE_MII_DUMP(scp);
1145179551Sjhb				XE_SELECT_PAGE(2);
1146179551Sjhb				XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08);
1147179551Sjhb				scp->media = IFM_ETHER | IFM_100_TX;
1148179551Sjhb				scp->autoneg_status = XE_AUTONEG_NONE;
1149179551Sjhb			} else {
1150179551Sjhb				DEVPRINTF(2, (scp->dev,
1151179551Sjhb				    "Autonegotiation failed; disabling PHY\n"));
1152179551Sjhb				XE_MII_DUMP(scp);
1153179551Sjhb				xe_phy_writereg(scp, PHY_BMCR, 0x0000);
1154179551Sjhb				XE_SELECT_PAGE(2);
115547136Sobrien
1156179551Sjhb				/* Disable PHY? */
1157179551Sjhb				XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08);
1158179551Sjhb				scp->autoneg_status = XE_AUTONEG_FAIL;
1159179551Sjhb			}
1160179551Sjhb			break;
1161179551Sjhb		}
116247136Sobrien
1163179551Sjhb		/*
1164179551Sjhb		 * If we got down here _and_ autoneg_status is
1165179551Sjhb		 * XE_AUTONEG_FAIL, then either autonegotiation
1166179551Sjhb		 * failed, or never got started to begin with.  In
1167179551Sjhb		 * either case, select a suitable 10Mbit media and
1168179551Sjhb		 * hope it works.  We don't need to reset the card
1169179551Sjhb		 * again, since it will have been done already by the
1170179551Sjhb		 * big switch above.
1171179551Sjhb		 */
1172179551Sjhb		if (scp->autoneg_status == XE_AUTONEG_FAIL) {
1173179551Sjhb			DEVPRINTF(2, (scp->dev, "Selecting 10baseX\n"));
1174179551Sjhb			if (scp->mohawk) {
1175179551Sjhb				XE_SELECT_PAGE(0x42);
1176179551Sjhb				XE_OUTB(XE_SWC1, 0x80);
1177179551Sjhb				scp->media = IFM_ETHER | IFM_10_T;
1178179551Sjhb				scp->autoneg_status = XE_AUTONEG_NONE;
1179179551Sjhb			} else {
1180179551Sjhb				XE_SELECT_PAGE(4);
1181179551Sjhb				XE_OUTB(XE_GPR0, 4);
1182179551Sjhb				DELAY(50000);
1183179551Sjhb				XE_SELECT_PAGE(0x42);
1184179551Sjhb				XE_OUTB(XE_SWC1,
1185179551Sjhb				    (XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ?
1186179551Sjhb				    0x80 : 0xc0);
1187179551Sjhb				scp->media = IFM_ETHER | ((XE_INB(XE_ESR) &
1188179551Sjhb				    XE_ESR_MEDIA_SELECT) ? IFM_10_T : IFM_10_2);
1189179551Sjhb				scp->autoneg_status = XE_AUTONEG_NONE;
1190179551Sjhb			}
1191179551Sjhb		}
1192179551Sjhb		break;
1193179551Sjhb
119447136Sobrien	/*
1195179551Sjhb	 * If a specific media has been requested, we just reset the
1196179551Sjhb	 * card and select it (one small exception -- if 100baseTX is
1197179551Sjhb	 * requested but there is no PHY, we fall back to 10baseT
1198179551Sjhb	 * operation).
119947136Sobrien	 */
1200179551Sjhb	case IFM_100_TX:	/* Force 100baseTX */
1201179551Sjhb		if (scp->phy_ok) {
1202179551Sjhb			DEVPRINTF(2, (scp->dev, "Selecting 100baseTX\n"));
1203179551Sjhb			XE_SELECT_PAGE(0x42);
1204179551Sjhb			XE_OUTB(XE_SWC1, 0);
1205179551Sjhb			xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL);
1206179551Sjhb			XE_SELECT_PAGE(2);
1207179551Sjhb			XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08);
1208179551Sjhb			scp->media |= IFM_100_TX;
1209179551Sjhb			break;
1210179551Sjhb		}
1211179551Sjhb		/* FALLTHROUGH */
1212179551Sjhb	case IFM_10_T:		/* Force 10baseT */
1213179551Sjhb		DEVPRINTF(2, (scp->dev, "Selecting 10baseT\n"));
1214179551Sjhb		if (scp->phy_ok) {
1215179551Sjhb			xe_phy_writereg(scp, PHY_BMCR, 0x0000);
1216179551Sjhb			XE_SELECT_PAGE(2);
1217179551Sjhb
1218179551Sjhb			/* Disable PHY */
1219179551Sjhb			XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08);
1220179551Sjhb		}
1221179551Sjhb		XE_SELECT_PAGE(0x42);
1222179551Sjhb		XE_OUTB(XE_SWC1, 0x80);
1223179551Sjhb		scp->media |= IFM_10_T;
1224179551Sjhb		break;
1225179551Sjhb	case IFM_10_2:
1226179551Sjhb		DEVPRINTF(2, (scp->dev, "Selecting 10base2\n"));
1227179551Sjhb		XE_SELECT_PAGE(0x42);
1228179551Sjhb		XE_OUTB(XE_SWC1, 0xc0);
1229179551Sjhb		scp->media |= IFM_10_2;
1230179551Sjhb		break;
123147136Sobrien	}
123247136Sobrien
1233179551Sjhb	/*
1234179551Sjhb	 * Finally, the LEDs are set to match whatever media was
1235179551Sjhb	 * chosen and the transmitter is unblocked.
1236179551Sjhb	 */
1237179551Sjhb	DEVPRINTF(2, (scp->dev, "Setting LEDs\n"));
123847136Sobrien	XE_SELECT_PAGE(2);
1239179551Sjhb	switch (IFM_SUBTYPE(scp->media)) {
1240179551Sjhb	case IFM_100_TX:
1241179551Sjhb	case IFM_10_T:
1242179551Sjhb		XE_OUTB(XE_LED, 0x3b);
1243179551Sjhb		if (scp->dingo)
1244179551Sjhb			XE_OUTB(0x0b, 0x04);	/* 100Mbit LED */
1245179551Sjhb		break;
1246179551Sjhb	case IFM_10_2:
1247179551Sjhb		XE_OUTB(XE_LED, 0x3a);
1248179551Sjhb		break;
1249179551Sjhb	}
125047136Sobrien
1251179551Sjhb	/* Restart output? */
1252179551Sjhb	xe_enable_intr(scp);
1253179551Sjhb	scp->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1254179551Sjhb	xe_start_locked(scp->ifp);
125547136Sobrien}
125647136Sobrien
125747136Sobrien/*
125847136Sobrien * Hard reset (power cycle) the card.
125947136Sobrien */
126047136Sobrienstatic void
1261179551Sjhbxe_reset(struct xe_softc *scp)
1262179551Sjhb{
126347136Sobrien
1264179551Sjhb	DEVPRINTF(2, (scp->dev, "reset\n"));
126547136Sobrien
1266179551Sjhb	XE_ASSERT_LOCKED(scp);
126747136Sobrien
1268179551Sjhb	/* Power down */
1269179551Sjhb	XE_SELECT_PAGE(4);
1270179551Sjhb	XE_OUTB(XE_GPR1, 0);
1271179551Sjhb	DELAY(40000);
127247136Sobrien
1273179551Sjhb	/* Power up again */
1274179551Sjhb	if (scp->mohawk)
1275179551Sjhb		XE_OUTB(XE_GPR1, XE_GPR1_POWER_DOWN);
1276179551Sjhb	else
1277179551Sjhb		XE_OUTB(XE_GPR1, XE_GPR1_POWER_DOWN | XE_GPR1_AIC);
127847136Sobrien
1279179551Sjhb	DELAY(40000);
1280179551Sjhb	XE_SELECT_PAGE(0);
128147133Sobrien}
128247133Sobrien
128347133Sobrien/*
128447136Sobrien * Take interface offline.  This is done by powering down the device, which I
128547136Sobrien * assume means just shutting down the transceiver and Ethernet logic.  This
128647136Sobrien * requires a _hard_ reset to recover from, as we need to power up again.
128747133Sobrien */
1288179492Sjhbvoid
1289179551Sjhbxe_stop(struct xe_softc *scp)
1290179551Sjhb{
129147133Sobrien
1292179551Sjhb	DEVPRINTF(2, (scp->dev, "stop\n"));
129347133Sobrien
1294179551Sjhb	XE_ASSERT_LOCKED(scp);
129547133Sobrien
1296179551Sjhb	/*
1297179551Sjhb	 * Shut off interrupts.
1298179551Sjhb	 */
1299179551Sjhb	xe_disable_intr(scp);
130047133Sobrien
1301179551Sjhb	/*
1302179551Sjhb	 * Power down.
1303179551Sjhb	 */
1304179551Sjhb	XE_SELECT_PAGE(4);
1305179551Sjhb	XE_OUTB(XE_GPR1, 0);
1306179551Sjhb	XE_SELECT_PAGE(0);
1307179551Sjhb	if (scp->mohawk) {
1308179551Sjhb		/*
1309179551Sjhb		 * set GP1 and GP2 as outputs (bits 2 & 3)
1310179551Sjhb		 * set GP1 high to power on the ML6692 (bit 0)
1311179551Sjhb		 * set GP2 low to power on the 10Mhz chip (bit 1)
1312179551Sjhb		 */
1313179551Sjhb		XE_SELECT_PAGE(4);
1314179551Sjhb		XE_OUTB(XE_GPR0, XE_GPR0_GP2_SELECT | XE_GPR0_GP1_SELECT |
1315179551Sjhb		    XE_GPR0_GP1_OUT);
1316179551Sjhb	}
131747136Sobrien
1318179551Sjhb	/*
1319179551Sjhb	 * ~IFF_DRV_RUNNING == interface down.
1320179551Sjhb	 */
1321179551Sjhb	scp->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1322179551Sjhb	scp->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1323179551Sjhb	scp->tx_timeout = 0;
1324179551Sjhb	callout_stop(&scp->wdog_timer);
1325179551Sjhb	callout_stop(&scp->media_timer);
132647133Sobrien}
132747133Sobrien
132847133Sobrien/*
1329121099Srsm * Enable interrupts from the card.
133047136Sobrien */
133147136Sobrienstatic void
1332179551Sjhbxe_enable_intr(struct xe_softc *scp)
1333179551Sjhb{
1334121099Srsm
1335179551Sjhb	DEVPRINTF(2, (scp->dev, "enable_intr\n"));
133647136Sobrien
1337179551Sjhb	XE_SELECT_PAGE(0);
1338179551Sjhb	XE_OUTB(XE_CR, XE_CR_ENABLE_INTR);	/* Enable interrupts */
1339179551Sjhb	if (scp->modem && !scp->dingo) {	/* This bit is just magic */
1340179551Sjhb		if (!(XE_INB(0x10) & 0x01)) {
1341179551Sjhb			XE_OUTB(0x10, 0x11);	/* Unmask master int enable */
1342179551Sjhb		}
1343179551Sjhb	}
134447136Sobrien}
134547136Sobrien
134647136Sobrien/*
1347121099Srsm * Disable interrupts from the card.
134847136Sobrien */
134947136Sobrienstatic void
1350179551Sjhbxe_disable_intr(struct xe_softc *scp)
1351179551Sjhb{
1352121099Srsm
1353179551Sjhb	DEVPRINTF(2, (scp->dev, "disable_intr\n"));
135447136Sobrien
1355179551Sjhb	XE_SELECT_PAGE(0);
1356179551Sjhb	XE_OUTB(XE_CR, 0);			/* Disable interrupts */
1357179551Sjhb	if (scp->modem && !scp->dingo) {	/* More magic */
1358179551Sjhb		XE_OUTB(0x10, 0x10);		/* Mask the master int enable */
1359179551Sjhb	}
136047136Sobrien}
136147136Sobrien
136247136Sobrien/*
1363121099Srsm * Set up multicast filter and promiscuous modes.
136447133Sobrien */
136547133Sobrienstatic void
1366179551Sjhbxe_set_multicast(struct xe_softc *scp)
1367179551Sjhb{
1368179551Sjhb	struct ifnet *ifp;
1369179551Sjhb	struct ifmultiaddr *maddr;
1370179551Sjhb	unsigned count, i;
137147133Sobrien
1372179551Sjhb	DEVPRINTF(2, (scp->dev, "set_multicast\n"));
1373121099Srsm
1374179551Sjhb	ifp = scp->ifp;
1375179551Sjhb	XE_SELECT_PAGE(0x42);
137647133Sobrien
1377179551Sjhb	/* Handle PROMISC flag */
1378179551Sjhb	if (ifp->if_flags & IFF_PROMISC) {
1379179551Sjhb		XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) | XE_SWC1_PROMISCUOUS);
1380179551Sjhb		return;
1381179551Sjhb	} else
1382179551Sjhb		XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~XE_SWC1_PROMISCUOUS);
138347133Sobrien
1384179551Sjhb	/* Handle ALLMULTI flag */
1385179551Sjhb	if (ifp->if_flags & IFF_ALLMULTI) {
1386179551Sjhb		XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) | XE_SWC1_ALLMULTI);
1387179551Sjhb		return;
1388179551Sjhb	} else
1389179551Sjhb		XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI);
1390121099Srsm
1391179551Sjhb	/* Iterate over multicast address list */
1392179551Sjhb	count = 0;
1393195049Srwatson	if_maddr_rlock(ifp);
1394179551Sjhb	TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) {
1395179551Sjhb		if (maddr->ifma_addr->sa_family != AF_LINK)
1396179551Sjhb			continue;
1397121099Srsm
1398179551Sjhb		count++;
1399121099Srsm
1400179551Sjhb		if (count < 10)
1401179551Sjhb			/*
1402179551Sjhb			 * First 9 use Individual Addresses for exact
1403179551Sjhb			 * matching.
1404179551Sjhb			 */
1405179551Sjhb			xe_set_addr(scp,
1406179551Sjhb			    LLADDR((struct sockaddr_dl *)maddr->ifma_addr),
1407179551Sjhb			    count);
1408179551Sjhb		else if (scp->mohawk)
1409179551Sjhb			/* Use hash filter on Mohawk and Dingo */
1410179551Sjhb			xe_mchash(scp,
1411179551Sjhb			    LLADDR((struct sockaddr_dl *)maddr->ifma_addr));
1412179551Sjhb		else
1413179551Sjhb			/* Nowhere else to put them on CE2 */
1414179551Sjhb			break;
1415179551Sjhb	}
1416195049Srwatson	if_maddr_runlock(ifp);
1417121099Srsm
1418179551Sjhb	DEVPRINTF(2, (scp->dev, "set_multicast: count = %u\n", count));
1419121099Srsm
1420179551Sjhb	/* Now do some cleanup and enable multicast handling as needed */
1421179551Sjhb	if (count == 0) {
1422179551Sjhb		/* Disable all multicast handling */
1423179551Sjhb		XE_SELECT_PAGE(0x42);
1424179551Sjhb		XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) &
1425179551Sjhb		    ~(XE_SWC1_IA_ENABLE | XE_SWC1_ALLMULTI));
1426179551Sjhb		if (scp->mohawk) {
1427179551Sjhb			XE_SELECT_PAGE(0x02);
1428179551Sjhb			XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE);
1429179551Sjhb		}
1430179551Sjhb	} else if (count < 10) {
1431179551Sjhb		/*
1432179551Sjhb		 * Full in any unused Individual Addresses with our
1433179551Sjhb		 * MAC address.
1434179551Sjhb		 */
1435179551Sjhb		for (i = count + 1; i < 10; i++)
1436179551Sjhb			xe_set_addr(scp, IF_LLADDR(scp->ifp), i);
1437121099Srsm
1438179551Sjhb		/* Enable Individual Address matching only */
1439179551Sjhb		XE_SELECT_PAGE(0x42);
1440179551Sjhb		XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI) |
1441179551Sjhb		    XE_SWC1_IA_ENABLE);
1442179551Sjhb		if (scp->mohawk) {
1443179551Sjhb			XE_SELECT_PAGE(0x02);
1444179551Sjhb			XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE);
1445179551Sjhb		}
1446179551Sjhb	} else if (scp->mohawk) {
1447179551Sjhb		/* Check whether hash table is full */
1448179551Sjhb		XE_SELECT_PAGE(0x58);
1449179551Sjhb		for (i = 0x08; i < 0x10; i++)
1450179551Sjhb			if (XE_INB(i) != 0xff)
1451179551Sjhb				break;
1452179551Sjhb		if (i == 0x10) {
1453179551Sjhb			/*
1454179551Sjhb			 * Hash table full - enable
1455179551Sjhb			 * promiscuous multicast matching
1456179551Sjhb			 */
1457179551Sjhb			XE_SELECT_PAGE(0x42);
1458179551Sjhb			XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) &
1459179551Sjhb			    ~XE_SWC1_IA_ENABLE) | XE_SWC1_ALLMULTI);
1460179551Sjhb			XE_SELECT_PAGE(0x02);
1461179551Sjhb			XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE);
1462179551Sjhb		} else {
1463179551Sjhb			/* Enable hash table and Individual Address matching */
1464179551Sjhb			XE_SELECT_PAGE(0x42);
1465179551Sjhb			XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI) |
1466179551Sjhb			    XE_SWC1_IA_ENABLE);
1467179551Sjhb			XE_SELECT_PAGE(0x02);
1468179551Sjhb			XE_OUTB(XE_MSR, XE_INB(XE_MSR) | XE_MSR_HASH_TABLE);
1469179551Sjhb		}
1470179551Sjhb	} else {
1471179551Sjhb		/* Enable promiscuous multicast matching */
1472179551Sjhb		XE_SELECT_PAGE(0x42);
1473179551Sjhb		XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_IA_ENABLE) |
1474179551Sjhb		    XE_SWC1_ALLMULTI);
1475179551Sjhb	}
1476179551Sjhb
1477179551Sjhb	XE_SELECT_PAGE(0);
147847133Sobrien}
147947133Sobrien
148047133Sobrien/*
1481121099Srsm * Copy the Ethernet multicast address in addr to the on-chip registers for
1482121099Srsm * Individual Address idx.  Assumes that addr is really a multicast address
1483121099Srsm * and that idx > 0 (slot 0 is always used for the card MAC address).
148447133Sobrien */
148547133Sobrienstatic void
1486179551Sjhbxe_set_addr(struct xe_softc *scp, uint8_t* addr, unsigned idx)
1487179551Sjhb{
1488179551Sjhb	uint8_t page, reg;
1489179551Sjhb	unsigned i;
149047133Sobrien
1491179551Sjhb	/*
1492179551Sjhb	 * Individual Addresses are stored in registers 8-F of pages
1493179551Sjhb	 * 0x50-0x57.  IA1 therefore starts at register 0xE on page
1494179551Sjhb	 * 0x50.  The expressions below compute the starting page and
1495179551Sjhb	 * register for any IA index > 0.
1496179551Sjhb	 */
1497179551Sjhb	--idx;
1498179551Sjhb	page = 0x50 + idx % 4 + idx / 4 * 3;
1499179551Sjhb	reg = 0x0e - 2 * (idx % 4);
150047133Sobrien
1501179551Sjhb	DEVPRINTF(3, (scp->dev,
1502179551Sjhb	    "set_addr: idx = %u, page = 0x%02x, reg = 0x%02x\n", idx + 1, page,
1503179551Sjhb	    reg));
150447133Sobrien
1505179551Sjhb	/*
1506179551Sjhb	 * Copy the IA bytes.  Note that the byte order is reversed
1507179551Sjhb	 * for Mohawk and Dingo wrt. CE2 hardware.
1508179551Sjhb	 */
1509179551Sjhb	XE_SELECT_PAGE(page);
1510179551Sjhb	for (i = 0; i < ETHER_ADDR_LEN; i++) {
1511179551Sjhb		if (i > 0) {
1512179551Sjhb			DPRINTF(3, (":%02x", addr[i]));
1513179551Sjhb		} else {
1514179551Sjhb			DEVPRINTF(3, (scp->dev, "set_addr: %02x", addr[0]));
1515179551Sjhb		}
1516179551Sjhb		XE_OUTB(reg, addr[scp->mohawk ? 5 - i : i]);
1517179551Sjhb		if (++reg == 0x10) {
1518179551Sjhb			reg = 0x08;
1519179551Sjhb			XE_SELECT_PAGE(++page);
1520179551Sjhb		}
1521179551Sjhb	}
1522179551Sjhb	DPRINTF(3, ("\n"));
1523121099Srsm}
152447133Sobrien
1525121099Srsm/*
1526121099Srsm * Set the appropriate bit in the multicast hash table for the supplied
1527121099Srsm * Ethernet multicast address addr.  Assumes that addr is really a multicast
1528121099Srsm * address.
1529121099Srsm */
1530121099Srsmstatic void
1531179551Sjhbxe_mchash(struct xe_softc* scp, const uint8_t *addr)
1532179551Sjhb{
1533179551Sjhb	int bit;
1534179551Sjhb	uint8_t byte, hash;
1535121099Srsm
1536179551Sjhb	hash = ether_crc32_le(addr, ETHER_ADDR_LEN) & 0x3F;
153747133Sobrien
1538179551Sjhb	/*
1539179551Sjhb	 * Top 3 bits of hash give register - 8, bottom 3 give bit
1540179551Sjhb	 * within register.
1541179551Sjhb	 */
1542179551Sjhb	byte = hash >> 3 | 0x08;
1543179551Sjhb	bit = 0x01 << (hash & 0x07);
1544121099Srsm
1545179551Sjhb	DEVPRINTF(3, (scp->dev,
1546179551Sjhb	    "set_hash: hash = 0x%02x, byte = 0x%02x, bit = 0x%02x\n", hash,
1547179551Sjhb	    byte, bit));
1548121099Srsm
1549179551Sjhb	XE_SELECT_PAGE(0x58);
1550179551Sjhb	XE_OUTB(byte, XE_INB(byte) | bit);
155147133Sobrien}
155247133Sobrien
155347133Sobrien/*
155447133Sobrien * Write an outgoing packet to the card using programmed I/O.
155547133Sobrien */
155647133Sobrienstatic int
1557179551Sjhbxe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp)
1558179551Sjhb{
1559179551Sjhb	unsigned len, pad;
1560179551Sjhb	unsigned char wantbyte;
1561179551Sjhb	uint8_t *data;
1562179551Sjhb	uint8_t savebyte[2];
156347133Sobrien
1564179551Sjhb	/* Get total packet length */
1565179551Sjhb	if (mbp->m_flags & M_PKTHDR)
1566179551Sjhb		len = mbp->m_pkthdr.len;
1567179551Sjhb	else {
1568179551Sjhb		struct mbuf* mbp2 = mbp;
1569179551Sjhb		for (len = 0; mbp2 != NULL;
1570179551Sjhb		     len += mbp2->m_len, mbp2 = mbp2->m_next);
1571179551Sjhb	}
157247133Sobrien
1573179551Sjhb	DEVPRINTF(3, (scp->dev, "pio_write_packet: len = %u\n", len));
1574121099Srsm
1575179551Sjhb	/* Packets < minimum length may need to be padded out */
1576179551Sjhb	pad = 0;
1577179551Sjhb	if (len < scp->tx_min) {
1578179551Sjhb		pad = scp->tx_min - len;
1579179551Sjhb		len = scp->tx_min;
1580179551Sjhb	}
158147133Sobrien
1582179551Sjhb	/* Check transmit buffer space */
1583179551Sjhb	XE_SELECT_PAGE(0);
1584179551Sjhb	XE_OUTW(XE_TRS, len + 2);	/* Only effective on rev. 1 CE2 cards */
1585179551Sjhb	if ((XE_INW(XE_TSO) & 0x7fff) <= len + 2)
1586179551Sjhb		return (1);
158747133Sobrien
1588179551Sjhb	/* Send packet length to card */
1589179551Sjhb	XE_OUTW(XE_EDP, len);
159047133Sobrien
1591179551Sjhb	/*
1592179551Sjhb	 * Write packet to card using PIO (code stolen from the ed driver)
1593179551Sjhb	 */
159447133Sobrien	wantbyte = 0;
1595179551Sjhb	while (mbp != NULL) {
1596179551Sjhb		len = mbp->m_len;
1597179551Sjhb		if (len > 0) {
1598179551Sjhb			data = mtod(mbp, caddr_t);
1599179551Sjhb			if (wantbyte) {		/* Finish the last word */
1600179551Sjhb				savebyte[1] = *data;
1601179551Sjhb				XE_OUTW(XE_EDP, *(u_short *)savebyte);
1602179551Sjhb				data++;
1603179551Sjhb				len--;
1604179551Sjhb				wantbyte = 0;
1605179551Sjhb			}
1606179551Sjhb			if (len > 1) {		/* Output contiguous words */
1607179551Sjhb				bus_write_multi_2(scp->port_res, XE_EDP,
1608179551Sjhb				    (uint16_t *)data, len >> 1);
1609179551Sjhb				data += len & ~1;
1610179551Sjhb				len &= 1;
1611179551Sjhb			}
1612179551Sjhb			if (len == 1) {		/* Save last byte, if needed */
1613179551Sjhb				savebyte[0] = *data;
1614179551Sjhb				wantbyte = 1;
1615179551Sjhb			}
1616179551Sjhb		}
1617179551Sjhb		mbp = mbp->m_next;
1618179551Sjhb	}
161947133Sobrien
1620179551Sjhb	/*
1621179551Sjhb	 * Send last byte of odd-length packets
1622179551Sjhb	 */
1623179551Sjhb	if (wantbyte)
1624179551Sjhb		XE_OUTB(XE_EDP, savebyte[0]);
1625121099Srsm
1626179551Sjhb	/*
1627179551Sjhb	 * Can just tell CE3 cards to send; short packets will be
1628179551Sjhb	 * padded out with random cruft automatically.  For CE2,
1629179551Sjhb	 * manually pad the packet with garbage; it will be sent when
1630179551Sjhb	 * the required number of bytes have been delivered to the
1631179551Sjhb	 * card.
1632179551Sjhb	 */
1633179551Sjhb	if (scp->mohawk)
1634179551Sjhb		XE_OUTB(XE_CR, XE_CR_TX_PACKET | XE_CR_RESTART_TX |
1635179551Sjhb		    XE_CR_ENABLE_INTR);
1636179551Sjhb	else if (pad > 0) {
1637179551Sjhb		if (pad & 0x01)
1638179551Sjhb			XE_OUTB(XE_EDP, 0xaa);
1639179551Sjhb		pad >>= 1;
1640179551Sjhb		while (pad > 0) {
1641179551Sjhb			XE_OUTW(XE_EDP, 0xdead);
1642179551Sjhb			pad--;
1643179551Sjhb		}
1644179551Sjhb	}
164547133Sobrien
1646179551Sjhb	return (0);
164747133Sobrien}
164847133Sobrien
164947133Sobrien/**************************************************************
165047133Sobrien *                                                            *
165147133Sobrien *                  M I I  F U N C T I O N S                  *
165247133Sobrien *                                                            *
165347133Sobrien **************************************************************/
165447133Sobrien
165547133Sobrien/*
165647133Sobrien * Alternative MII/PHY handling code adapted from the xl driver.  It doesn't
165747133Sobrien * seem to work any better than the xirc2_ps stuff, but it's cleaner code.
165848117Sobrien * XXX - this stuff shouldn't be here.  It should all be abstracted off to
165948117Sobrien * XXX - some kind of common MII-handling code, shared by all drivers.  But
166048117Sobrien * XXX - that's a whole other mission.
166147133Sobrien */
1662179551Sjhb#define	XE_MII_SET(x)	XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) | (x))
1663179551Sjhb#define	XE_MII_CLR(x)	XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) & ~(x))
166447133Sobrien
166547133Sobrien/*
166647133Sobrien * Sync the PHYs by setting data bit and strobing the clock 32 times.
166747133Sobrien */
166847133Sobrienstatic void
1669179551Sjhbxe_mii_sync(struct xe_softc *scp)
1670179551Sjhb{
1671179551Sjhb	int i;
167247133Sobrien
1673179551Sjhb	XE_SELECT_PAGE(2);
1674179551Sjhb	XE_MII_SET(XE_MII_DIR|XE_MII_WRD);
167547133Sobrien
1676179551Sjhb	for (i = 0; i < 32; i++) {
1677179551Sjhb		XE_MII_SET(XE_MII_CLK);
1678179551Sjhb		DELAY(1);
1679179551Sjhb		XE_MII_CLR(XE_MII_CLK);
1680179551Sjhb		DELAY(1);
1681179551Sjhb	}
168247133Sobrien}
168347133Sobrien
168447133Sobrien/*
168547136Sobrien * Look for a MII-compliant PHY.  If we find one, reset it.
168647136Sobrien */
168747136Sobrienstatic int
1688179551Sjhbxe_mii_init(struct xe_softc *scp)
1689179551Sjhb{
1690179551Sjhb	uint16_t status;
169147136Sobrien
1692179551Sjhb	status = xe_phy_readreg(scp, PHY_BMSR);
1693179551Sjhb	if ((status & 0xff00) != 0x7800) {
1694179551Sjhb		DEVPRINTF(2, (scp->dev, "no PHY found, %0x\n", status));
1695179551Sjhb		return (0);
1696179551Sjhb	} else {
1697179551Sjhb		DEVPRINTF(2, (scp->dev, "PHY OK!\n"));
169847136Sobrien
1699179551Sjhb		/* Reset the PHY */
1700179551Sjhb		xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_RESET);
1701179551Sjhb		DELAY(500);
1702179551Sjhb		while(xe_phy_readreg(scp, PHY_BMCR) & PHY_BMCR_RESET)
1703179551Sjhb			;	/* nothing */
1704179551Sjhb		XE_MII_DUMP(scp);
1705179551Sjhb		return (1);
1706179551Sjhb	}
170747136Sobrien}
170847136Sobrien
170947136Sobrien/*
171047133Sobrien * Clock a series of bits through the MII.
171147133Sobrien */
171247133Sobrienstatic void
1713179551Sjhbxe_mii_send(struct xe_softc *scp, uint32_t bits, int cnt)
1714179551Sjhb{
1715179551Sjhb	int i;
171647133Sobrien
1717179551Sjhb	XE_SELECT_PAGE(2);
1718179551Sjhb	XE_MII_CLR(XE_MII_CLK);
171947133Sobrien
1720179551Sjhb	for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
1721179551Sjhb		if (bits & i) {
1722179551Sjhb			XE_MII_SET(XE_MII_WRD);
1723179551Sjhb		} else {
1724179551Sjhb			XE_MII_CLR(XE_MII_WRD);
1725179551Sjhb		}
1726179551Sjhb		DELAY(1);
1727179551Sjhb		XE_MII_CLR(XE_MII_CLK);
1728179551Sjhb		DELAY(1);
1729179551Sjhb		XE_MII_SET(XE_MII_CLK);
1730179551Sjhb	}
173147133Sobrien}
173247133Sobrien
173347133Sobrien/*
173447133Sobrien * Read an PHY register through the MII.
173547133Sobrien */
173647133Sobrienstatic int
1737179551Sjhbxe_mii_readreg(struct xe_softc *scp, struct xe_mii_frame *frame)
1738179551Sjhb{
1739179551Sjhb	int i, ack;
174047133Sobrien
1741179551Sjhb	XE_ASSERT_LOCKED(scp);
174247133Sobrien
1743179551Sjhb	/*
1744179551Sjhb	 * Set up frame for RX.
1745179551Sjhb	 */
1746179551Sjhb	frame->mii_stdelim = XE_MII_STARTDELIM;
1747179551Sjhb	frame->mii_opcode = XE_MII_READOP;
1748179551Sjhb	frame->mii_turnaround = 0;
1749179551Sjhb	frame->mii_data = 0;
175047133Sobrien
1751179551Sjhb	XE_SELECT_PAGE(2);
1752179551Sjhb	XE_OUTB(XE_GPR2, 0);
175347133Sobrien
1754179551Sjhb	/*
1755179551Sjhb	 * Turn on data xmit.
1756179551Sjhb	 */
1757179551Sjhb	XE_MII_SET(XE_MII_DIR);
175847133Sobrien
1759179551Sjhb	xe_mii_sync(scp);
176047133Sobrien
1761179551Sjhb	/*
1762179551Sjhb	 * Send command/address info.
1763179551Sjhb	 */
1764179551Sjhb	xe_mii_send(scp, frame->mii_stdelim, 2);
1765179551Sjhb	xe_mii_send(scp, frame->mii_opcode, 2);
1766179551Sjhb	xe_mii_send(scp, frame->mii_phyaddr, 5);
1767179551Sjhb	xe_mii_send(scp, frame->mii_regaddr, 5);
176847133Sobrien
1769179551Sjhb	/* Idle bit */
1770179551Sjhb	XE_MII_CLR((XE_MII_CLK|XE_MII_WRD));
1771179551Sjhb	DELAY(1);
1772179551Sjhb	XE_MII_SET(XE_MII_CLK);
1773179551Sjhb	DELAY(1);
177447133Sobrien
1775179551Sjhb	/* Turn off xmit. */
1776179551Sjhb	XE_MII_CLR(XE_MII_DIR);
177747133Sobrien
1778179551Sjhb	/* Check for ack */
1779179551Sjhb	XE_MII_CLR(XE_MII_CLK);
1780179551Sjhb	DELAY(1);
1781179551Sjhb	ack = XE_INB(XE_GPR2) & XE_MII_RDD;
1782179551Sjhb	XE_MII_SET(XE_MII_CLK);
1783179551Sjhb	DELAY(1);
178447133Sobrien
1785179551Sjhb	/*
1786179551Sjhb	 * Now try reading data bits. If the ack failed, we still
1787179551Sjhb	 * need to clock through 16 cycles to keep the PHY(s) in sync.
1788179551Sjhb	 */
1789179551Sjhb	if (ack) {
1790179551Sjhb		for(i = 0; i < 16; i++) {
1791179551Sjhb			XE_MII_CLR(XE_MII_CLK);
1792179551Sjhb			DELAY(1);
1793179551Sjhb			XE_MII_SET(XE_MII_CLK);
1794179551Sjhb			DELAY(1);
1795179551Sjhb		}
1796179551Sjhb		goto fail;
1797179551Sjhb	}
179847133Sobrien
1799179551Sjhb	for (i = 0x8000; i; i >>= 1) {
1800179551Sjhb		XE_MII_CLR(XE_MII_CLK);
1801179551Sjhb		DELAY(1);
1802179551Sjhb		if (!ack) {
1803179551Sjhb			if (XE_INB(XE_GPR2) & XE_MII_RDD)
1804179551Sjhb				frame->mii_data |= i;
1805179551Sjhb			DELAY(1);
1806179551Sjhb		}
1807179551Sjhb		XE_MII_SET(XE_MII_CLK);
1808179551Sjhb		DELAY(1);
1809179551Sjhb	}
181047133Sobrien
181147133Sobrienfail:
1812179551Sjhb	XE_MII_CLR(XE_MII_CLK);
1813179551Sjhb	DELAY(1);
1814179551Sjhb	XE_MII_SET(XE_MII_CLK);
1815179551Sjhb	DELAY(1);
181647133Sobrien
1817179551Sjhb	if (ack)
1818179551Sjhb		return(1);
1819179551Sjhb	return(0);
182047133Sobrien}
182147133Sobrien
182247133Sobrien/*
182347133Sobrien * Write to a PHY register through the MII.
182447133Sobrien */
182547133Sobrienstatic int
1826179551Sjhbxe_mii_writereg(struct xe_softc *scp, struct xe_mii_frame *frame)
1827179551Sjhb{
182847133Sobrien
1829179551Sjhb	XE_ASSERT_LOCKED(scp);
183047133Sobrien
1831179551Sjhb	/*
1832179551Sjhb	 * Set up frame for TX.
1833179551Sjhb	 */
1834179551Sjhb	frame->mii_stdelim = XE_MII_STARTDELIM;
1835179551Sjhb	frame->mii_opcode = XE_MII_WRITEOP;
1836179551Sjhb	frame->mii_turnaround = XE_MII_TURNAROUND;
183747133Sobrien
1838179551Sjhb	XE_SELECT_PAGE(2);
183947133Sobrien
1840179551Sjhb	/*
1841179551Sjhb	 * Turn on data output.
1842179551Sjhb	 */
1843179551Sjhb	XE_MII_SET(XE_MII_DIR);
184447133Sobrien
1845179551Sjhb	xe_mii_sync(scp);
184647133Sobrien
1847179551Sjhb	xe_mii_send(scp, frame->mii_stdelim, 2);
1848179551Sjhb	xe_mii_send(scp, frame->mii_opcode, 2);
1849179551Sjhb	xe_mii_send(scp, frame->mii_phyaddr, 5);
1850179551Sjhb	xe_mii_send(scp, frame->mii_regaddr, 5);
1851179551Sjhb	xe_mii_send(scp, frame->mii_turnaround, 2);
1852179551Sjhb	xe_mii_send(scp, frame->mii_data, 16);
185347133Sobrien
1854179551Sjhb	/* Idle bit. */
1855179551Sjhb	XE_MII_SET(XE_MII_CLK);
1856179551Sjhb	DELAY(1);
1857179551Sjhb	XE_MII_CLR(XE_MII_CLK);
1858179551Sjhb	DELAY(1);
185947133Sobrien
1860179551Sjhb	/*
1861179551Sjhb	 * Turn off xmit.
1862179551Sjhb	 */
1863179551Sjhb	XE_MII_CLR(XE_MII_DIR);
186447133Sobrien
1865179551Sjhb	return(0);
186647133Sobrien}
186747133Sobrien
186847136Sobrien/*
186947136Sobrien * Read a register from the PHY.
187047136Sobrien */
1871179551Sjhbstatic uint16_t
1872179551Sjhbxe_phy_readreg(struct xe_softc *scp, uint16_t reg)
1873179551Sjhb{
1874179551Sjhb	struct xe_mii_frame frame;
187547133Sobrien
1876179551Sjhb	bzero((char *)&frame, sizeof(frame));
187747133Sobrien
1878179551Sjhb	frame.mii_phyaddr = 0;
1879179551Sjhb	frame.mii_regaddr = reg;
1880179551Sjhb	xe_mii_readreg(scp, &frame);
188147133Sobrien
1882179551Sjhb	return (frame.mii_data);
188347133Sobrien}
188447133Sobrien
188547136Sobrien/*
188647136Sobrien * Write to a PHY register.
188747136Sobrien */
188847133Sobrienstatic void
1889179551Sjhbxe_phy_writereg(struct xe_softc *scp, uint16_t reg, uint16_t data)
1890179551Sjhb{
1891179551Sjhb	struct xe_mii_frame frame;
189247133Sobrien
1893179551Sjhb	bzero((char *)&frame, sizeof(frame));
189447133Sobrien
1895179551Sjhb	frame.mii_phyaddr = 0;
1896179551Sjhb	frame.mii_regaddr = reg;
1897179551Sjhb	frame.mii_data = data;
1898179551Sjhb	xe_mii_writereg(scp, &frame);
189947133Sobrien}
190047133Sobrien
190147145Sobrien/*
190247145Sobrien * A bit of debugging code.
190347145Sobrien */
190447133Sobrienstatic void
1905179551Sjhbxe_mii_dump(struct xe_softc *scp)
1906179551Sjhb{
1907179551Sjhb	int i;
190847133Sobrien
1909179551Sjhb	device_printf(scp->dev, "MII registers: ");
1910179551Sjhb	for (i = 0; i < 2; i++) {
1911179551Sjhb		printf(" %d:%04x", i, xe_phy_readreg(scp, i));
1912179551Sjhb	}
1913179551Sjhb	for (i = 4; i < 7; i++) {
1914179551Sjhb		printf(" %d:%04x", i, xe_phy_readreg(scp, i));
1915179551Sjhb	}
1916179551Sjhb	printf("\n");
191747133Sobrien}
191847133Sobrien
1919122170Srsm#if 0
1920122106Simpvoid
1921179551Sjhbxe_reg_dump(struct xe_softc *scp)
1922179551Sjhb{
1923179551Sjhb	int page, i;
192447133Sobrien
1925179551Sjhb	device_printf(scp->dev, "Common registers: ");
1926179551Sjhb	for (i = 0; i < 8; i++) {
1927179551Sjhb		printf(" %2.2x", XE_INB(i));
1928179551Sjhb	}
1929179551Sjhb	printf("\n");
193047133Sobrien
1931179551Sjhb	for (page = 0; page <= 8; page++) {
1932179551Sjhb		device_printf(scp->dev, "Register page %2.2x: ", page);
1933179551Sjhb		XE_SELECT_PAGE(page);
1934179551Sjhb		for (i = 8; i < 16; i++) {
1935179551Sjhb			printf(" %2.2x", XE_INB(i));
1936179551Sjhb		}
1937179551Sjhb		printf("\n");
1938179551Sjhb	}
193947133Sobrien
1940179551Sjhb	for (page = 0x10; page < 0x5f; page++) {
1941179551Sjhb		if ((page >= 0x11 && page <= 0x3f) ||
1942179551Sjhb		    (page == 0x41) ||
1943179551Sjhb		    (page >= 0x43 && page <= 0x4f) ||
1944179551Sjhb		    (page >= 0x59))
1945179551Sjhb			continue;
1946179551Sjhb		device_printf(scp->dev, "Register page %2.2x: ", page);
1947179551Sjhb		XE_SELECT_PAGE(page);
1948179551Sjhb		for (i = 8; i < 16; i++) {
1949179551Sjhb			printf(" %2.2x", XE_INB(i));
1950179551Sjhb		}
1951179551Sjhb		printf("\n");
1952179551Sjhb	}
195347133Sobrien}
1954122170Srsm#endif
195547133Sobrien
195655723Simpint
195755723Simpxe_activate(device_t dev)
195855723Simp{
195955723Simp	struct xe_softc *sc = device_get_softc(dev);
1960179492Sjhb	int start, i;
196147133Sobrien
1962122081Srsm	DEVPRINTF(2, (dev, "activate\n"));
1963121099Srsm
1964121099Srsm	if (!sc->modem) {
196561084Simp		sc->port_rid = 0;	/* 0 is managed by pccard */
196661084Simp		sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT,
1967179551Sjhb		    &sc->port_rid, 0ul, ~0ul, 16, RF_ACTIVE);
1968179551Sjhb	} else if (sc->dingo) {
196961084Simp		/*
197061084Simp		 * Find a 16 byte aligned ioport for the card.
197161084Simp		 */
1972122081Srsm		DEVPRINTF(1, (dev, "Finding an aligned port for RealPort\n"));
197361084Simp		sc->port_rid = 1;	/* 0 is managed by pccard */
197461084Simp		start = 0x100;
197561084Simp		do {
1976179551Sjhb			sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT,
1977179551Sjhb			    &sc->port_rid, start, 0x3ff, 16, RF_ACTIVE);
1978179551Sjhb			if (sc->port_res == NULL)
1979179551Sjhb				break;
198061084Simp			if ((rman_get_start(sc->port_res) & 0xf) == 0)
1981179551Sjhb				break;
198261084Simp			bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid,
198361084Simp			    sc->port_res);
198461084Simp			start = (rman_get_start(sc->port_res) + 15) & ~0xf;
198561084Simp		} while (1);
1986122081Srsm		DEVPRINTF(1, (dev, "RealPort port 0x%0lx, size 0x%0lx\n",
1987121099Srsm		    bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid),
1988122081Srsm		    bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid)));
1989179551Sjhb	} else if (sc->ce2) {
1990179551Sjhb		/*
1991179551Sjhb		 * Find contiguous I/O port for the Ethernet function
1992179551Sjhb		 * on CEM2 and CEM3 cards.  We allocate window 0
1993179551Sjhb		 * wherever pccard has decided it should be, then find
1994179551Sjhb		 * an available window adjacent to it for the second
1995179551Sjhb		 * function.  Not sure that both windows are actually
1996179551Sjhb		 * needed.
1997179551Sjhb		 */
1998179551Sjhb		DEVPRINTF(1, (dev, "Finding I/O port for CEM2/CEM3\n"));
1999179551Sjhb		sc->ce2_port_rid = 0;	/* 0 is managed by pccard */
2000179551Sjhb		sc->ce2_port_res = bus_alloc_resource(dev, SYS_RES_IOPORT,
2001179551Sjhb		    &sc->ce2_port_rid, 0ul, ~0ul, 8, RF_ACTIVE);
2002179551Sjhb		if (sc->ce2_port_res == NULL) {
2003179551Sjhb			DEVPRINTF(1, (dev,
2004179551Sjhb			    "Cannot allocate I/O port for modem\n"));
2005179551Sjhb			xe_deactivate(dev);
2006179551Sjhb			return (ENOMEM);
2007179551Sjhb		}
2008121099Srsm
2009179551Sjhb		sc->port_rid = 1;
2010179551Sjhb		start = bus_get_resource_start(dev, SYS_RES_IOPORT,
2011179551Sjhb		    sc->ce2_port_rid);
2012179551Sjhb		for (i = 0; i < 2; i++) {
2013179551Sjhb			start += (i == 0 ? 8 : -24);
2014179551Sjhb			sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT,
2015179551Sjhb			    &sc->port_rid, start, start + 15, 16, RF_ACTIVE);
2016179551Sjhb			if (sc->port_res == NULL)
2017179551Sjhb				continue;
2018179551Sjhb			if (bus_get_resource_start(dev, SYS_RES_IOPORT,
2019179551Sjhb			    sc->port_rid) == start)
2020179551Sjhb				break;
2021121099Srsm
2022179551Sjhb			bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid,
2023179551Sjhb			    sc->port_res);
2024179551Sjhb			sc->port_res = NULL;
2025179551Sjhb		}
2026122081Srsm		DEVPRINTF(1, (dev, "CEM2/CEM3 port 0x%0lx, size 0x%0lx\n",
202761084Simp		    bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid),
2028122081Srsm		    bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid)));
202961084Simp	}
2030121099Srsm
203155723Simp	if (!sc->port_res) {
2032122081Srsm		DEVPRINTF(1, (dev, "Cannot allocate ioport\n"));
2033148030Simp		xe_deactivate(dev);
2034179551Sjhb		return (ENOMEM);
203555723Simp	}
203647133Sobrien
203755723Simp	sc->irq_rid = 0;
2038127135Snjl	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
2039127135Snjl	    RF_ACTIVE);
2040179551Sjhb	if (sc->irq_res == NULL) {
2041122081Srsm		DEVPRINTF(1, (dev, "Cannot allocate irq\n"));
204255723Simp		xe_deactivate(dev);
2043179551Sjhb		return (ENOMEM);
204455723Simp	}
204559620Simp
204655723Simp	return (0);
204755723Simp}
204847151Sobrien
204955723Simpvoid
205055723Simpxe_deactivate(device_t dev)
205155723Simp{
205255723Simp	struct xe_softc *sc = device_get_softc(dev);
205355723Simp
2054122081Srsm	DEVPRINTF(2, (dev, "deactivate\n"));
205555723Simp	if (sc->intrhand)
205655723Simp		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
2057179551Sjhb	sc->intrhand = NULL;
205855723Simp	if (sc->port_res)
205955723Simp		bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid,
206055723Simp		    sc->port_res);
2061179551Sjhb	sc->port_res = NULL;
2062121099Srsm	if (sc->ce2_port_res)
2063121099Srsm	    bus_release_resource(dev, SYS_RES_IOPORT, sc->ce2_port_rid,
2064179551Sjhb		sc->ce2_port_res);
2065179551Sjhb	sc->ce2_port_res = NULL;
206655723Simp	if (sc->irq_res)
206755723Simp		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
206855723Simp		    sc->irq_res);
2069179551Sjhb	sc->irq_res = NULL;
2070179492Sjhb	if (sc->ifp)
2071179492Sjhb		if_free(sc->ifp);
2072179551Sjhb	sc->ifp = NULL;
207347133Sobrien}
2074