if_pcn.c revision 72084
166131Swpaul/*
266131Swpaul * Copyright (c) 2000 Berkeley Software Design, Inc.
366131Swpaul * Copyright (c) 1997, 1998, 1999, 2000
466131Swpaul *	Bill Paul <wpaul@osd.bsdi.com>.  All rights reserved.
566131Swpaul *
666131Swpaul * Redistribution and use in source and binary forms, with or without
766131Swpaul * modification, are permitted provided that the following conditions
866131Swpaul * are met:
966131Swpaul * 1. Redistributions of source code must retain the above copyright
1066131Swpaul *    notice, this list of conditions and the following disclaimer.
1166131Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1266131Swpaul *    notice, this list of conditions and the following disclaimer in the
1366131Swpaul *    documentation and/or other materials provided with the distribution.
1466131Swpaul * 3. All advertising materials mentioning features or use of this software
1566131Swpaul *    must display the following acknowledgement:
1666131Swpaul *	This product includes software developed by Bill Paul.
1766131Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1866131Swpaul *    may be used to endorse or promote products derived from this software
1966131Swpaul *    without specific prior written permission.
2066131Swpaul *
2166131Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2266131Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2366131Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2466131Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2566131Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2666131Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2766131Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2866131Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2966131Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3066131Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3166131Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3266131Swpaul *
3366131Swpaul * $FreeBSD: head/sys/pci/if_pcn.c 72084 2001-02-06 10:12:15Z phk $
3466131Swpaul */
3566131Swpaul
3666131Swpaul/*
3766131Swpaul * AMD Am79c972 fast ethernet PCI NIC driver. Datatheets are available
3866131Swpaul * from http://www.amd.com.
3966131Swpaul *
4066131Swpaul * Written by Bill Paul <wpaul@osd.bsdi.com>
4166131Swpaul */
4266131Swpaul
4366131Swpaul/*
4466131Swpaul * The AMD PCnet/PCI controllers are more advanced and functional
4566131Swpaul * versions of the venerable 7990 LANCE. The PCnet/PCI chips retain
4666131Swpaul * backwards compatibility with the LANCE and thus can be made
4766131Swpaul * to work with older LANCE drivers. This is in fact how the
4866131Swpaul * PCnet/PCI chips were supported in FreeBSD originally. The trouble
4966131Swpaul * is that the PCnet/PCI devices offer several performance enhancements
5066131Swpaul * which can't be exploited in LANCE compatibility mode. Chief among
5166131Swpaul * these enhancements is the ability to perform PCI DMA operations
5266131Swpaul * using 32-bit addressing (which eliminates the need for ISA
5366131Swpaul * bounce-buffering), and special receive buffer alignment (which
5466131Swpaul * allows the receive handler to pass packets to the upper protocol
5566131Swpaul * layers without copying on both the x86 and alpha platforms).
5666131Swpaul */
5766131Swpaul
5866131Swpaul#include <sys/param.h>
5966131Swpaul#include <sys/systm.h>
6066131Swpaul#include <sys/sockio.h>
6166131Swpaul#include <sys/mbuf.h>
6266131Swpaul#include <sys/malloc.h>
6366131Swpaul#include <sys/kernel.h>
6466131Swpaul#include <sys/socket.h>
6566131Swpaul
6666131Swpaul#include <net/if.h>
6766131Swpaul#include <net/if_arp.h>
6866131Swpaul#include <net/ethernet.h>
6966131Swpaul#include <net/if_dl.h>
7066131Swpaul#include <net/if_media.h>
7166131Swpaul
7266131Swpaul#include <net/bpf.h>
7366131Swpaul
7466131Swpaul#include <vm/vm.h>              /* for vtophys */
7566131Swpaul#include <vm/pmap.h>            /* for vtophys */
7666131Swpaul#include <machine/bus_pio.h>
7766131Swpaul#include <machine/bus_memio.h>
7866131Swpaul#include <machine/bus.h>
7966131Swpaul#include <machine/resource.h>
8066131Swpaul#include <sys/bus.h>
8166131Swpaul#include <sys/rman.h>
8266131Swpaul
8366131Swpaul#include <dev/mii/mii.h>
8466131Swpaul#include <dev/mii/miivar.h>
8566131Swpaul
8666131Swpaul#include <pci/pcireg.h>
8766131Swpaul#include <pci/pcivar.h>
8866131Swpaul
8966131Swpaul#define PCN_USEIOSPACE
9066131Swpaul
9166131Swpaul#include <pci/if_pcnreg.h>
9266131Swpaul
9366131SwpaulMODULE_DEPEND(pcn, miibus, 1, 1, 1);
9466131Swpaul
9566131Swpaul/* "controller miibus0" required.  See GENERIC if you get errors here. */
9666131Swpaul#include "miibus_if.h"
9766131Swpaul
9866131Swpaul#ifndef lint
9966131Swpaulstatic const char rcsid[] =
10066131Swpaul  "$FreeBSD: head/sys/pci/if_pcn.c 72084 2001-02-06 10:12:15Z phk $";
10166131Swpaul#endif
10266131Swpaul
10366131Swpaul/*
10466131Swpaul * Various supported device vendors/types and their names.
10566131Swpaul */
10666131Swpaulstatic struct pcn_type pcn_devs[] = {
10766131Swpaul	{ PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" },
10866131Swpaul	{ PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" },
10966131Swpaul	{ 0, 0, NULL }
11066131Swpaul};
11166131Swpaul
11266131Swpaulstatic u_int32_t pcn_csr_read	__P((struct pcn_softc *, int));
11368837Swpaulstatic u_int16_t pcn_csr_read16	__P((struct pcn_softc *, int));
11469067Swpaulstatic u_int16_t pcn_bcr_read16	__P((struct pcn_softc *, int));
11566131Swpaulstatic void pcn_csr_write	__P((struct pcn_softc *, int, int));
11666131Swpaulstatic u_int32_t pcn_bcr_read	__P((struct pcn_softc *, int));
11766131Swpaulstatic void pcn_bcr_write	__P((struct pcn_softc *, int, int));
11866131Swpaul
11966131Swpaulstatic int pcn_probe		__P((device_t));
12066131Swpaulstatic int pcn_attach		__P((device_t));
12166131Swpaulstatic int pcn_detach		__P((device_t));
12266131Swpaul
12366131Swpaulstatic int pcn_newbuf		__P((struct pcn_softc *, int, struct mbuf *));
12466131Swpaulstatic int pcn_encap		__P((struct pcn_softc *,
12566131Swpaul					struct mbuf *, u_int32_t *));
12666131Swpaulstatic void pcn_rxeof		__P((struct pcn_softc *));
12766131Swpaulstatic void pcn_txeof		__P((struct pcn_softc *));
12866131Swpaulstatic void pcn_intr		__P((void *));
12966131Swpaulstatic void pcn_tick		__P((void *));
13066131Swpaulstatic void pcn_start		__P((struct ifnet *));
13166131Swpaulstatic int pcn_ioctl		__P((struct ifnet *, u_long, caddr_t));
13266131Swpaulstatic void pcn_init		__P((void *));
13366131Swpaulstatic void pcn_stop		__P((struct pcn_softc *));
13466131Swpaulstatic void pcn_watchdog		__P((struct ifnet *));
13566131Swpaulstatic void pcn_shutdown		__P((device_t));
13666131Swpaulstatic int pcn_ifmedia_upd	__P((struct ifnet *));
13766131Swpaulstatic void pcn_ifmedia_sts	__P((struct ifnet *, struct ifmediareq *));
13866131Swpaul
13966131Swpaulstatic int pcn_miibus_readreg	__P((device_t, int, int));
14066131Swpaulstatic int pcn_miibus_writereg	__P((device_t, int, int, int));
14166131Swpaulstatic void pcn_miibus_statchg	__P((device_t));
14266131Swpaul
14368270Swpaulstatic void pcn_setfilt		__P((struct ifnet *));
14466131Swpaulstatic void pcn_setmulti	__P((struct pcn_softc *));
14566131Swpaulstatic u_int32_t pcn_crc	__P((caddr_t));
14666131Swpaulstatic void pcn_reset		__P((struct pcn_softc *));
14766131Swpaulstatic int pcn_list_rx_init	__P((struct pcn_softc *));
14866131Swpaulstatic int pcn_list_tx_init	__P((struct pcn_softc *));
14966131Swpaul
15066131Swpaul#ifdef PCN_USEIOSPACE
15166131Swpaul#define PCN_RES			SYS_RES_IOPORT
15266131Swpaul#define PCN_RID			PCN_PCI_LOIO
15366131Swpaul#else
15466131Swpaul#define PCN_RES			SYS_RES_MEMORY
15566131Swpaul#define PCN_RID			PCN_PCI_LOMEM
15666131Swpaul#endif
15766131Swpaul
15866131Swpaulstatic device_method_t pcn_methods[] = {
15966131Swpaul	/* Device interface */
16066131Swpaul	DEVMETHOD(device_probe,		pcn_probe),
16166131Swpaul	DEVMETHOD(device_attach,	pcn_attach),
16266131Swpaul	DEVMETHOD(device_detach,	pcn_detach),
16366131Swpaul	DEVMETHOD(device_shutdown,	pcn_shutdown),
16466131Swpaul
16566131Swpaul	/* bus interface */
16666131Swpaul	DEVMETHOD(bus_print_child,	bus_generic_print_child),
16766131Swpaul	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
16866131Swpaul
16966131Swpaul	/* MII interface */
17066131Swpaul	DEVMETHOD(miibus_readreg,	pcn_miibus_readreg),
17166131Swpaul	DEVMETHOD(miibus_writereg,	pcn_miibus_writereg),
17266131Swpaul	DEVMETHOD(miibus_statchg,	pcn_miibus_statchg),
17366131Swpaul
17466131Swpaul	{ 0, 0 }
17566131Swpaul};
17666131Swpaul
17766131Swpaulstatic driver_t pcn_driver = {
17866131Swpaul	"pcn",
17966131Swpaul	pcn_methods,
18066131Swpaul	sizeof(struct pcn_softc)
18166131Swpaul};
18266131Swpaul
18366131Swpaulstatic devclass_t pcn_devclass;
18466131Swpaul
18566131SwpaulDRIVER_MODULE(if_pcn, pci, pcn_driver, pcn_devclass, 0, 0);
18666131SwpaulDRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0);
18766131Swpaul
18866131Swpaul#define PCN_CSR_SETBIT(sc, reg, x)			\
18966131Swpaul	pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x))
19066131Swpaul
19166131Swpaul#define PCN_CSR_CLRBIT(sc, reg, x)			\
19266131Swpaul	pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x))
19366131Swpaul
19466131Swpaul#define PCN_BCR_SETBIT(sc, reg, x)			\
19566131Swpaul	pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x))
19666131Swpaul
19766131Swpaul#define PCN_BCR_CLRBIT(sc, reg, x)			\
19866131Swpaul	pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x))
19966131Swpaul
20066131Swpaulstatic u_int32_t pcn_csr_read(sc, reg)
20166131Swpaul	struct pcn_softc	*sc;
20266131Swpaul	int			reg;
20366131Swpaul{
20466131Swpaul	CSR_WRITE_4(sc, PCN_IO32_RAP, reg);
20566131Swpaul	return(CSR_READ_4(sc, PCN_IO32_RDP));
20666131Swpaul}
20766131Swpaul
20868837Swpaulstatic u_int16_t pcn_csr_read16(sc, reg)
20968837Swpaul	struct pcn_softc	*sc;
21068837Swpaul	int			reg;
21168837Swpaul{
21268837Swpaul	CSR_WRITE_2(sc, PCN_IO16_RAP, reg);
21368837Swpaul	return(CSR_READ_2(sc, PCN_IO16_RDP));
21468837Swpaul}
21568837Swpaul
21666131Swpaulstatic void pcn_csr_write(sc, reg, val)
21766131Swpaul	struct pcn_softc	*sc;
21866131Swpaul	int			reg;
21966131Swpaul{
22066131Swpaul	CSR_WRITE_4(sc, PCN_IO32_RAP, reg);
22166131Swpaul	CSR_WRITE_4(sc, PCN_IO32_RDP, val);
22266131Swpaul	return;
22366131Swpaul}
22466131Swpaul
22566131Swpaulstatic u_int32_t pcn_bcr_read(sc, reg)
22666131Swpaul	struct pcn_softc	*sc;
22766131Swpaul	int			reg;
22866131Swpaul{
22966131Swpaul	CSR_WRITE_4(sc, PCN_IO32_RAP, reg);
23066131Swpaul	return(CSR_READ_4(sc, PCN_IO32_BDP));
23166131Swpaul}
23266131Swpaul
23369067Swpaulstatic u_int16_t pcn_bcr_read16(sc, reg)
23469067Swpaul	struct pcn_softc	*sc;
23569067Swpaul	int			reg;
23669067Swpaul{
23769067Swpaul	CSR_WRITE_2(sc, PCN_IO16_RAP, reg);
23869067Swpaul	return(CSR_READ_2(sc, PCN_IO16_BDP));
23969067Swpaul}
24069067Swpaul
24166131Swpaulstatic void pcn_bcr_write(sc, reg, val)
24266131Swpaul	struct pcn_softc	*sc;
24366131Swpaul	int			reg;
24466131Swpaul{
24566131Swpaul	CSR_WRITE_4(sc, PCN_IO32_RAP, reg);
24666131Swpaul	CSR_WRITE_4(sc, PCN_IO32_BDP, val);
24766131Swpaul	return;
24866131Swpaul}
24966131Swpaul
25066131Swpaulstatic int pcn_miibus_readreg(dev, phy, reg)
25166131Swpaul	device_t		dev;
25266131Swpaul	int			phy, reg;
25366131Swpaul{
25466131Swpaul	struct pcn_softc	*sc;
25566131Swpaul	int			val;
25666131Swpaul
25766131Swpaul	sc = device_get_softc(dev);
25866131Swpaul
25966208Swpaul	if (sc->pcn_phyaddr && phy > sc->pcn_phyaddr)
26066131Swpaul		return(0);
26166131Swpaul
26266131Swpaul	pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5));
26366131Swpaul	val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF;
26466131Swpaul	if (val == 0xFFFF)
26566131Swpaul		return(0);
26666208Swpaul
26766208Swpaul	sc->pcn_phyaddr = phy;
26866208Swpaul
26966131Swpaul	return(val);
27066131Swpaul}
27166131Swpaul
27266131Swpaulstatic int pcn_miibus_writereg(dev, phy, reg, data)
27366131Swpaul	device_t		dev;
27466131Swpaul	int			phy, reg, data;
27566131Swpaul{
27666131Swpaul	struct pcn_softc	*sc;
27766131Swpaul
27866131Swpaul	sc = device_get_softc(dev);
27966131Swpaul
28066131Swpaul	pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5));
28166131Swpaul	pcn_bcr_write(sc, PCN_BCR_MIIDATA, data);
28266131Swpaul
28366131Swpaul	return(0);
28466131Swpaul}
28566131Swpaul
28666131Swpaulstatic void pcn_miibus_statchg(dev)
28766131Swpaul	device_t		dev;
28866131Swpaul{
28966131Swpaul	struct pcn_softc	*sc;
29066131Swpaul	struct mii_data		*mii;
29166131Swpaul
29266131Swpaul	sc = device_get_softc(dev);
29366131Swpaul	mii = device_get_softc(sc->pcn_miibus);
29466131Swpaul
29566131Swpaul	if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
29666131Swpaul		PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN);
29766131Swpaul	} else {
29866131Swpaul		PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN);
29966131Swpaul	}
30066131Swpaul
30166131Swpaul	return;
30266131Swpaul}
30366131Swpaul
30466131Swpaul#define DC_POLY		0xEDB88320
30566131Swpaul
30666131Swpaulstatic u_int32_t pcn_crc(addr)
30766131Swpaul	caddr_t			addr;
30866131Swpaul{
30966131Swpaul	u_int32_t		idx, bit, data, crc;
31066131Swpaul
31166131Swpaul	/* Compute CRC for the address value. */
31266131Swpaul	crc = 0xFFFFFFFF; /* initial value */
31366131Swpaul
31466131Swpaul	for (idx = 0; idx < 6; idx++) {
31566131Swpaul		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1)
31666131Swpaul			crc = (crc >> 1) ^ (((crc ^ data) & 1) ? DC_POLY : 0);
31766131Swpaul	}
31866131Swpaul
31966131Swpaul	return ((crc >> 26) & 0x3F);
32066131Swpaul}
32166131Swpaul
32266131Swpaulstatic void pcn_setmulti(sc)
32366131Swpaul	struct pcn_softc	*sc;
32466131Swpaul{
32566131Swpaul	struct ifnet		*ifp;
32666131Swpaul	struct ifmultiaddr	*ifma;
32766131Swpaul	u_int32_t		h, i;
32866131Swpaul	u_int16_t		hashes[4] = { 0, 0, 0, 0 };
32966131Swpaul
33066131Swpaul	ifp = &sc->arpcom.ac_if;
33166131Swpaul
33266131Swpaul	PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
33366131Swpaul
33466131Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
33566131Swpaul		for (i = 0; i < 4; i++)
33666131Swpaul			pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF);
33766131Swpaul		PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
33866131Swpaul		return;
33966131Swpaul	}
34066131Swpaul
34166131Swpaul	/* first, zot all the existing hash bits */
34266131Swpaul	for (i = 0; i < 4; i++)
34366131Swpaul		pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0);
34466131Swpaul
34566131Swpaul	/* now program new ones */
34672084Sphk	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
34766131Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
34866131Swpaul			continue;
34966131Swpaul		h = pcn_crc(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
35066131Swpaul		hashes[h >> 4] |= 1 << (h & 0xF);
35166131Swpaul	}
35266131Swpaul
35366131Swpaul	for (i = 0; i < 4; i++)
35466131Swpaul		pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]);
35566131Swpaul
35666131Swpaul	PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
35766131Swpaul
35866131Swpaul	return;
35966131Swpaul}
36066131Swpaul
36166131Swpaulstatic void pcn_reset(sc)
36266131Swpaul	struct pcn_softc	*sc;
36366131Swpaul{
36466131Swpaul	/*
36566131Swpaul	 * Issue a reset by reading from the RESET register.
36666131Swpaul	 * Note that we don't know if the chip is operating in
36766131Swpaul	 * 16-bit or 32-bit mode at this point, so we attempt
36866131Swpaul	 * to reset the chip both ways. If one fails, the other
36966131Swpaul	 * will succeed.
37066131Swpaul	 */
37166131Swpaul	CSR_READ_2(sc, PCN_IO16_RESET);
37266131Swpaul	CSR_READ_4(sc, PCN_IO32_RESET);
37366131Swpaul
37466131Swpaul	/* Wait a little while for the chip to get its brains in order. */
37566131Swpaul	DELAY(1000);
37666131Swpaul
37766131Swpaul	/* Select 32-bit (DWIO) mode */
37866131Swpaul	CSR_WRITE_4(sc, PCN_IO32_RDP, 0);
37966131Swpaul
38066131Swpaul	/* Select software style 3. */
38166131Swpaul	pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST);
38266131Swpaul
38366131Swpaul        return;
38466131Swpaul}
38566131Swpaul
38666131Swpaul/*
38766131Swpaul * Probe for an AMD chip. Check the PCI vendor and device
38866131Swpaul * IDs against our list and return a device name if we find a match.
38966131Swpaul */
39066131Swpaulstatic int pcn_probe(dev)
39166131Swpaul	device_t		dev;
39266131Swpaul{
39366131Swpaul	struct pcn_type		*t;
39466131Swpaul	struct pcn_softc	*sc;
39566131Swpaul	int			rid;
39666131Swpaul	u_int32_t		chip_id;
39766131Swpaul
39866131Swpaul	t = pcn_devs;
39966131Swpaul	sc = device_get_softc(dev);
40066131Swpaul
40166131Swpaul	while(t->pcn_name != NULL) {
40266131Swpaul		if ((pci_get_vendor(dev) == t->pcn_vid) &&
40366131Swpaul		    (pci_get_device(dev) == t->pcn_did)) {
40466131Swpaul			/*
40566131Swpaul			 * Temporarily map the I/O space
40666131Swpaul			 * so we can read the chip ID register.
40766131Swpaul			 */
40866131Swpaul			rid = PCN_RID;
40966131Swpaul			sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid,
41066131Swpaul			    0, ~0, 1, RF_ACTIVE);
41166131Swpaul			if (sc->pcn_res == NULL) {
41266131Swpaul				device_printf(dev,
41366131Swpaul				    "couldn't map ports/memory\n");
41466131Swpaul				return(ENXIO);
41566131Swpaul			}
41666131Swpaul			sc->pcn_btag = rman_get_bustag(sc->pcn_res);
41766131Swpaul			sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res);
41867089Swpaul			mtx_init(&sc->pcn_mtx,
41967089Swpaul			    device_get_nameunit(dev), MTX_DEF);
42067087Swpaul			PCN_LOCK(sc);
42168837Swpaul			/*
42268837Swpaul			 * Note: we can *NOT* put the chip into
42368837Swpaul			 * 32-bit mode yet. The lnc driver will only
42468837Swpaul			 * work in 16-bit mode, and once the chip
42568837Swpaul			 * goes into 32-bit mode, the only way to
42668837Swpaul			 * get it out again is with a hardware reset.
42768837Swpaul			 * So if pcn_probe() is called before the
42868837Swpaul			 * lnc driver's probe routine, the chip will
42968837Swpaul			 * be locked into 32-bit operation and the lnc
43068837Swpaul			 * driver will be unable to attach to it.
43169067Swpaul			 * Note II: if the chip happens to already
43269067Swpaul			 * be in 32-bit mode, we still need to check
43369067Swpaul			 * the chip ID, but first we have to detect
43469067Swpaul			 * 32-bit mode using only 16-bit operations.
43569067Swpaul			 * The safest way to do this is to read the
43669067Swpaul			 * PCI subsystem ID from BCR23/24 and compare
43769067Swpaul			 * that with the value read from PCI config
43869067Swpaul			 * space.
43968837Swpaul			 */
44069067Swpaul			chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID);
44166131Swpaul			chip_id <<= 16;
44269067Swpaul			chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID);
44369067Swpaul			if (chip_id == pci_read_config(dev,
44469067Swpaul			    PCIR_SUBVEND_0, 4)) {
44569067Swpaul				/* We're in 16-bit mode. */
44669067Swpaul				chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1);
44769067Swpaul				chip_id <<= 16;
44869067Swpaul				chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0);
44969067Swpaul			} else {
45069067Swpaul				/* We're in 32-bit mode. */
45169067Swpaul				chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1);
45269067Swpaul				chip_id <<= 16;
45369067Swpaul				chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0);
45469067Swpaul			}
45566131Swpaul			bus_release_resource(dev, PCN_RES,
45666131Swpaul			    PCN_RID, sc->pcn_res);
45767087Swpaul			PCN_UNLOCK(sc);
45867087Swpaul			mtx_destroy(&sc->pcn_mtx);
45966131Swpaul			chip_id >>= 12;
46066131Swpaul			sc->pcn_type = chip_id & PART_MASK;
46166131Swpaul			switch(sc->pcn_type) {
46266131Swpaul			case Am79C971:
46366131Swpaul			case Am79C972:
46466131Swpaul			case Am79C973:
46566690Swpaul			case Am79C975:
46666592Swpaul			case Am79C976:
46766131Swpaul			case Am79C978:
46866131Swpaul				break;
46966131Swpaul			default:
47066131Swpaul				return(ENXIO);
47166131Swpaul				break;
47266131Swpaul			}
47366131Swpaul			device_set_desc(dev, t->pcn_name);
47466131Swpaul			return(0);
47566131Swpaul		}
47666131Swpaul		t++;
47766131Swpaul	}
47866131Swpaul
47966131Swpaul	return(ENXIO);
48066131Swpaul}
48166131Swpaul
48266131Swpaul/*
48366131Swpaul * Attach the interface. Allocate softc structures, do ifmedia
48466131Swpaul * setup and ethernet/BPF attach.
48566131Swpaul */
48666131Swpaulstatic int pcn_attach(dev)
48766131Swpaul	device_t		dev;
48866131Swpaul{
48966131Swpaul	u_int32_t		eaddr[2];
49066131Swpaul	u_int32_t		command;
49166131Swpaul	struct pcn_softc	*sc;
49266131Swpaul	struct ifnet		*ifp;
49366131Swpaul	int			unit, error = 0, rid;
49466131Swpaul
49566131Swpaul	sc = device_get_softc(dev);
49666131Swpaul	unit = device_get_unit(dev);
49766131Swpaul
49869583Swpaul	/* Initialize our mutex. */
49971228Sbmilekic	mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE);
50069583Swpaul	PCN_LOCK(sc);
50169583Swpaul
50266131Swpaul	/*
50366131Swpaul	 * Handle power management nonsense.
50466131Swpaul	 */
50566131Swpaul
50666131Swpaul	command = pci_read_config(dev, PCN_PCI_CAPID, 4) & 0x000000FF;
50766131Swpaul	if (command == 0x01) {
50866131Swpaul
50966131Swpaul		command = pci_read_config(dev, PCN_PCI_PWRMGMTCTRL, 4);
51066131Swpaul		if (command & PCN_PSTATE_MASK) {
51166131Swpaul			u_int32_t		iobase, membase, irq;
51266131Swpaul
51366131Swpaul			/* Save important PCI config data. */
51466131Swpaul			iobase = pci_read_config(dev, PCN_PCI_LOIO, 4);
51566131Swpaul			membase = pci_read_config(dev, PCN_PCI_LOMEM, 4);
51666131Swpaul			irq = pci_read_config(dev, PCN_PCI_INTLINE, 4);
51766131Swpaul
51866131Swpaul			/* Reset the power state. */
51966131Swpaul			printf("pcn%d: chip is in D%d power mode "
52066131Swpaul			"-- setting to D0\n", unit, command & PCN_PSTATE_MASK);
52166131Swpaul			command &= 0xFFFFFFFC;
52266131Swpaul			pci_write_config(dev, PCN_PCI_PWRMGMTCTRL, command, 4);
52366131Swpaul
52466131Swpaul			/* Restore PCI config data. */
52566131Swpaul			pci_write_config(dev, PCN_PCI_LOIO, iobase, 4);
52666131Swpaul			pci_write_config(dev, PCN_PCI_LOMEM, membase, 4);
52766131Swpaul			pci_write_config(dev, PCN_PCI_INTLINE, irq, 4);
52866131Swpaul		}
52966131Swpaul	}
53066131Swpaul
53166131Swpaul	/*
53266131Swpaul	 * Map control/status registers.
53366131Swpaul	 */
53466131Swpaul	command = pci_read_config(dev, PCIR_COMMAND, 4);
53566131Swpaul	command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
53666131Swpaul	pci_write_config(dev, PCIR_COMMAND, command, 4);
53766131Swpaul	command = pci_read_config(dev, PCIR_COMMAND, 4);
53866131Swpaul
53966131Swpaul#ifdef PCN_USEIOSPACE
54066131Swpaul	if (!(command & PCIM_CMD_PORTEN)) {
54166131Swpaul		printf("pcn%d: failed to enable I/O ports!\n", unit);
54266131Swpaul		error = ENXIO;;
54366131Swpaul		goto fail;
54466131Swpaul	}
54566131Swpaul#else
54666131Swpaul	if (!(command & PCIM_CMD_MEMEN)) {
54766131Swpaul		printf("pcn%d: failed to enable memory mapping!\n", unit);
54866131Swpaul		error = ENXIO;;
54966131Swpaul		goto fail;
55066131Swpaul	}
55166131Swpaul#endif
55266131Swpaul
55366131Swpaul	rid = PCN_RID;
55466131Swpaul	sc->pcn_res = bus_alloc_resource(dev, PCN_RES, &rid,
55566131Swpaul	    0, ~0, 1, RF_ACTIVE);
55666131Swpaul
55766131Swpaul	if (sc->pcn_res == NULL) {
55866131Swpaul		printf("pcn%d: couldn't map ports/memory\n", unit);
55966131Swpaul		error = ENXIO;
56066131Swpaul		goto fail;
56166131Swpaul	}
56266131Swpaul
56366131Swpaul	sc->pcn_btag = rman_get_bustag(sc->pcn_res);
56466131Swpaul	sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res);
56566131Swpaul
56666131Swpaul	/* Allocate interrupt */
56766131Swpaul	rid = 0;
56866131Swpaul	sc->pcn_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
56966131Swpaul	    RF_SHAREABLE | RF_ACTIVE);
57066131Swpaul
57166131Swpaul	if (sc->pcn_irq == NULL) {
57266131Swpaul		printf("pcn%d: couldn't map interrupt\n", unit);
57366131Swpaul		bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res);
57466131Swpaul		error = ENXIO;
57566131Swpaul		goto fail;
57666131Swpaul	}
57766131Swpaul
57866131Swpaul	error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET,
57966131Swpaul	    pcn_intr, sc, &sc->pcn_intrhand);
58066131Swpaul
58166131Swpaul	if (error) {
58266131Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_res);
58366131Swpaul		bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res);
58466131Swpaul		printf("pcn%d: couldn't set up irq\n", unit);
58566131Swpaul		goto fail;
58666131Swpaul	}
58766131Swpaul
58866131Swpaul	/* Reset the adapter. */
58966131Swpaul	pcn_reset(sc);
59066131Swpaul
59166131Swpaul	/*
59266131Swpaul	 * Get station address from the EEPROM.
59366131Swpaul	 */
59466131Swpaul	eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00);
59566131Swpaul	eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01);
59666131Swpaul	bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
59766131Swpaul
59866131Swpaul	/*
59966131Swpaul	 * An AMD chip was detected. Inform the world.
60066131Swpaul	 */
60166131Swpaul	printf("pcn%d: Ethernet address: %6D\n", unit,
60266131Swpaul	    sc->arpcom.ac_enaddr, ":");
60366131Swpaul
60466131Swpaul	sc->pcn_unit = unit;
60566131Swpaul	callout_handle_init(&sc->pcn_stat_ch);
60666131Swpaul
60766131Swpaul	sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF,
60866131Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
60966131Swpaul
61066131Swpaul	if (sc->pcn_ldata == NULL) {
61166131Swpaul		printf("pcn%d: no memory for list buffers!\n", unit);
61266131Swpaul		bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand);
61366131Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq);
61466131Swpaul		bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res);
61566131Swpaul		error = ENXIO;
61666131Swpaul		goto fail;
61766131Swpaul	}
61866131Swpaul	bzero(sc->pcn_ldata, sizeof(struct pcn_list_data));
61966131Swpaul
62066131Swpaul	ifp = &sc->arpcom.ac_if;
62166131Swpaul	ifp->if_softc = sc;
62266131Swpaul	ifp->if_unit = unit;
62366131Swpaul	ifp->if_name = "pcn";
62466131Swpaul	ifp->if_mtu = ETHERMTU;
62566131Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
62666131Swpaul	ifp->if_ioctl = pcn_ioctl;
62766131Swpaul	ifp->if_output = ether_output;
62866131Swpaul	ifp->if_start = pcn_start;
62966131Swpaul	ifp->if_watchdog = pcn_watchdog;
63066131Swpaul	ifp->if_init = pcn_init;
63166131Swpaul	ifp->if_baudrate = 10000000;
63266131Swpaul	ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1;
63366131Swpaul
63466131Swpaul	/*
63566131Swpaul	 * Do MII setup.
63666131Swpaul	 */
63766131Swpaul	if (mii_phy_probe(dev, &sc->pcn_miibus,
63866131Swpaul	    pcn_ifmedia_upd, pcn_ifmedia_sts)) {
63966131Swpaul		printf("pcn%d: MII without any PHY!\n", sc->pcn_unit);
64066131Swpaul		bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand);
64166131Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq);
64266131Swpaul		bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res);
64366131Swpaul		error = ENXIO;
64466131Swpaul		goto fail;
64566131Swpaul	}
64666131Swpaul
64766131Swpaul	/*
64866131Swpaul	 * Call MI attach routine.
64966131Swpaul	 */
65066131Swpaul	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
65166131Swpaul	callout_handle_init(&sc->pcn_stat_ch);
65267087Swpaul	PCN_UNLOCK(sc);
65367087Swpaul	return(0);
65466131Swpaul
65566131Swpaulfail:
65667087Swpaul	PCN_UNLOCK(sc);
65767087Swpaul	mtx_destroy(&sc->pcn_mtx);
65867087Swpaul
65966131Swpaul	return(error);
66066131Swpaul}
66166131Swpaul
66266131Swpaulstatic int pcn_detach(dev)
66366131Swpaul	device_t		dev;
66466131Swpaul{
66566131Swpaul	struct pcn_softc	*sc;
66666131Swpaul	struct ifnet		*ifp;
66766131Swpaul
66866131Swpaul	sc = device_get_softc(dev);
66966131Swpaul	ifp = &sc->arpcom.ac_if;
67066131Swpaul
67167087Swpaul	PCN_LOCK(sc);
67267087Swpaul
67366131Swpaul	pcn_reset(sc);
67466131Swpaul	pcn_stop(sc);
67566131Swpaul	ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
67666131Swpaul
67766131Swpaul	if (sc->pcn_miibus != NULL) {
67866131Swpaul		bus_generic_detach(dev);
67966131Swpaul		device_delete_child(dev, sc->pcn_miibus);
68066131Swpaul	}
68166131Swpaul
68266131Swpaul	bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand);
68366131Swpaul	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq);
68466131Swpaul	bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res);
68566131Swpaul
68666131Swpaul	contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF);
68767087Swpaul	PCN_UNLOCK(sc);
68866131Swpaul
68967087Swpaul	mtx_destroy(&sc->pcn_mtx);
69066131Swpaul
69166131Swpaul	return(0);
69266131Swpaul}
69366131Swpaul
69466131Swpaul/*
69566131Swpaul * Initialize the transmit descriptors.
69666131Swpaul */
69766131Swpaulstatic int pcn_list_tx_init(sc)
69866131Swpaul	struct pcn_softc	*sc;
69966131Swpaul{
70066131Swpaul	struct pcn_list_data	*ld;
70166131Swpaul	struct pcn_ring_data	*cd;
70266131Swpaul	int			i;
70366131Swpaul
70466131Swpaul	cd = &sc->pcn_cdata;
70566131Swpaul	ld = sc->pcn_ldata;
70666131Swpaul
70766131Swpaul	for (i = 0; i < PCN_TX_LIST_CNT; i++) {
70866131Swpaul		cd->pcn_tx_chain[i] = NULL;
70966131Swpaul		ld->pcn_tx_list[i].pcn_tbaddr = 0;
71066131Swpaul		ld->pcn_tx_list[i].pcn_txctl = 0;
71166131Swpaul		ld->pcn_tx_list[i].pcn_txstat = 0;
71266131Swpaul	}
71366131Swpaul
71466131Swpaul	cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0;
71566131Swpaul
71666131Swpaul	return(0);
71766131Swpaul}
71866131Swpaul
71966131Swpaul
72066131Swpaul/*
72166131Swpaul * Initialize the RX descriptors and allocate mbufs for them.
72266131Swpaul */
72366131Swpaulstatic int pcn_list_rx_init(sc)
72466131Swpaul	struct pcn_softc	*sc;
72566131Swpaul{
72666131Swpaul	struct pcn_list_data	*ld;
72766131Swpaul	struct pcn_ring_data	*cd;
72866131Swpaul	int			i;
72966131Swpaul
73066131Swpaul	ld = sc->pcn_ldata;
73166131Swpaul	cd = &sc->pcn_cdata;
73266131Swpaul
73366131Swpaul	for (i = 0; i < PCN_RX_LIST_CNT; i++) {
73466131Swpaul		if (pcn_newbuf(sc, i, NULL) == ENOBUFS)
73566131Swpaul			return(ENOBUFS);
73666131Swpaul	}
73766131Swpaul
73866131Swpaul	cd->pcn_rx_prod = 0;
73966131Swpaul
74066131Swpaul	return(0);
74166131Swpaul}
74266131Swpaul
74366131Swpaul/*
74466131Swpaul * Initialize an RX descriptor and attach an MBUF cluster.
74566131Swpaul */
74666131Swpaulstatic int pcn_newbuf(sc, idx, m)
74766131Swpaul	struct pcn_softc	*sc;
74866131Swpaul	int			idx;
74966131Swpaul	struct mbuf		*m;
75066131Swpaul{
75166131Swpaul	struct mbuf		*m_new = NULL;
75266131Swpaul	struct pcn_rx_desc	*c;
75366131Swpaul
75466131Swpaul	c = &sc->pcn_ldata->pcn_rx_list[idx];
75566131Swpaul
75666131Swpaul	if (m == NULL) {
75766131Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
75866131Swpaul		if (m_new == NULL) {
75966131Swpaul			printf("pcn%d: no memory for rx list "
76066131Swpaul			    "-- packet dropped!\n", sc->pcn_unit);
76166131Swpaul			return(ENOBUFS);
76266131Swpaul		}
76366131Swpaul
76466131Swpaul		MCLGET(m_new, M_DONTWAIT);
76566131Swpaul		if (!(m_new->m_flags & M_EXT)) {
76666131Swpaul			printf("pcn%d: no memory for rx list "
76766131Swpaul			    "-- packet dropped!\n", sc->pcn_unit);
76866131Swpaul			m_freem(m_new);
76966131Swpaul			return(ENOBUFS);
77066131Swpaul		}
77166131Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
77266131Swpaul	} else {
77366131Swpaul		m_new = m;
77466131Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
77566131Swpaul		m_new->m_data = m_new->m_ext.ext_buf;
77666131Swpaul	}
77766131Swpaul
77866131Swpaul	m_adj(m_new, ETHER_ALIGN);
77966131Swpaul
78066131Swpaul	sc->pcn_cdata.pcn_rx_chain[idx] = m_new;
78166131Swpaul	c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t));
78266131Swpaul	c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ;
78366131Swpaul	c->pcn_bufsz |= PCN_RXLEN_MBO;
78466131Swpaul	c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN;
78566131Swpaul
78666131Swpaul	return(0);
78766131Swpaul}
78866131Swpaul
78966131Swpaul/*
79066131Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to
79166131Swpaul * the higher level protocols.
79266131Swpaul */
79366131Swpaulstatic void pcn_rxeof(sc)
79466131Swpaul	struct pcn_softc	*sc;
79566131Swpaul{
79666131Swpaul        struct ether_header	*eh;
79766131Swpaul        struct mbuf		*m;
79866131Swpaul        struct ifnet		*ifp;
79966131Swpaul	struct pcn_rx_desc	*cur_rx;
80066131Swpaul	int			i;
80166131Swpaul
80266131Swpaul	ifp = &sc->arpcom.ac_if;
80366131Swpaul	i = sc->pcn_cdata.pcn_rx_prod;
80466131Swpaul
80566131Swpaul	while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) {
80666131Swpaul		cur_rx = &sc->pcn_ldata->pcn_rx_list[i];
80766131Swpaul		m = sc->pcn_cdata.pcn_rx_chain[i];
80866131Swpaul		sc->pcn_cdata.pcn_rx_chain[i] = NULL;
80966131Swpaul
81066131Swpaul		/*
81166131Swpaul		 * If an error occurs, update stats, clear the
81266131Swpaul		 * status word and leave the mbuf cluster in place:
81366131Swpaul		 * it should simply get re-used next time this descriptor
81466131Swpaul	 	 * comes up in the ring.
81566131Swpaul		 */
81666131Swpaul		if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) {
81766131Swpaul			ifp->if_ierrors++;
81866131Swpaul			pcn_newbuf(sc, i, m);
81966131Swpaul			PCN_INC(i, PCN_RX_LIST_CNT);
82066131Swpaul			continue;
82166131Swpaul		}
82266131Swpaul
82366592Swpaul		if (pcn_newbuf(sc, i, NULL)) {
82466592Swpaul			/* Ran out of mbufs; recycle this one. */
82566592Swpaul			pcn_newbuf(sc, i, m);
82666592Swpaul			ifp->if_ierrors++;
82766592Swpaul			PCN_INC(i, PCN_RX_LIST_CNT);
82866592Swpaul			continue;
82966592Swpaul		}
83066592Swpaul
83166131Swpaul		PCN_INC(i, PCN_RX_LIST_CNT);
83266131Swpaul
83366131Swpaul		/* No errors; receive the packet. */
83466131Swpaul		ifp->if_ipackets++;
83566131Swpaul		eh = mtod(m, struct ether_header *);
83666131Swpaul		m->m_len = m->m_pkthdr.len =
83766131Swpaul		    cur_rx->pcn_rxlen - ETHER_CRC_LEN;
83866131Swpaul		m->m_pkthdr.rcvif = ifp;
83966131Swpaul
84066131Swpaul		/* Remove header from mbuf and pass it on. */
84166131Swpaul		m_adj(m, sizeof(struct ether_header));
84266131Swpaul		ether_input(ifp, eh, m);
84366131Swpaul	}
84466131Swpaul
84566131Swpaul	sc->pcn_cdata.pcn_rx_prod = i;
84666131Swpaul
84766131Swpaul	return;
84866131Swpaul}
84966131Swpaul
85066131Swpaul/*
85166131Swpaul * A frame was downloaded to the chip. It's safe for us to clean up
85266131Swpaul * the list buffers.
85366131Swpaul */
85466131Swpaul
85566131Swpaulstatic void pcn_txeof(sc)
85666131Swpaul	struct pcn_softc	*sc;
85766131Swpaul{
85866131Swpaul	struct pcn_tx_desc	*cur_tx = NULL;
85966131Swpaul	struct ifnet		*ifp;
86066131Swpaul	u_int32_t		idx;
86166131Swpaul
86266131Swpaul	ifp = &sc->arpcom.ac_if;
86366131Swpaul
86466131Swpaul	/* Clear the timeout timer. */
86566131Swpaul	ifp->if_timer = 0;
86666131Swpaul
86766131Swpaul	/*
86866131Swpaul	 * Go through our tx list and free mbufs for those
86966131Swpaul	 * frames that have been transmitted.
87066131Swpaul	 */
87166131Swpaul	idx = sc->pcn_cdata.pcn_tx_cons;
87266131Swpaul	while (idx != sc->pcn_cdata.pcn_tx_prod) {
87366131Swpaul		cur_tx = &sc->pcn_ldata->pcn_tx_list[idx];
87466131Swpaul
87566131Swpaul		if (!PCN_OWN_TXDESC(cur_tx))
87666131Swpaul			break;
87766131Swpaul
87866131Swpaul		if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) {
87966131Swpaul			sc->pcn_cdata.pcn_tx_cnt--;
88066131Swpaul			PCN_INC(idx, PCN_TX_LIST_CNT);
88166131Swpaul			continue;
88266131Swpaul		}
88366131Swpaul
88466131Swpaul		if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) {
88566131Swpaul			ifp->if_oerrors++;
88666131Swpaul			if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF)
88766131Swpaul				ifp->if_collisions++;
88866131Swpaul			if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY)
88966131Swpaul				ifp->if_collisions++;
89066131Swpaul		}
89166131Swpaul
89266131Swpaul		ifp->if_collisions +=
89366131Swpaul		    cur_tx->pcn_txstat & PCN_TXSTAT_TRC;
89466131Swpaul
89566131Swpaul		ifp->if_opackets++;
89666131Swpaul		if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) {
89766131Swpaul			m_freem(sc->pcn_cdata.pcn_tx_chain[idx]);
89866131Swpaul			sc->pcn_cdata.pcn_tx_chain[idx] = NULL;
89966131Swpaul		}
90066131Swpaul
90166131Swpaul		sc->pcn_cdata.pcn_tx_cnt--;
90266131Swpaul		PCN_INC(idx, PCN_TX_LIST_CNT);
90366131Swpaul		ifp->if_timer = 0;
90466131Swpaul	}
90566131Swpaul
90666131Swpaul	sc->pcn_cdata.pcn_tx_cons = idx;
90766131Swpaul
90866131Swpaul	if (cur_tx != NULL)
90966131Swpaul		ifp->if_flags &= ~IFF_OACTIVE;
91066131Swpaul
91166131Swpaul	return;
91266131Swpaul}
91366131Swpaul
91466131Swpaulstatic void pcn_tick(xsc)
91566131Swpaul	void			*xsc;
91666131Swpaul{
91766131Swpaul	struct pcn_softc	*sc;
91866131Swpaul	struct mii_data		*mii;
91966131Swpaul	struct ifnet		*ifp;
92066131Swpaul
92166131Swpaul	sc = xsc;
92266131Swpaul	ifp = &sc->arpcom.ac_if;
92367087Swpaul	PCN_LOCK(sc);
92466131Swpaul
92566131Swpaul	mii = device_get_softc(sc->pcn_miibus);
92666131Swpaul	mii_tick(mii);
92766131Swpaul
92866131Swpaul	if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE))
92966131Swpaul		sc->pcn_link = 0;
93066131Swpaul
93166131Swpaul	if (!sc->pcn_link) {
93266131Swpaul		mii_pollstat(mii);
93366131Swpaul		if (mii->mii_media_status & IFM_ACTIVE &&
93466131Swpaul		    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
93566131Swpaul			sc->pcn_link++;
93666131Swpaul			if (ifp->if_snd.ifq_head != NULL)
93766131Swpaul				pcn_start(ifp);
93866131Swpaul	}
93966131Swpaul
94066131Swpaul	sc->pcn_stat_ch = timeout(pcn_tick, sc, hz);
94166131Swpaul
94267087Swpaul	PCN_UNLOCK(sc);
94366131Swpaul
94466131Swpaul	return;
94566131Swpaul}
94666131Swpaul
94766131Swpaulstatic void pcn_intr(arg)
94866131Swpaul	void			*arg;
94966131Swpaul{
95066131Swpaul	struct pcn_softc	*sc;
95166131Swpaul	struct ifnet		*ifp;
95266131Swpaul	u_int32_t		status;
95366131Swpaul
95466131Swpaul	sc = arg;
95566131Swpaul	ifp = &sc->arpcom.ac_if;
95666131Swpaul
95766131Swpaul	/* Supress unwanted interrupts */
95866131Swpaul	if (!(ifp->if_flags & IFF_UP)) {
95966131Swpaul		pcn_stop(sc);
96066131Swpaul		return;
96166131Swpaul	}
96266131Swpaul
96366131Swpaul	CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR);
96466131Swpaul
96566131Swpaul	while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) {
96666131Swpaul		CSR_WRITE_4(sc, PCN_IO32_RDP, status);
96766131Swpaul
96866131Swpaul		if (status & PCN_CSR_RINT)
96966131Swpaul			pcn_rxeof(sc);
97066131Swpaul
97166131Swpaul		if (status & PCN_CSR_TINT)
97266131Swpaul			pcn_txeof(sc);
97366131Swpaul
97466131Swpaul		if (status & PCN_CSR_ERR) {
97566131Swpaul			pcn_init(sc);
97666131Swpaul			break;
97766131Swpaul		}
97866131Swpaul	}
97966131Swpaul
98066131Swpaul	if (ifp->if_snd.ifq_head != NULL)
98166131Swpaul		pcn_start(ifp);
98266131Swpaul
98366131Swpaul	return;
98466131Swpaul}
98566131Swpaul
98666131Swpaul/*
98766131Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
98866131Swpaul * pointers to the fragment pointers.
98966131Swpaul */
99066131Swpaulstatic int pcn_encap(sc, m_head, txidx)
99166131Swpaul	struct pcn_softc	*sc;
99266131Swpaul	struct mbuf		*m_head;
99366131Swpaul	u_int32_t		*txidx;
99466131Swpaul{
99566131Swpaul	struct pcn_tx_desc	*f = NULL;
99666131Swpaul	struct mbuf		*m;
99766131Swpaul	int			frag, cur, cnt = 0;
99866131Swpaul
99966131Swpaul	/*
100066131Swpaul 	 * Start packing the mbufs in this chain into
100166131Swpaul	 * the fragment pointers. Stop when we run out
100266131Swpaul 	 * of fragments or hit the end of the mbuf chain.
100366131Swpaul	 */
100466131Swpaul	m = m_head;
100566131Swpaul	cur = frag = *txidx;
100666131Swpaul
100766131Swpaul	for (m = m_head; m != NULL; m = m->m_next) {
100866131Swpaul		if (m->m_len != 0) {
100966131Swpaul			if ((PCN_TX_LIST_CNT -
101066131Swpaul			    (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2)
101166131Swpaul				return(ENOBUFS);
101266131Swpaul			f = &sc->pcn_ldata->pcn_tx_list[frag];
101366131Swpaul			f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ;
101466131Swpaul			f->pcn_txctl |= PCN_TXCTL_MBO;
101566131Swpaul			f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t));
101666131Swpaul			if (cnt == 0)
101766131Swpaul				f->pcn_txctl |= PCN_TXCTL_STP;
101866131Swpaul			else
101966131Swpaul				f->pcn_txctl |= PCN_TXCTL_OWN;
102066131Swpaul			cur = frag;
102166131Swpaul			PCN_INC(frag, PCN_TX_LIST_CNT);
102266131Swpaul			cnt++;
102366131Swpaul		}
102466131Swpaul	}
102566131Swpaul
102666131Swpaul	if (m != NULL)
102766131Swpaul		return(ENOBUFS);
102866131Swpaul
102966131Swpaul	sc->pcn_cdata.pcn_tx_chain[cur] = m_head;
103066131Swpaul	sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |=
103166131Swpaul	    PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT;
103266131Swpaul	sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN;
103366131Swpaul	sc->pcn_cdata.pcn_tx_cnt += cnt;
103466131Swpaul	*txidx = frag;
103566131Swpaul
103666131Swpaul	return(0);
103766131Swpaul}
103866131Swpaul
103966131Swpaul/*
104066131Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers
104166131Swpaul * to the mbuf data regions directly in the transmit lists. We also save a
104266131Swpaul * copy of the pointers since the transmit list fragment pointers are
104366131Swpaul * physical addresses.
104466131Swpaul */
104566131Swpaulstatic void pcn_start(ifp)
104666131Swpaul	struct ifnet		*ifp;
104766131Swpaul{
104866131Swpaul	struct pcn_softc	*sc;
104966131Swpaul	struct mbuf		*m_head = NULL;
105066131Swpaul	u_int32_t		idx;
105166131Swpaul
105266131Swpaul	sc = ifp->if_softc;
105366131Swpaul
105467087Swpaul	PCN_LOCK(sc);
105567087Swpaul
105667087Swpaul	if (!sc->pcn_link) {
105767087Swpaul		PCN_UNLOCK(sc);
105866131Swpaul		return;
105967087Swpaul	}
106066131Swpaul
106166131Swpaul	idx = sc->pcn_cdata.pcn_tx_prod;
106266131Swpaul
106367087Swpaul	if (ifp->if_flags & IFF_OACTIVE) {
106467087Swpaul		PCN_UNLOCK(sc);
106566131Swpaul		return;
106667087Swpaul	}
106766131Swpaul
106866131Swpaul	while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) {
106966131Swpaul		IF_DEQUEUE(&ifp->if_snd, m_head);
107066131Swpaul		if (m_head == NULL)
107166131Swpaul			break;
107266131Swpaul
107366131Swpaul		if (pcn_encap(sc, m_head, &idx)) {
107466131Swpaul			IF_PREPEND(&ifp->if_snd, m_head);
107566131Swpaul			ifp->if_flags |= IFF_OACTIVE;
107666131Swpaul			break;
107766131Swpaul		}
107866131Swpaul
107966131Swpaul		/*
108066131Swpaul		 * If there's a BPF listener, bounce a copy of this frame
108166131Swpaul		 * to him.
108266131Swpaul		 */
108366131Swpaul		if (ifp->if_bpf)
108466131Swpaul			bpf_mtap(ifp, m_head);
108566131Swpaul
108666131Swpaul	}
108766131Swpaul
108866131Swpaul	/* Transmit */
108966131Swpaul	sc->pcn_cdata.pcn_tx_prod = idx;
109066131Swpaul	pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN);
109166131Swpaul
109266131Swpaul	/*
109366131Swpaul	 * Set a timeout in case the chip goes out to lunch.
109466131Swpaul	 */
109566131Swpaul	ifp->if_timer = 5;
109666131Swpaul
109767087Swpaul	PCN_UNLOCK(sc);
109867087Swpaul
109966131Swpaul	return;
110066131Swpaul}
110166131Swpaul
110268270Swpaulstatic void pcn_setfilt(ifp)
110368270Swpaul	struct ifnet		*ifp;
110468270Swpaul{
110568270Swpaul	struct pcn_softc	*sc;
110668270Swpaul
110768270Swpaul	sc = ifp->if_softc;
110868270Swpaul
110968270Swpaul	 /* If we want promiscuous mode, set the allframes bit. */
111068270Swpaul	if (ifp->if_flags & IFF_PROMISC) {
111168270Swpaul		PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC);
111268270Swpaul	} else {
111368270Swpaul		PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC);
111468270Swpaul	}
111568270Swpaul
111668270Swpaul	/* Set the capture broadcast bit to capture broadcast frames. */
111768270Swpaul	if (ifp->if_flags & IFF_BROADCAST) {
111868270Swpaul		PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD);
111968270Swpaul	} else {
112068270Swpaul		PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD);
112168270Swpaul	}
112268270Swpaul
112368270Swpaul	return;
112468270Swpaul}
112568270Swpaul
112666131Swpaulstatic void pcn_init(xsc)
112766131Swpaul	void			*xsc;
112866131Swpaul{
112966131Swpaul	struct pcn_softc	*sc = xsc;
113066131Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
113166131Swpaul	struct mii_data		*mii = NULL;
113266131Swpaul
113367087Swpaul	PCN_LOCK(sc);
113466131Swpaul
113566131Swpaul	/*
113666131Swpaul	 * Cancel pending I/O and free all RX/TX buffers.
113766131Swpaul	 */
113866131Swpaul	pcn_stop(sc);
113966131Swpaul	pcn_reset(sc);
114066131Swpaul
114166131Swpaul	mii = device_get_softc(sc->pcn_miibus);
114266131Swpaul
114366131Swpaul	/* Set MAC address */
114466131Swpaul	pcn_csr_write(sc, PCN_CSR_PAR0,
114566131Swpaul	    ((u_int16_t *)sc->arpcom.ac_enaddr)[0]);
114666131Swpaul	pcn_csr_write(sc, PCN_CSR_PAR1,
114766131Swpaul	    ((u_int16_t *)sc->arpcom.ac_enaddr)[1]);
114866131Swpaul	pcn_csr_write(sc, PCN_CSR_PAR2,
114966131Swpaul	    ((u_int16_t *)sc->arpcom.ac_enaddr)[2]);
115066131Swpaul
115166131Swpaul	/* Init circular RX list. */
115266131Swpaul	if (pcn_list_rx_init(sc) == ENOBUFS) {
115366131Swpaul		printf("pcn%d: initialization failed: no "
115466131Swpaul		    "memory for rx buffers\n", sc->pcn_unit);
115566131Swpaul		pcn_stop(sc);
115667087Swpaul		PCN_UNLOCK(sc);
115766131Swpaul		return;
115866131Swpaul	}
115966131Swpaul
116066131Swpaul	/*
116166131Swpaul	 * Init tx descriptors.
116266131Swpaul	 */
116366131Swpaul	pcn_list_tx_init(sc);
116466131Swpaul
116566131Swpaul	/* Set up the mode register. */
116666131Swpaul	pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII);
116766131Swpaul
116868270Swpaul	/* Set up RX filter. */
116968270Swpaul	pcn_setfilt(ifp);
117066131Swpaul
117166131Swpaul	/*
117266131Swpaul	 * Load the multicast filter.
117366131Swpaul	 */
117466131Swpaul	pcn_setmulti(sc);
117566131Swpaul
117666131Swpaul	/*
117766131Swpaul	 * Load the addresses of the RX and TX lists.
117866131Swpaul	 */
117966131Swpaul	pcn_csr_write(sc, PCN_CSR_RXADDR0,
118066131Swpaul	    vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF);
118166131Swpaul	pcn_csr_write(sc, PCN_CSR_RXADDR1,
118266131Swpaul	    (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF);
118366131Swpaul	pcn_csr_write(sc, PCN_CSR_TXADDR0,
118466131Swpaul	    vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF);
118566131Swpaul	pcn_csr_write(sc, PCN_CSR_TXADDR1,
118666131Swpaul	    (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF);
118766131Swpaul
118866131Swpaul	/* Set the RX and TX ring sizes. */
118966131Swpaul	pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1);
119066131Swpaul	pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1);
119166131Swpaul
119266131Swpaul	/* We're not using the initialization block. */
119366131Swpaul	pcn_csr_write(sc, PCN_CSR_IAB1, 0);
119466131Swpaul
119566131Swpaul	/* Enable fast suspend mode. */
119666131Swpaul	PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE);
119766131Swpaul
119866131Swpaul	/*
119966131Swpaul	 * Enable burst read and write. Also set the no underflow
120066131Swpaul	 * bit. This will avoid transmit underruns in certain
120166210Swpaul	 * conditions while still providing decent performance.
120266131Swpaul	 */
120366131Swpaul	PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW|
120466131Swpaul	    PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE);
120566131Swpaul
120666131Swpaul	/* Enable graceful recovery from underflow. */
120766131Swpaul	PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO);
120866131Swpaul
120966131Swpaul	/* Enable auto-padding of short TX frames. */
121066131Swpaul	PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX);
121166131Swpaul
121266131Swpaul	/* Disable MII autoneg (we handle this ourselves). */
121366131Swpaul	PCN_BCR_CLRBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS);
121466131Swpaul
121566131Swpaul	if (sc->pcn_type == Am79C978)
121666131Swpaul		pcn_bcr_write(sc, PCN_BCR_PHYSEL,
121766131Swpaul		    PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA);
121866131Swpaul
121966131Swpaul	/* Enable interrupts and start the controller running. */
122066131Swpaul	pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START);
122166131Swpaul
122266131Swpaul	mii_mediachg(mii);
122366131Swpaul
122466131Swpaul	ifp->if_flags |= IFF_RUNNING;
122566131Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
122666131Swpaul
122766131Swpaul	sc->pcn_stat_ch = timeout(pcn_tick, sc, hz);
122867087Swpaul	PCN_UNLOCK(sc);
122966131Swpaul
123066131Swpaul	return;
123166131Swpaul}
123266131Swpaul
123366131Swpaul/*
123466131Swpaul * Set media options.
123566131Swpaul */
123666131Swpaulstatic int pcn_ifmedia_upd(ifp)
123766131Swpaul	struct ifnet		*ifp;
123866131Swpaul{
123966131Swpaul	struct pcn_softc	*sc;
124066131Swpaul	struct mii_data		*mii;
124166131Swpaul
124266131Swpaul	sc = ifp->if_softc;
124366131Swpaul	mii = device_get_softc(sc->pcn_miibus);
124466131Swpaul
124566131Swpaul	sc->pcn_link = 0;
124666131Swpaul	if (mii->mii_instance) {
124766131Swpaul		struct mii_softc        *miisc;
124872012Sphk		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
124966131Swpaul			mii_phy_reset(miisc);
125066131Swpaul	}
125166131Swpaul	mii_mediachg(mii);
125266131Swpaul
125366131Swpaul	return(0);
125466131Swpaul}
125566131Swpaul
125666131Swpaul/*
125766131Swpaul * Report current media status.
125866131Swpaul */
125966131Swpaulstatic void pcn_ifmedia_sts(ifp, ifmr)
126066131Swpaul	struct ifnet		*ifp;
126166131Swpaul	struct ifmediareq	*ifmr;
126266131Swpaul{
126366131Swpaul	struct pcn_softc	*sc;
126466131Swpaul	struct mii_data		*mii;
126566131Swpaul
126666131Swpaul	sc = ifp->if_softc;
126766131Swpaul
126866131Swpaul	mii = device_get_softc(sc->pcn_miibus);
126966131Swpaul	mii_pollstat(mii);
127066131Swpaul	ifmr->ifm_active = mii->mii_media_active;
127166131Swpaul	ifmr->ifm_status = mii->mii_media_status;
127266131Swpaul
127366131Swpaul	return;
127466131Swpaul}
127566131Swpaul
127666131Swpaulstatic int pcn_ioctl(ifp, command, data)
127766131Swpaul	struct ifnet		*ifp;
127866131Swpaul	u_long			command;
127966131Swpaul	caddr_t			data;
128066131Swpaul{
128166131Swpaul	struct pcn_softc	*sc = ifp->if_softc;
128266131Swpaul	struct ifreq		*ifr = (struct ifreq *) data;
128366131Swpaul	struct mii_data		*mii = NULL;
128467087Swpaul	int			error = 0;
128566131Swpaul
128667087Swpaul	PCN_LOCK(sc);
128766131Swpaul
128866131Swpaul	switch(command) {
128966131Swpaul	case SIOCSIFADDR:
129066131Swpaul	case SIOCGIFADDR:
129166131Swpaul	case SIOCSIFMTU:
129266131Swpaul		error = ether_ioctl(ifp, command, data);
129366131Swpaul		break;
129466131Swpaul	case SIOCSIFFLAGS:
129566131Swpaul		if (ifp->if_flags & IFF_UP) {
129666131Swpaul                        if (ifp->if_flags & IFF_RUNNING &&
129766131Swpaul			    ifp->if_flags & IFF_PROMISC &&
129866131Swpaul			    !(sc->pcn_if_flags & IFF_PROMISC)) {
129966131Swpaul				PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1,
130066131Swpaul				    PCN_EXTCTL1_SPND);
130168270Swpaul				pcn_setfilt(ifp);
130266131Swpaul				PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1,
130366131Swpaul				    PCN_EXTCTL1_SPND);
130466771Swpaul				pcn_csr_write(sc, PCN_CSR_CSR,
130566771Swpaul				    PCN_CSR_INTEN|PCN_CSR_START);
130666131Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
130766131Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
130866131Swpaul				sc->pcn_if_flags & IFF_PROMISC) {
130966131Swpaul				PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1,
131066131Swpaul				    PCN_EXTCTL1_SPND);
131168270Swpaul				pcn_setfilt(ifp);
131266131Swpaul				PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1,
131366131Swpaul				    PCN_EXTCTL1_SPND);
131466771Swpaul				pcn_csr_write(sc, PCN_CSR_CSR,
131566771Swpaul				    PCN_CSR_INTEN|PCN_CSR_START);
131666131Swpaul			} else if (!(ifp->if_flags & IFF_RUNNING))
131766131Swpaul				pcn_init(sc);
131866131Swpaul		} else {
131966131Swpaul			if (ifp->if_flags & IFF_RUNNING)
132066131Swpaul				pcn_stop(sc);
132166131Swpaul		}
132266131Swpaul		sc->pcn_if_flags = ifp->if_flags;
132366131Swpaul		error = 0;
132466131Swpaul		break;
132566131Swpaul	case SIOCADDMULTI:
132666131Swpaul	case SIOCDELMULTI:
132766131Swpaul		pcn_setmulti(sc);
132866131Swpaul		error = 0;
132966131Swpaul		break;
133066131Swpaul	case SIOCGIFMEDIA:
133166131Swpaul	case SIOCSIFMEDIA:
133266131Swpaul		mii = device_get_softc(sc->pcn_miibus);
133366131Swpaul		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
133466131Swpaul		break;
133566131Swpaul	default:
133666131Swpaul		error = EINVAL;
133766131Swpaul		break;
133866131Swpaul	}
133966131Swpaul
134067087Swpaul	PCN_UNLOCK(sc);
134166131Swpaul
134266131Swpaul	return(error);
134366131Swpaul}
134466131Swpaul
134566131Swpaulstatic void pcn_watchdog(ifp)
134666131Swpaul	struct ifnet		*ifp;
134766131Swpaul{
134866131Swpaul	struct pcn_softc	*sc;
134966131Swpaul
135066131Swpaul	sc = ifp->if_softc;
135166131Swpaul
135267087Swpaul	PCN_LOCK(sc);
135367087Swpaul
135466131Swpaul	ifp->if_oerrors++;
135566131Swpaul	printf("pcn%d: watchdog timeout\n", sc->pcn_unit);
135666131Swpaul
135766131Swpaul	pcn_stop(sc);
135866131Swpaul	pcn_reset(sc);
135966131Swpaul	pcn_init(sc);
136066131Swpaul
136166131Swpaul	if (ifp->if_snd.ifq_head != NULL)
136266131Swpaul		pcn_start(ifp);
136366131Swpaul
136467087Swpaul	PCN_UNLOCK(sc);
136567087Swpaul
136666131Swpaul	return;
136766131Swpaul}
136866131Swpaul
136966131Swpaul/*
137066131Swpaul * Stop the adapter and free any mbufs allocated to the
137166131Swpaul * RX and TX lists.
137266131Swpaul */
137366131Swpaulstatic void pcn_stop(sc)
137466131Swpaul	struct pcn_softc	*sc;
137566131Swpaul{
137666131Swpaul	register int		i;
137766131Swpaul	struct ifnet		*ifp;
137866131Swpaul
137966131Swpaul	ifp = &sc->arpcom.ac_if;
138067087Swpaul	PCN_LOCK(sc);
138166131Swpaul	ifp->if_timer = 0;
138266131Swpaul
138366131Swpaul	untimeout(pcn_tick, sc, sc->pcn_stat_ch);
138466131Swpaul	PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP);
138566131Swpaul	sc->pcn_link = 0;
138666131Swpaul
138766131Swpaul	/*
138866131Swpaul	 * Free data in the RX lists.
138966131Swpaul	 */
139066131Swpaul	for (i = 0; i < PCN_RX_LIST_CNT; i++) {
139166131Swpaul		if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) {
139266131Swpaul			m_freem(sc->pcn_cdata.pcn_rx_chain[i]);
139366131Swpaul			sc->pcn_cdata.pcn_rx_chain[i] = NULL;
139466131Swpaul		}
139566131Swpaul	}
139666131Swpaul	bzero((char *)&sc->pcn_ldata->pcn_rx_list,
139766131Swpaul		sizeof(sc->pcn_ldata->pcn_rx_list));
139866131Swpaul
139966131Swpaul	/*
140066131Swpaul	 * Free the TX list buffers.
140166131Swpaul	 */
140266131Swpaul	for (i = 0; i < PCN_TX_LIST_CNT; i++) {
140366131Swpaul		if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) {
140466131Swpaul			m_freem(sc->pcn_cdata.pcn_tx_chain[i]);
140566131Swpaul			sc->pcn_cdata.pcn_tx_chain[i] = NULL;
140666131Swpaul		}
140766131Swpaul	}
140866131Swpaul
140966131Swpaul	bzero((char *)&sc->pcn_ldata->pcn_tx_list,
141066131Swpaul		sizeof(sc->pcn_ldata->pcn_tx_list));
141166131Swpaul
141266131Swpaul	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
141367087Swpaul	PCN_UNLOCK(sc);
141466131Swpaul
141566131Swpaul	return;
141666131Swpaul}
141766131Swpaul
141866131Swpaul/*
141966131Swpaul * Stop all chip I/O so that the kernel's probe routines don't
142066131Swpaul * get confused by errant DMAs when rebooting.
142166131Swpaul */
142266131Swpaulstatic void pcn_shutdown(dev)
142366131Swpaul	device_t		dev;
142466131Swpaul{
142566131Swpaul	struct pcn_softc	*sc;
142666131Swpaul
142766131Swpaul	sc = device_get_softc(dev);
142866131Swpaul
142967087Swpaul	PCN_LOCK(sc);
143066131Swpaul	pcn_reset(sc);
143166131Swpaul	pcn_stop(sc);
143267087Swpaul	PCN_UNLOCK(sc);
143366131Swpaul
143466131Swpaul	return;
143566131Swpaul}
1436