xref: /freebsd/sys/dev/fxp/if_fxp.c (revision dccee1a1938f7b841e217ada226c04d1c1c1ed66)
1a17c678eSDavid Greenman /*
2a17c678eSDavid Greenman  * Copyright (c) 1995, David Greenman
3a17c678eSDavid Greenman  * All rights reserved.
4a17c678eSDavid Greenman  *
5a17c678eSDavid Greenman  * Redistribution and use in source and binary forms, with or without
6a17c678eSDavid Greenman  * modification, are permitted provided that the following conditions
7a17c678eSDavid Greenman  * are met:
8a17c678eSDavid Greenman  * 1. Redistributions of source code must retain the above copyright
9a17c678eSDavid Greenman  *    notice unmodified, this list of conditions, and the following
10a17c678eSDavid Greenman  *    disclaimer.
11a17c678eSDavid Greenman  * 2. Redistributions in binary form must reproduce the above copyright
12a17c678eSDavid Greenman  *    notice, this list of conditions and the following disclaimer in the
13a17c678eSDavid Greenman  *    documentation and/or other materials provided with the distribution.
14a17c678eSDavid Greenman  *
15a17c678eSDavid Greenman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16a17c678eSDavid Greenman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17a17c678eSDavid Greenman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18a17c678eSDavid Greenman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19a17c678eSDavid Greenman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20a17c678eSDavid Greenman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21a17c678eSDavid Greenman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22a17c678eSDavid Greenman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23a17c678eSDavid Greenman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24a17c678eSDavid Greenman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25a17c678eSDavid Greenman  * SUCH DAMAGE.
26a17c678eSDavid Greenman  *
27dccee1a1SDavid Greenman  *	$Id: if_fxp.c,v 1.29 1997/02/22 09:44:05 peter Exp $
28a17c678eSDavid Greenman  */
29a17c678eSDavid Greenman 
30a17c678eSDavid Greenman /*
31ae12cddaSDavid Greenman  * Intel EtherExpress Pro/100B PCI Fast Ethernet driver
32a17c678eSDavid Greenman  */
33a17c678eSDavid Greenman 
34a17c678eSDavid Greenman #include "bpfilter.h"
35a17c678eSDavid Greenman 
36a17c678eSDavid Greenman #include <sys/param.h>
37a17c678eSDavid Greenman #include <sys/systm.h>
38a17c678eSDavid Greenman #include <sys/ioctl.h>
39a17c678eSDavid Greenman #include <sys/mbuf.h>
40a17c678eSDavid Greenman #include <sys/malloc.h>
41a17c678eSDavid Greenman #include <sys/kernel.h>
424458ac71SBruce Evans #include <sys/socket.h>
43a17c678eSDavid Greenman #include <sys/syslog.h>
44a17c678eSDavid Greenman 
45a17c678eSDavid Greenman #include <net/if.h>
46a17c678eSDavid Greenman #include <net/if_dl.h>
47a17c678eSDavid Greenman #include <net/if_types.h>
48a17c678eSDavid Greenman 
49a17c678eSDavid Greenman #ifdef INET
50a17c678eSDavid Greenman #include <netinet/in.h>
51a17c678eSDavid Greenman #include <netinet/in_systm.h>
52a17c678eSDavid Greenman #include <netinet/in_var.h>
53a17c678eSDavid Greenman #include <netinet/ip.h>
54a17c678eSDavid Greenman #include <netinet/if_ether.h>
55a17c678eSDavid Greenman #endif
56a17c678eSDavid Greenman 
57a17c678eSDavid Greenman #ifdef IPX
58a17c678eSDavid Greenman #include <netipx/ipx.h>
59a17c678eSDavid Greenman #include <netipx/ipx_if.h>
60a17c678eSDavid Greenman #endif
61a17c678eSDavid Greenman 
62a17c678eSDavid Greenman #ifdef NS
63a17c678eSDavid Greenman #include <netns/ns.h>
64a17c678eSDavid Greenman #include <netns/ns_if.h>
65a17c678eSDavid Greenman #endif
66a17c678eSDavid Greenman 
67a17c678eSDavid Greenman #if NBPFILTER > 0
68a17c678eSDavid Greenman #include <net/bpf.h>
69a17c678eSDavid Greenman #include <net/bpfdesc.h>
70a17c678eSDavid Greenman #endif
71a17c678eSDavid Greenman 
72dfe61cf1SDavid Greenman #include <vm/vm.h>		/* for vtophys */
73dfe61cf1SDavid Greenman #include <vm/vm_param.h>	/* for vtophys */
74efeaf95aSDavid Greenman #include <vm/pmap.h>		/* for vtophys */
75dfe61cf1SDavid Greenman #include <machine/clock.h>	/* for DELAY */
76a17c678eSDavid Greenman 
77a17c678eSDavid Greenman #include <pci/pcivar.h>
78a17c678eSDavid Greenman #include <pci/if_fxpreg.h>
79a17c678eSDavid Greenman 
80a17c678eSDavid Greenman struct fxp_softc {
81dfe61cf1SDavid Greenman 	struct arpcom arpcom;		/* per-interface network data */
82dfe61cf1SDavid Greenman 	struct fxp_csr *csr;		/* control/status registers */
83a17c678eSDavid Greenman 	struct fxp_cb_tx *cbl_base;	/* base of TxCB list */
84a17c678eSDavid Greenman 	struct fxp_cb_tx *cbl_first;	/* first active TxCB in list */
85a17c678eSDavid Greenman 	struct fxp_cb_tx *cbl_last;	/* last active TxCB in list */
86a17c678eSDavid Greenman 	struct mbuf *rfa_headm;		/* first mbuf in receive frame area */
87a17c678eSDavid Greenman 	struct mbuf *rfa_tailm;		/* last mbuf in receive frame area */
88a17c678eSDavid Greenman 	struct fxp_stats *fxp_stats;	/* Pointer to interface stats */
89a17c678eSDavid Greenman 	int tx_queued;			/* # of active TxCB's */
90a17c678eSDavid Greenman 	int promisc_mode;		/* promiscuous mode enabled */
91dccee1a1SDavid Greenman 	int phy_primary_addr;		/* address of primary PHY */
92dccee1a1SDavid Greenman 	int phy_primary_device;		/* device type of primary PHY */
93dccee1a1SDavid Greenman 	int phy_10Mbps_only;		/* PHY is 10Mbps-only device */
94a17c678eSDavid Greenman };
95a17c678eSDavid Greenman 
96a17c678eSDavid Greenman static u_long fxp_count;
97a17c678eSDavid Greenman 
98a17c678eSDavid Greenman /*
99a17c678eSDavid Greenman  * Template for default configuration parameters.
100a17c678eSDavid Greenman  * See struct fxp_cb_config for the bit definitions.
101a17c678eSDavid Greenman  */
102a17c678eSDavid Greenman static u_char fxp_cb_config_template[] = {
103a17c678eSDavid Greenman 	0x0, 0x0,		/* cb_status */
104a17c678eSDavid Greenman 	0x80, 0x2,		/* cb_command */
105a17c678eSDavid Greenman 	0xff, 0xff, 0xff, 0xff,	/* link_addr */
106a17c678eSDavid Greenman 	0x16,	/*  0 */
107a17c678eSDavid Greenman 	0x8,	/*  1 */
108a17c678eSDavid Greenman 	0x0,	/*  2 */
109a17c678eSDavid Greenman 	0x0,	/*  3 */
110a17c678eSDavid Greenman 	0x0,	/*  4 */
111a17c678eSDavid Greenman 	0x80,	/*  5 */
112a17c678eSDavid Greenman 	0xb2,	/*  6 */
113a17c678eSDavid Greenman 	0x3,	/*  7 */
114a17c678eSDavid Greenman 	0x1,	/*  8 */
115a17c678eSDavid Greenman 	0x0,	/*  9 */
116a17c678eSDavid Greenman 	0x26,	/* 10 */
117a17c678eSDavid Greenman 	0x0,	/* 11 */
118a17c678eSDavid Greenman 	0x60,	/* 12 */
119a17c678eSDavid Greenman 	0x0,	/* 13 */
120a17c678eSDavid Greenman 	0xf2,	/* 14 */
121a17c678eSDavid Greenman 	0x48,	/* 15 */
122a17c678eSDavid Greenman 	0x0,	/* 16 */
123a17c678eSDavid Greenman 	0x40,	/* 17 */
124a17c678eSDavid Greenman 	0xf3,	/* 18 */
125a17c678eSDavid Greenman 	0x0,	/* 19 */
126a17c678eSDavid Greenman 	0x3f,	/* 20 */
127a17c678eSDavid Greenman 	0x5,	/* 21 */
128a17c678eSDavid Greenman 	0x0, 0x0
129a17c678eSDavid Greenman };
130a17c678eSDavid Greenman 
1311cd443acSDavid Greenman static inline void fxp_scb_wait	__P((struct fxp_csr *));
132a17c678eSDavid Greenman static char *fxp_probe		__P((pcici_t, pcidi_t));
133a17c678eSDavid Greenman static void fxp_attach		__P((pcici_t, int));
134dd7610fcSStefan Eßer static void fxp_intr		__P((void *));
135a17c678eSDavid Greenman static void fxp_start		__P((struct ifnet *));
136a17c678eSDavid Greenman static int fxp_ioctl		__P((struct ifnet *, int, caddr_t));
137fb583156SDavid Greenman static void fxp_init		__P((void *));
1384a5f1499SDavid Greenman static void fxp_stop		__P((struct fxp_softc *));
1394a5f1499SDavid Greenman static void fxp_watchdog	__P((struct ifnet *));
140a17c678eSDavid Greenman static int fxp_add_rfabuf	__P((struct fxp_softc *, struct mbuf *));
1414a684684SDavid Greenman static void fxp_shutdown	__P((int, void *));
142dccee1a1SDavid Greenman static int fxp_mdi_read		__P((struct fxp_csr *, int, int));
143dccee1a1SDavid Greenman static void fxp_mdi_write	__P((struct fxp_csr *, int, int, int));
144dccee1a1SDavid Greenman static void fxp_read_eeprom	__P((struct fxp_csr *, u_short *, int, int));
145dccee1a1SDavid Greenman 
146a17c678eSDavid Greenman 
147a17c678eSDavid Greenman timeout_t fxp_stats_update;
148a17c678eSDavid Greenman 
149a17c678eSDavid Greenman static struct pci_device fxp_device = {
150a17c678eSDavid Greenman 	"fxp",
151a17c678eSDavid Greenman 	fxp_probe,
152a17c678eSDavid Greenman 	fxp_attach,
153a17c678eSDavid Greenman 	&fxp_count,
1544a684684SDavid Greenman 	NULL
155a17c678eSDavid Greenman };
156a17c678eSDavid Greenman DATA_SET(pcidevice_set, fxp_device);
157a17c678eSDavid Greenman 
158a17c678eSDavid Greenman /*
159f9be9005SDavid Greenman  * Set initial transmit threshold at 64 (512 bytes). This is
160f9be9005SDavid Greenman  * increased by 64 (512 bytes) at a time, to maximum of 192
161f9be9005SDavid Greenman  * (1536 bytes), if an underrun occurs.
162f9be9005SDavid Greenman  */
163f9be9005SDavid Greenman static int tx_threshold = 64;
164f9be9005SDavid Greenman 
165f9be9005SDavid Greenman /*
166a17c678eSDavid Greenman  * Number of transmit control blocks. This determines the number
167a17c678eSDavid Greenman  * of transmit buffers that can be chained in the CB list.
168a17c678eSDavid Greenman  * This must be a power of two.
169a17c678eSDavid Greenman  */
1701cd443acSDavid Greenman #define FXP_NTXCB	128
171a17c678eSDavid Greenman 
172a17c678eSDavid Greenman /*
173a17c678eSDavid Greenman  * TxCB list index mask. This is used to do list wrap-around.
174a17c678eSDavid Greenman  */
175a17c678eSDavid Greenman #define FXP_TXCB_MASK	(FXP_NTXCB - 1)
176a17c678eSDavid Greenman 
177a17c678eSDavid Greenman /*
178a17c678eSDavid Greenman  * Number of DMA segments in a TxCB. Note that this is carefully
17923a0ed7cSDavid Greenman  * chosen to make the total struct size an even power of two. It's
18023a0ed7cSDavid Greenman  * critical that no TxCB be split across a page boundry since
18123a0ed7cSDavid Greenman  * no attempt is made to allocate physically contiguous memory.
18223a0ed7cSDavid Greenman  *
18323a0ed7cSDavid Greenman  * XXX - don't forget to change the hard-coded constant in the
18423a0ed7cSDavid Greenman  * fxp_cb_tx struct (defined in if_fxpreg.h), too!
185a17c678eSDavid Greenman  */
18623a0ed7cSDavid Greenman #define FXP_NTXSEG	29
187a17c678eSDavid Greenman 
188a17c678eSDavid Greenman /*
189a17c678eSDavid Greenman  * Number of receive frame area buffers. These are large so chose
190a17c678eSDavid Greenman  * wisely.
191a17c678eSDavid Greenman  */
192a17c678eSDavid Greenman #define FXP_NRFABUFS	32
193a17c678eSDavid Greenman 
194dfe61cf1SDavid Greenman /*
195dfe61cf1SDavid Greenman  * Wait for the previous command to be accepted (but not necessarily
196dfe61cf1SDavid Greenman  * completed).
197dfe61cf1SDavid Greenman  */
1981cd443acSDavid Greenman static inline void
199a17c678eSDavid Greenman fxp_scb_wait(csr)
200a17c678eSDavid Greenman 	struct fxp_csr *csr;
201a17c678eSDavid Greenman {
202a17c678eSDavid Greenman 	int i = 10000;
203a17c678eSDavid Greenman 
204a17c678eSDavid Greenman 	while ((csr->scb_command & FXP_SCB_COMMAND_MASK) && --i);
205a17c678eSDavid Greenman }
206a17c678eSDavid Greenman 
207dfe61cf1SDavid Greenman /*
208dfe61cf1SDavid Greenman  * Return identification string if this is device is ours.
209dfe61cf1SDavid Greenman  */
210a17c678eSDavid Greenman static char *
211a17c678eSDavid Greenman fxp_probe(config_id, device_id)
212a17c678eSDavid Greenman 	pcici_t config_id;
213a17c678eSDavid Greenman 	pcidi_t device_id;
214a17c678eSDavid Greenman {
215a17c678eSDavid Greenman 	if (((device_id & 0xffff) == FXP_VENDORID_INTEL) &&
216a17c678eSDavid Greenman 	    ((device_id >> 16) & 0xffff) == FXP_DEVICEID_i82557)
217dccee1a1SDavid Greenman 		return ("Intel EtherExpress Pro 10/100B Ethernet");
218a17c678eSDavid Greenman 
219a17c678eSDavid Greenman 	return NULL;
220a17c678eSDavid Greenman }
221a17c678eSDavid Greenman 
222a17c678eSDavid Greenman /*
223a17c678eSDavid Greenman  * Allocate data structures and attach the device.
224a17c678eSDavid Greenman  */
225a17c678eSDavid Greenman static void
226a17c678eSDavid Greenman fxp_attach(config_id, unit)
227a17c678eSDavid Greenman 	pcici_t config_id;
228a17c678eSDavid Greenman 	int unit;
229a17c678eSDavid Greenman {
230a17c678eSDavid Greenman 	struct fxp_softc *sc;
231a17c678eSDavid Greenman 	struct ifnet *ifp;
232a17c678eSDavid Greenman 	vm_offset_t pbase;
233a17c678eSDavid Greenman 	int s, i;
234dccee1a1SDavid Greenman 	u_short data;
235a17c678eSDavid Greenman 
236a17c678eSDavid Greenman 	sc = malloc(sizeof(struct fxp_softc), M_DEVBUF, M_NOWAIT);
237a17c678eSDavid Greenman 	if (sc == NULL)
238a17c678eSDavid Greenman 		return;
239a17c678eSDavid Greenman 	bzero(sc, sizeof(struct fxp_softc));
240a17c678eSDavid Greenman 
241a17c678eSDavid Greenman 	s = splimp();
242a17c678eSDavid Greenman 
243dfe61cf1SDavid Greenman 	/*
244dfe61cf1SDavid Greenman 	 * Map control/status registers.
245dfe61cf1SDavid Greenman 	 */
246a17c678eSDavid Greenman 	if (!pci_map_mem(config_id, FXP_PCI_MMBA,
247a17c678eSDavid Greenman 	    (vm_offset_t *)&sc->csr, &pbase)) {
248a17c678eSDavid Greenman 		printf("fxp%d: couldn't map memory\n", unit);
249a17c678eSDavid Greenman 		goto fail;
250a17c678eSDavid Greenman 	}
251a17c678eSDavid Greenman 
252a17c678eSDavid Greenman 	/*
25333d14d86SDavid Greenman 	 * Reset to a stable state.
254a17c678eSDavid Greenman 	 */
25533d14d86SDavid Greenman 	sc->csr->port = FXP_PORT_SELECTIVE_RESET;
256a17c678eSDavid Greenman 	DELAY(10);
257a17c678eSDavid Greenman 
258dfe61cf1SDavid Greenman 	/*
259dfe61cf1SDavid Greenman 	 * Allocate our interrupt.
260dfe61cf1SDavid Greenman 	 */
261a17c678eSDavid Greenman 	if (!pci_map_int(config_id, fxp_intr, sc, &net_imask)) {
262a17c678eSDavid Greenman 		printf("fxp%d: couldn't map interrupt\n", unit);
263a17c678eSDavid Greenman 		goto fail;
264a17c678eSDavid Greenman 	}
265a17c678eSDavid Greenman 
266a17c678eSDavid Greenman 	sc->cbl_base = malloc(sizeof(struct fxp_cb_tx) * FXP_NTXCB,
267a17c678eSDavid Greenman 	    M_DEVBUF, M_NOWAIT);
268a17c678eSDavid Greenman 	if (sc->cbl_base == NULL)
269a17c678eSDavid Greenman 		goto malloc_fail;
270a17c678eSDavid Greenman 
271a17c678eSDavid Greenman 	sc->fxp_stats = malloc(sizeof(struct fxp_stats), M_DEVBUF, M_NOWAIT);
272a17c678eSDavid Greenman 	if (sc->fxp_stats == NULL)
273a17c678eSDavid Greenman 		goto malloc_fail;
274a17c678eSDavid Greenman 	bzero(sc->fxp_stats, sizeof(struct fxp_stats));
275a17c678eSDavid Greenman 
276dfe61cf1SDavid Greenman 	/*
277dfe61cf1SDavid Greenman 	 * Pre-allocate our receive buffers.
278dfe61cf1SDavid Greenman 	 */
279a17c678eSDavid Greenman 	for (i = 0; i < FXP_NRFABUFS; i++) {
280a17c678eSDavid Greenman 		if (fxp_add_rfabuf(sc, NULL) != 0) {
281a17c678eSDavid Greenman 			goto malloc_fail;
282a17c678eSDavid Greenman 		}
283a17c678eSDavid Greenman 	}
284a17c678eSDavid Greenman 
285dccee1a1SDavid Greenman 	/*
286dccee1a1SDavid Greenman 	 * Get info about the primary PHY
287dccee1a1SDavid Greenman 	 */
288dccee1a1SDavid Greenman 	fxp_read_eeprom(sc->csr, (u_short *)&data, 6, 1);
289dccee1a1SDavid Greenman 	sc->phy_primary_addr = data & 0xff;
290dccee1a1SDavid Greenman 	sc->phy_primary_device = (data >> 8) & 0x3f;
291dccee1a1SDavid Greenman 	sc->phy_10Mbps_only = data >> 15;
292dccee1a1SDavid Greenman 
293a17c678eSDavid Greenman 	ifp = &sc->arpcom.ac_if;
2949b44ff22SGarrett Wollman 	ifp->if_softc = sc;
295a17c678eSDavid Greenman 	ifp->if_unit = unit;
296a17c678eSDavid Greenman 	ifp->if_name = "fxp";
297a17c678eSDavid Greenman 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
298a17c678eSDavid Greenman 	ifp->if_ioctl = fxp_ioctl;
299a17c678eSDavid Greenman 	ifp->if_output = ether_output;
300a17c678eSDavid Greenman 	ifp->if_start = fxp_start;
301a17c678eSDavid Greenman 	ifp->if_watchdog = fxp_watchdog;
302a330e1f1SGary Palmer 	ifp->if_baudrate = 100000000;
303fb583156SDavid Greenman 	ifp->if_init = fxp_init;
304a17c678eSDavid Greenman 
305dccee1a1SDavid Greenman 	/*
306dccee1a1SDavid Greenman 	 * Read MAC address
307dccee1a1SDavid Greenman 	 */
308dccee1a1SDavid Greenman 	fxp_read_eeprom(sc->csr, (u_short *)sc->arpcom.ac_enaddr, 0, 3);
309dccee1a1SDavid Greenman 	printf("fxp%d: Ethernet address %6D", unit, sc->arpcom.ac_enaddr, ":");
310dccee1a1SDavid Greenman 	if (sc->phy_10Mbps_only)
311dccee1a1SDavid Greenman 		printf(", 10Mbps");
312dccee1a1SDavid Greenman 	printf("\n");
313a17c678eSDavid Greenman 
314dfe61cf1SDavid Greenman 	/*
315dfe61cf1SDavid Greenman 	 * Attach the interface.
316dfe61cf1SDavid Greenman 	 */
317a17c678eSDavid Greenman 	if_attach(ifp);
3189b44ff22SGarrett Wollman 	ether_ifattach(ifp);
3199b44ff22SGarrett Wollman 
320a17c678eSDavid Greenman #if NBPFILTER > 0
3219b44ff22SGarrett Wollman 	bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
322a17c678eSDavid Greenman #endif
3234a684684SDavid Greenman 
3244a684684SDavid Greenman 	/*
3254a684684SDavid Greenman 	 * Add shutdown hook so that DMA is disabled prior to reboot. Not
3264a684684SDavid Greenman 	 * doing do could allow DMA to corrupt kernel memory during the
3274a684684SDavid Greenman 	 * reboot before the driver initializes.
3284a684684SDavid Greenman 	 */
3294a684684SDavid Greenman 	at_shutdown(fxp_shutdown, sc, SHUTDOWN_POST_SYNC);
3304a684684SDavid Greenman 
331a17c678eSDavid Greenman 	splx(s);
332a17c678eSDavid Greenman 	return;
333a17c678eSDavid Greenman 
334a17c678eSDavid Greenman malloc_fail:
335a17c678eSDavid Greenman 	printf("fxp%d: Failed to malloc memory\n", unit);
336a17c678eSDavid Greenman 	(void) pci_unmap_int(config_id);
337a17c678eSDavid Greenman 	if (sc && sc->cbl_base)
338a17c678eSDavid Greenman 		free(sc->cbl_base, M_DEVBUF);
339a17c678eSDavid Greenman 	if (sc && sc->fxp_stats)
340a17c678eSDavid Greenman 		free(sc->fxp_stats, M_DEVBUF);
341a17c678eSDavid Greenman 	/* frees entire chain */
342a17c678eSDavid Greenman 	if (sc && sc->rfa_headm)
343a17c678eSDavid Greenman 		m_freem(sc->rfa_headm);
344a17c678eSDavid Greenman fail:
345a17c678eSDavid Greenman 	if (sc)
346a17c678eSDavid Greenman 		free(sc, M_DEVBUF);
347a17c678eSDavid Greenman 	splx(s);
348a17c678eSDavid Greenman }
349a17c678eSDavid Greenman 
350a17c678eSDavid Greenman /*
351dccee1a1SDavid Greenman  * Read from the serial EEPROM. Basically, you manually shift in
352dccee1a1SDavid Greenman  * the read opcode (one bit at a time) and then shift in the address,
353dccee1a1SDavid Greenman  * and then you shift out the data (all of this one bit at a time).
354dccee1a1SDavid Greenman  * The word size is 16 bits, so you have to provide the address for
355dccee1a1SDavid Greenman  * every 16 bits of data.
356a17c678eSDavid Greenman  */
357a17c678eSDavid Greenman static void
358dccee1a1SDavid Greenman fxp_read_eeprom(csr, data, offset, words)
359a17c678eSDavid Greenman 	struct fxp_csr *csr;
360dccee1a1SDavid Greenman 	u_short *data;
361dccee1a1SDavid Greenman 	int offset;
362dccee1a1SDavid Greenman 	int words;
363dccee1a1SDavid Greenman {
364dccee1a1SDavid Greenman 	u_short reg;
365a17c678eSDavid Greenman 	int i, x;
366a17c678eSDavid Greenman 
367dccee1a1SDavid Greenman 	for (i = 0; i < words; i++) {
368a17c678eSDavid Greenman 		csr->eeprom_control = FXP_EEPROM_EECS;
369a17c678eSDavid Greenman 		/*
370a17c678eSDavid Greenman 		 * Shift in read opcode.
371a17c678eSDavid Greenman 		 */
372a17c678eSDavid Greenman 		for (x = 3; x > 0; x--) {
373a17c678eSDavid Greenman 			if (FXP_EEPROM_OPC_READ & (1 << (x - 1))) {
374a17c678eSDavid Greenman 				reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
375a17c678eSDavid Greenman 			} else {
376a17c678eSDavid Greenman 				reg = FXP_EEPROM_EECS;
377a17c678eSDavid Greenman 			}
378a17c678eSDavid Greenman 			csr->eeprom_control = reg;
379a17c678eSDavid Greenman 			csr->eeprom_control = reg | FXP_EEPROM_EESK;
380a17c678eSDavid Greenman 			DELAY(1);
381a17c678eSDavid Greenman 			csr->eeprom_control = reg;
382a17c678eSDavid Greenman 			DELAY(1);
383a17c678eSDavid Greenman 		}
384a17c678eSDavid Greenman 		/*
385a17c678eSDavid Greenman 		 * Shift in address.
386a17c678eSDavid Greenman 		 */
387a17c678eSDavid Greenman 		for (x = 6; x > 0; x--) {
388dccee1a1SDavid Greenman 			if ((i + offset) & (1 << (x - 1))) {
389a17c678eSDavid Greenman 				reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI;
390a17c678eSDavid Greenman 			} else {
391a17c678eSDavid Greenman 				reg = FXP_EEPROM_EECS;
392a17c678eSDavid Greenman 			}
393a17c678eSDavid Greenman 			csr->eeprom_control = reg;
394a17c678eSDavid Greenman 			csr->eeprom_control = reg | FXP_EEPROM_EESK;
395a17c678eSDavid Greenman 			DELAY(1);
396a17c678eSDavid Greenman 			csr->eeprom_control = reg;
397a17c678eSDavid Greenman 			DELAY(1);
398a17c678eSDavid Greenman 		}
399a17c678eSDavid Greenman 		reg = FXP_EEPROM_EECS;
400a17c678eSDavid Greenman 		data[i] = 0;
401a17c678eSDavid Greenman 		/*
402a17c678eSDavid Greenman 		 * Shift out data.
403a17c678eSDavid Greenman 		 */
404a17c678eSDavid Greenman 		for (x = 16; x > 0; x--) {
405a17c678eSDavid Greenman 			csr->eeprom_control = reg | FXP_EEPROM_EESK;
406a17c678eSDavid Greenman 			DELAY(1);
407a17c678eSDavid Greenman 			if (csr->eeprom_control & FXP_EEPROM_EEDO)
408a17c678eSDavid Greenman 				data[i] |= (1 << (x - 1));
409a17c678eSDavid Greenman 			csr->eeprom_control = reg;
410a17c678eSDavid Greenman 			DELAY(1);
411a17c678eSDavid Greenman 		}
412a17c678eSDavid Greenman 		csr->eeprom_control = 0;
413a17c678eSDavid Greenman 		DELAY(1);
414a17c678eSDavid Greenman 	}
415a17c678eSDavid Greenman }
416a17c678eSDavid Greenman 
417a17c678eSDavid Greenman /*
4184a684684SDavid Greenman  * Device shutdown routine. Called at system shutdown after sync. The
419a17c678eSDavid Greenman  * main purpose of this routine is to shut off receiver DMA so that
420a17c678eSDavid Greenman  * kernel memory doesn't get clobbered during warmboot.
421a17c678eSDavid Greenman  */
4224a684684SDavid Greenman static void
4234a684684SDavid Greenman fxp_shutdown(howto, sc)
4244a684684SDavid Greenman 	int howto;
4254a684684SDavid Greenman 	void *sc;
426a17c678eSDavid Greenman {
4274a684684SDavid Greenman 	fxp_stop((struct fxp_softc *) sc);
428a17c678eSDavid Greenman }
429a17c678eSDavid Greenman 
430a17c678eSDavid Greenman /*
431a17c678eSDavid Greenman  * Start packet transmission on the interface.
432a17c678eSDavid Greenman  */
433a17c678eSDavid Greenman static void
434a17c678eSDavid Greenman fxp_start(ifp)
435a17c678eSDavid Greenman 	struct ifnet *ifp;
436a17c678eSDavid Greenman {
4379b44ff22SGarrett Wollman 	struct fxp_softc *sc = ifp->if_softc;
438a17c678eSDavid Greenman 	struct fxp_csr *csr = sc->csr;
439a17c678eSDavid Greenman 	struct fxp_cb_tx *txp;
440a17c678eSDavid Greenman 	struct mbuf *m, *mb_head;
44178fb85bbSDavid Greenman 	int segment, first = 1;
442a17c678eSDavid Greenman 
443a17c678eSDavid Greenman txloop:
444a17c678eSDavid Greenman 	/*
4451cd443acSDavid Greenman 	 * See if we're all filled up with buffers to transmit.
446a17c678eSDavid Greenman 	 */
4471cd443acSDavid Greenman 	if (sc->tx_queued >= FXP_NTXCB)
448a17c678eSDavid Greenman 		return;
4491cd443acSDavid Greenman 
450dfe61cf1SDavid Greenman 	/*
451dfe61cf1SDavid Greenman 	 * Grab a packet to transmit.
452dfe61cf1SDavid Greenman 	 */
453a17c678eSDavid Greenman 	IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, mb_head);
454a17c678eSDavid Greenman 	if (mb_head == NULL) {
455a17c678eSDavid Greenman 		/*
456a17c678eSDavid Greenman 		 * No more packets to send.
457a17c678eSDavid Greenman 		 */
458a17c678eSDavid Greenman 		return;
459a17c678eSDavid Greenman 	}
460a17c678eSDavid Greenman 
461dfe61cf1SDavid Greenman 	/*
462dfe61cf1SDavid Greenman 	 * Get pointer to next available (unused) descriptor.
463dfe61cf1SDavid Greenman 	 */
464a17c678eSDavid Greenman 	txp = sc->cbl_last->next;
465a17c678eSDavid Greenman 
466a17c678eSDavid Greenman 	/*
467a17c678eSDavid Greenman 	 * Go through each of the mbufs in the chain and initialize
468a17c678eSDavid Greenman 	 * the transmit buffers descriptors with the physical address
469a17c678eSDavid Greenman 	 * and size of the mbuf.
470a17c678eSDavid Greenman 	 */
47123a0ed7cSDavid Greenman tbdinit:
472a17c678eSDavid Greenman 	for (m = mb_head, segment = 0; m != NULL; m = m->m_next) {
473a17c678eSDavid Greenman 		if (m->m_len != 0) {
474a17c678eSDavid Greenman 			if (segment == FXP_NTXSEG)
475a17c678eSDavid Greenman 				break;
476a17c678eSDavid Greenman 			txp->tbd[segment].tb_addr =
477a17c678eSDavid Greenman 			    vtophys(mtod(m, vm_offset_t));
478a17c678eSDavid Greenman 			txp->tbd[segment].tb_size = m->m_len;
479a17c678eSDavid Greenman 			segment++;
480a17c678eSDavid Greenman 		}
481a17c678eSDavid Greenman 	}
482fb583156SDavid Greenman 	if (m != NULL) {
48323a0ed7cSDavid Greenman 		struct mbuf *mn;
48423a0ed7cSDavid Greenman 
485a17c678eSDavid Greenman 		/*
486a17c678eSDavid Greenman 		 * We ran out of segments. We have to recopy this mbuf
487a17c678eSDavid Greenman 		 * chain first.
488a17c678eSDavid Greenman 		 */
48923a0ed7cSDavid Greenman 		MGETHDR(mn, M_DONTWAIT, MT_DATA);
49023a0ed7cSDavid Greenman 		if (mn == NULL) {
49123a0ed7cSDavid Greenman 			m_freem(mb_head);
49223a0ed7cSDavid Greenman 			return;
493a17c678eSDavid Greenman 		}
49423a0ed7cSDavid Greenman 		if (mb_head->m_pkthdr.len > MHLEN) {
49523a0ed7cSDavid Greenman 			MCLGET(mn, M_DONTWAIT);
49623a0ed7cSDavid Greenman 			if ((mn->m_flags & M_EXT) == 0) {
49723a0ed7cSDavid Greenman 				m_freem(mn);
49823a0ed7cSDavid Greenman 				m_freem(mb_head);
49923a0ed7cSDavid Greenman 				return;
50023a0ed7cSDavid Greenman 			}
50123a0ed7cSDavid Greenman 		}
50223a0ed7cSDavid Greenman 		m_copydata(mb_head, 0, mb_head->m_pkthdr.len, mtod(mn, caddr_t));
50323a0ed7cSDavid Greenman 		mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len;
50423a0ed7cSDavid Greenman 		m_freem(mb_head);
50523a0ed7cSDavid Greenman 		mb_head = mn;
50623a0ed7cSDavid Greenman 		goto tbdinit;
50723a0ed7cSDavid Greenman 	}
50823a0ed7cSDavid Greenman 
50923a0ed7cSDavid Greenman 	txp->tbd_number = segment;
5101cd443acSDavid Greenman 	txp->mb_head = mb_head;
51123a0ed7cSDavid Greenman 
512a17c678eSDavid Greenman 	/*
513a17c678eSDavid Greenman 	 * Finish the initialization of this TxCB.
514a17c678eSDavid Greenman 	 */
515a17c678eSDavid Greenman 	txp->cb_status = 0;
516a17c678eSDavid Greenman 	txp->cb_command =
517a17c678eSDavid Greenman 	    FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S;
518f9be9005SDavid Greenman 	txp->tx_threshold = tx_threshold;
519a17c678eSDavid Greenman 
520a17c678eSDavid Greenman 	/*
521a17c678eSDavid Greenman 	 * Advance the end-of-list forward.
522a17c678eSDavid Greenman 	 */
523a17c678eSDavid Greenman 	sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
524a17c678eSDavid Greenman 	sc->cbl_last = txp;
525a17c678eSDavid Greenman 
526a17c678eSDavid Greenman 	/*
5271cd443acSDavid Greenman 	 * Advance the beginning of the list forward if there are
5281cd443acSDavid Greenman 	 * no other packets queued (when nothing is queued, cbl_first
5291cd443acSDavid Greenman 	 * sits on the last TxCB that was sent out)..
530a17c678eSDavid Greenman 	 */
5311cd443acSDavid Greenman 	if (sc->tx_queued == 0)
532a17c678eSDavid Greenman 		sc->cbl_first = txp;
533a17c678eSDavid Greenman 
5341cd443acSDavid Greenman 	sc->tx_queued++;
5351cd443acSDavid Greenman 
53678fb85bbSDavid Greenman 	/*
53778fb85bbSDavid Greenman 	 * Only need to wait prior to the first resume command.
53878fb85bbSDavid Greenman 	 */
53978fb85bbSDavid Greenman 	if (first) {
54078fb85bbSDavid Greenman 		first--;
5411cd443acSDavid Greenman 		fxp_scb_wait(csr);
54278fb85bbSDavid Greenman 	}
5433ba65732SDavid Greenman 
544a17c678eSDavid Greenman 	/*
54578fb85bbSDavid Greenman 	 * Resume transmission if suspended.
546a17c678eSDavid Greenman 	 */
547a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_CU_RESUME;
548a17c678eSDavid Greenman 
549a17c678eSDavid Greenman #if NBPFILTER > 0
550a17c678eSDavid Greenman 	/*
551a17c678eSDavid Greenman 	 * Pass packet to bpf if there is a listener.
552a17c678eSDavid Greenman 	 */
553fb583156SDavid Greenman 	if (ifp->if_bpf)
5549b44ff22SGarrett Wollman 		bpf_mtap(ifp, mb_head);
555a17c678eSDavid Greenman #endif
556a17c678eSDavid Greenman 	/*
557a17c678eSDavid Greenman 	 * Set a 5 second timer just in case we don't hear from the
558a17c678eSDavid Greenman 	 * card again.
559a17c678eSDavid Greenman 	 */
560a17c678eSDavid Greenman 	ifp->if_timer = 5;
561a17c678eSDavid Greenman 
562a17c678eSDavid Greenman 	goto txloop;
563a17c678eSDavid Greenman }
564a17c678eSDavid Greenman 
565a17c678eSDavid Greenman /*
5669c7d2607SDavid Greenman  * Process interface interrupts.
567a17c678eSDavid Greenman  */
568dd7610fcSStefan Eßer static void
569a17c678eSDavid Greenman fxp_intr(arg)
570a17c678eSDavid Greenman 	void *arg;
571a17c678eSDavid Greenman {
572a17c678eSDavid Greenman 	struct fxp_softc *sc = arg;
573a17c678eSDavid Greenman 	struct fxp_csr *csr = sc->csr;
574a17c678eSDavid Greenman 	struct ifnet *ifp = &sc->arpcom.ac_if;
5751cd443acSDavid Greenman 	u_int8_t statack;
576a17c678eSDavid Greenman 
577a17c678eSDavid Greenman 	while ((statack = csr->scb_statack) != 0) {
578a17c678eSDavid Greenman 		/*
579a17c678eSDavid Greenman 		 * First ACK all the interrupts in this pass.
580a17c678eSDavid Greenman 		 */
581a17c678eSDavid Greenman 		csr->scb_statack = statack;
582a17c678eSDavid Greenman 
583a17c678eSDavid Greenman 		/*
584a17c678eSDavid Greenman 		 * Free any finished transmit mbuf chains.
585a17c678eSDavid Greenman 		 */
586a17c678eSDavid Greenman 		if (statack & FXP_SCB_STATACK_CNA) {
587a17c678eSDavid Greenman 			struct fxp_cb_tx *txp;
588a17c678eSDavid Greenman 
589a17c678eSDavid Greenman 			for (txp = sc->cbl_first;
5901cd443acSDavid Greenman 			    (txp->cb_status & FXP_CB_STATUS_C) != 0;
591a17c678eSDavid Greenman 			    txp = txp->next) {
5921cd443acSDavid Greenman 				if (txp->mb_head != NULL) {
593a17c678eSDavid Greenman 					m_freem(txp->mb_head);
594a17c678eSDavid Greenman 					txp->mb_head = NULL;
595a17c678eSDavid Greenman 					sc->tx_queued--;
596a17c678eSDavid Greenman 				}
5971cd443acSDavid Greenman 				if (txp->cb_command & FXP_CB_COMMAND_S)
5981cd443acSDavid Greenman 					break;
5991cd443acSDavid Greenman 			}
600a17c678eSDavid Greenman 			sc->cbl_first = txp;
601a17c678eSDavid Greenman 			/*
602a17c678eSDavid Greenman 			 * Clear watchdog timer. It may or may not be set
603a17c678eSDavid Greenman 			 * again in fxp_start().
604a17c678eSDavid Greenman 			 */
605a17c678eSDavid Greenman 			ifp->if_timer = 0;
606a17c678eSDavid Greenman 			fxp_start(ifp);
607a17c678eSDavid Greenman 		}
608a17c678eSDavid Greenman 		/*
609a17c678eSDavid Greenman 		 * Process receiver interrupts. If a no-resource (RNR)
610a17c678eSDavid Greenman 		 * condition exists, get whatever packets we can and
611a17c678eSDavid Greenman 		 * re-start the receiver.
612a17c678eSDavid Greenman 		 */
613a17c678eSDavid Greenman 		if (statack & (FXP_SCB_STATACK_FR | FXP_SCB_STATACK_RNR)) {
614a17c678eSDavid Greenman 			struct mbuf *m;
615a17c678eSDavid Greenman 			struct fxp_rfa *rfa;
616a17c678eSDavid Greenman rcvloop:
617a17c678eSDavid Greenman 			m = sc->rfa_headm;
618dfe61cf1SDavid Greenman 			rfa = (struct fxp_rfa *)m->m_ext.ext_buf;
619a17c678eSDavid Greenman 
620a17c678eSDavid Greenman 			if (rfa->rfa_status & FXP_RFA_STATUS_C) {
621dfe61cf1SDavid Greenman 				/*
622dfe61cf1SDavid Greenman 				 * Remove first packet from the chain.
623dfe61cf1SDavid Greenman 				 */
624a17c678eSDavid Greenman 				sc->rfa_headm = m->m_next;
625a17c678eSDavid Greenman 				m->m_next = NULL;
626a17c678eSDavid Greenman 
627dfe61cf1SDavid Greenman 				/*
628dfe61cf1SDavid Greenman 				 * Add a new buffer to the receive chain. If this
629dfe61cf1SDavid Greenman 				 * fails, the old buffer is recycled instead.
630dfe61cf1SDavid Greenman 				 */
631a17c678eSDavid Greenman 				if (fxp_add_rfabuf(sc, m) == 0) {
632a17c678eSDavid Greenman 					struct ether_header *eh;
633a17c678eSDavid Greenman 					u_short total_len;
634a17c678eSDavid Greenman 
635a17c678eSDavid Greenman 					total_len = rfa->actual_size & (MCLBYTES - 1);
636a17c678eSDavid Greenman 					m->m_pkthdr.rcvif = ifp;
637a17c678eSDavid Greenman 					m->m_pkthdr.len = m->m_len = total_len -
638a17c678eSDavid Greenman 					    sizeof(struct ether_header);
639a17c678eSDavid Greenman 					eh = mtod(m, struct ether_header *);
640a17c678eSDavid Greenman #if NBPFILTER > 0
641fb583156SDavid Greenman 					if (ifp->if_bpf) {
6429b44ff22SGarrett Wollman 						bpf_tap(ifp, mtod(m, caddr_t), total_len);
643a17c678eSDavid Greenman 						/*
644a17c678eSDavid Greenman 						 * Only pass this packet up if it is for us.
645a17c678eSDavid Greenman 						 */
646a17c678eSDavid Greenman 						if ((ifp->if_flags & IFF_PROMISC) &&
647a17c678eSDavid Greenman 						    (rfa->rfa_status & FXP_RFA_STATUS_IAMATCH) &&
648a17c678eSDavid Greenman 						    (eh->ether_dhost[0] & 1) == 0) {
649a17c678eSDavid Greenman 							m_freem(m);
650a17c678eSDavid Greenman 							goto rcvloop;
651a17c678eSDavid Greenman 						}
652a17c678eSDavid Greenman 					}
653a17c678eSDavid Greenman #endif
654a17c678eSDavid Greenman 					m->m_data += sizeof(struct ether_header);
655a17c678eSDavid Greenman 					ether_input(ifp, eh, m);
656a17c678eSDavid Greenman 				}
657a17c678eSDavid Greenman 				goto rcvloop;
658a17c678eSDavid Greenman 			}
659a17c678eSDavid Greenman 			if (statack & FXP_SCB_STATACK_RNR) {
660a17c678eSDavid Greenman 				struct fxp_csr *csr = sc->csr;
661a17c678eSDavid Greenman 
6621cd443acSDavid Greenman 				fxp_scb_wait(csr);
663dfe61cf1SDavid Greenman 				csr->scb_general = vtophys(sc->rfa_headm->m_ext.ext_buf);
664a17c678eSDavid Greenman 				csr->scb_command = FXP_SCB_COMMAND_RU_START;
665a17c678eSDavid Greenman 			}
666a17c678eSDavid Greenman 		}
667a17c678eSDavid Greenman 	}
668a17c678eSDavid Greenman }
669a17c678eSDavid Greenman 
670dfe61cf1SDavid Greenman /*
671dfe61cf1SDavid Greenman  * Update packet in/out/collision statistics. The i82557 doesn't
672dfe61cf1SDavid Greenman  * allow you to access these counters without doing a fairly
673dfe61cf1SDavid Greenman  * expensive DMA to get _all_ of the statistics it maintains, so
674dfe61cf1SDavid Greenman  * we do this operation here only once per second. The statistics
675dfe61cf1SDavid Greenman  * counters in the kernel are updated from the previous dump-stats
676dfe61cf1SDavid Greenman  * DMA and then a new dump-stats DMA is started. The on-chip
677dfe61cf1SDavid Greenman  * counters are zeroed when the DMA completes. If we can't start
678dfe61cf1SDavid Greenman  * the DMA immediately, we don't wait - we just prepare to read
679dfe61cf1SDavid Greenman  * them again next time.
680dfe61cf1SDavid Greenman  */
681a17c678eSDavid Greenman void
682a17c678eSDavid Greenman fxp_stats_update(arg)
683a17c678eSDavid Greenman 	void *arg;
684a17c678eSDavid Greenman {
685a17c678eSDavid Greenman 	struct fxp_softc *sc = arg;
686a17c678eSDavid Greenman 	struct ifnet *ifp = &sc->arpcom.ac_if;
687a17c678eSDavid Greenman 	struct fxp_stats *sp = sc->fxp_stats;
688a17c678eSDavid Greenman 
689a17c678eSDavid Greenman 	ifp->if_opackets += sp->tx_good;
690a17c678eSDavid Greenman 	ifp->if_collisions += sp->tx_total_collisions;
691a17c678eSDavid Greenman 	ifp->if_ipackets += sp->rx_good;
6923ba65732SDavid Greenman 	ifp->if_ierrors +=
6933ba65732SDavid Greenman 	    sp->rx_crc_errors +
6943ba65732SDavid Greenman 	    sp->rx_alignment_errors +
6953ba65732SDavid Greenman 	    sp->rx_rnr_errors +
6966e39e599SDavid Greenman 	    sp->rx_overrun_errors;
697a17c678eSDavid Greenman 	/*
698f9be9005SDavid Greenman 	 * If any transmit underruns occured, bump up the transmit
699f9be9005SDavid Greenman 	 * threshold by another 512 bytes (64 * 8).
700f9be9005SDavid Greenman 	 */
701f9be9005SDavid Greenman 	if (sp->tx_underruns) {
702f9be9005SDavid Greenman 		ifp->if_oerrors += sp->tx_underruns;
703f9be9005SDavid Greenman 		if (tx_threshold < 192)
704f9be9005SDavid Greenman 			tx_threshold += 64;
705f9be9005SDavid Greenman 	}
706f9be9005SDavid Greenman 	/*
7073ba65732SDavid Greenman 	 * If there is no pending command, start another stats
7083ba65732SDavid Greenman 	 * dump. Otherwise punt for now.
709a17c678eSDavid Greenman 	 */
710dfe61cf1SDavid Greenman 	if ((sc->csr->scb_command & FXP_SCB_COMMAND_MASK) == 0) {
711a17c678eSDavid Greenman 		/*
712dfe61cf1SDavid Greenman 		 * Start another stats dump. By waiting for it to be
713dfe61cf1SDavid Greenman 		 * accepted, we avoid having to do splhigh locking when
714dfe61cf1SDavid Greenman 		 * writing scb_command in other parts of the driver.
715a17c678eSDavid Greenman 		 */
716a17c678eSDavid Greenman 		sc->csr->scb_command = FXP_SCB_COMMAND_CU_DUMPRESET;
7171cd443acSDavid Greenman 		fxp_scb_wait(sc->csr);
718dfe61cf1SDavid Greenman 	} else {
719dfe61cf1SDavid Greenman 		/*
720dfe61cf1SDavid Greenman 		 * A previous command is still waiting to be accepted.
721dfe61cf1SDavid Greenman 		 * Just zero our copy of the stats and wait for the
7223ba65732SDavid Greenman 		 * next timer event to update them.
723dfe61cf1SDavid Greenman 		 */
724dfe61cf1SDavid Greenman 		sp->tx_good = 0;
725f9be9005SDavid Greenman 		sp->tx_underruns = 0;
726dfe61cf1SDavid Greenman 		sp->tx_total_collisions = 0;
7273ba65732SDavid Greenman 
728dfe61cf1SDavid Greenman 		sp->rx_good = 0;
7293ba65732SDavid Greenman 		sp->rx_crc_errors = 0;
7303ba65732SDavid Greenman 		sp->rx_alignment_errors = 0;
7313ba65732SDavid Greenman 		sp->rx_rnr_errors = 0;
7323ba65732SDavid Greenman 		sp->rx_overrun_errors = 0;
733dfe61cf1SDavid Greenman 	}
734a17c678eSDavid Greenman 	/*
735a17c678eSDavid Greenman 	 * Schedule another timeout one second from now.
736a17c678eSDavid Greenman 	 */
737a17c678eSDavid Greenman 	timeout(fxp_stats_update, sc, hz);
738a17c678eSDavid Greenman }
739a17c678eSDavid Greenman 
740a17c678eSDavid Greenman /*
741a17c678eSDavid Greenman  * Stop the interface. Cancels the statistics updater and resets
742a17c678eSDavid Greenman  * the interface.
743a17c678eSDavid Greenman  */
744a17c678eSDavid Greenman static void
7454a5f1499SDavid Greenman fxp_stop(sc)
7464a5f1499SDavid Greenman 	struct fxp_softc *sc;
747a17c678eSDavid Greenman {
748a17c678eSDavid Greenman 	struct ifnet *ifp = &sc->arpcom.ac_if;
7493ba65732SDavid Greenman 	struct fxp_cb_tx *txp;
7503ba65732SDavid Greenman 	int i;
751a17c678eSDavid Greenman 
752a17c678eSDavid Greenman 	/*
753a17c678eSDavid Greenman 	 * Cancel stats updater.
754a17c678eSDavid Greenman 	 */
755a17c678eSDavid Greenman 	untimeout(fxp_stats_update, sc);
7563ba65732SDavid Greenman 
7573ba65732SDavid Greenman 	/*
7583ba65732SDavid Greenman 	 * Issue software reset
7593ba65732SDavid Greenman 	 */
76033d14d86SDavid Greenman 	sc->csr->port = FXP_PORT_SELECTIVE_RESET;
761a17c678eSDavid Greenman 	DELAY(10);
762a17c678eSDavid Greenman 
7633ba65732SDavid Greenman 	/*
7643ba65732SDavid Greenman 	 * Release any xmit buffers.
7653ba65732SDavid Greenman 	 */
7663ba65732SDavid Greenman 	for (txp = sc->cbl_first; txp != NULL && txp->mb_head != NULL;
7673ba65732SDavid Greenman 	    txp = txp->next) {
7683ba65732SDavid Greenman 		m_freem(txp->mb_head);
7693ba65732SDavid Greenman 		txp->mb_head = NULL;
7703ba65732SDavid Greenman 	}
7713ba65732SDavid Greenman 	sc->tx_queued = 0;
7723ba65732SDavid Greenman 
7733ba65732SDavid Greenman 	/*
7743ba65732SDavid Greenman 	 * Free all the receive buffers then reallocate/reinitialize
7753ba65732SDavid Greenman 	 */
7763ba65732SDavid Greenman 	if (sc->rfa_headm != NULL)
7773ba65732SDavid Greenman 		m_freem(sc->rfa_headm);
7783ba65732SDavid Greenman 	sc->rfa_headm = NULL;
7793ba65732SDavid Greenman 	sc->rfa_tailm = NULL;
7803ba65732SDavid Greenman 	for (i = 0; i < FXP_NRFABUFS; i++) {
7813ba65732SDavid Greenman 		if (fxp_add_rfabuf(sc, NULL) != 0) {
7823ba65732SDavid Greenman 			/*
7833ba65732SDavid Greenman 			 * This "can't happen" - we're at splimp()
7843ba65732SDavid Greenman 			 * and we just freed all the buffers we need
7853ba65732SDavid Greenman 			 * above.
7863ba65732SDavid Greenman 			 */
7873ba65732SDavid Greenman 			panic("fxp_stop: no buffers!");
7883ba65732SDavid Greenman 		}
7893ba65732SDavid Greenman 	}
7903ba65732SDavid Greenman 
7913ba65732SDavid Greenman 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
7923ba65732SDavid Greenman 	ifp->if_timer = 0;
793a17c678eSDavid Greenman }
794a17c678eSDavid Greenman 
795a17c678eSDavid Greenman /*
796a17c678eSDavid Greenman  * Watchdog/transmission transmit timeout handler. Called when a
797a17c678eSDavid Greenman  * transmission is started on the interface, but no interrupt is
798a17c678eSDavid Greenman  * received before the timeout. This usually indicates that the
799a17c678eSDavid Greenman  * card has wedged for some reason.
800a17c678eSDavid Greenman  */
801a17c678eSDavid Greenman static void
8024a5f1499SDavid Greenman fxp_watchdog(ifp)
8034a5f1499SDavid Greenman 	struct ifnet *ifp;
804a17c678eSDavid Greenman {
8054a5f1499SDavid Greenman 	log(LOG_ERR, "fxp%d: device timeout\n", ifp->if_unit);
8064a5f1499SDavid Greenman 	ifp->if_oerrors++;
807a17c678eSDavid Greenman 
808fb583156SDavid Greenman 	fxp_init(ifp->if_softc);
809a17c678eSDavid Greenman }
810a17c678eSDavid Greenman 
811a17c678eSDavid Greenman static void
812fb583156SDavid Greenman fxp_init(xsc)
813fb583156SDavid Greenman 	void *xsc;
814a17c678eSDavid Greenman {
815fb583156SDavid Greenman 	struct fxp_softc *sc = xsc;
816fb583156SDavid Greenman 	struct ifnet *ifp = &sc->arpcom.ac_if;
817a17c678eSDavid Greenman 	struct fxp_cb_config *cbp;
818a17c678eSDavid Greenman 	struct fxp_cb_ias *cb_ias;
819a17c678eSDavid Greenman 	struct fxp_cb_tx *txp;
820a17c678eSDavid Greenman 	struct fxp_csr *csr = sc->csr;
821a17c678eSDavid Greenman 	int i, s, mcast, prm;
822a17c678eSDavid Greenman 
823a17c678eSDavid Greenman 	s = splimp();
824a17c678eSDavid Greenman 	/*
8253ba65732SDavid Greenman 	 * Cancel any pending I/O
826a17c678eSDavid Greenman 	 */
8273ba65732SDavid Greenman 	fxp_stop(sc);
828a17c678eSDavid Greenman 
829a17c678eSDavid Greenman 	prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0;
830a17c678eSDavid Greenman 	sc->promisc_mode = prm;
831a17c678eSDavid Greenman 	/*
832a17c678eSDavid Greenman 	 * Sleeze out here and enable reception of all multicasts if
833a17c678eSDavid Greenman 	 * multicasts are enabled. Ideally, we'd program the multicast
834a17c678eSDavid Greenman 	 * address filter to only accept specific multicasts.
835a17c678eSDavid Greenman 	 */
836a17c678eSDavid Greenman 	mcast = (ifp->if_flags & (IFF_MULTICAST|IFF_ALLMULTI)) ? 1 : 0;
837a17c678eSDavid Greenman 
838a17c678eSDavid Greenman 	/*
839a17c678eSDavid Greenman 	 * Initialize base of CBL and RFA memory. Loading with zero
840a17c678eSDavid Greenman 	 * sets it up for regular linear addressing.
841a17c678eSDavid Greenman 	 */
842a17c678eSDavid Greenman 	csr->scb_general = 0;
843a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_CU_BASE;
844a17c678eSDavid Greenman 
8451cd443acSDavid Greenman 	fxp_scb_wait(csr);
846a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_RU_BASE;
847a17c678eSDavid Greenman 
848a17c678eSDavid Greenman 	/*
849a17c678eSDavid Greenman 	 * Initialize base of dump-stats buffer.
850a17c678eSDavid Greenman 	 */
8511cd443acSDavid Greenman 	fxp_scb_wait(csr);
852a17c678eSDavid Greenman 	csr->scb_general = vtophys(sc->fxp_stats);
853a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_CU_DUMP_ADR;
854a17c678eSDavid Greenman 
855a17c678eSDavid Greenman 	/*
856a17c678eSDavid Greenman 	 * We temporarily use memory that contains the TxCB list to
857a17c678eSDavid Greenman 	 * construct the config CB. The TxCB list memory is rebuilt
858a17c678eSDavid Greenman 	 * later.
859a17c678eSDavid Greenman 	 */
860a17c678eSDavid Greenman 	cbp = (struct fxp_cb_config *) sc->cbl_base;
861a17c678eSDavid Greenman 
862a17c678eSDavid Greenman 	/*
863a17c678eSDavid Greenman 	 * This bcopy is kind of disgusting, but there are a bunch of must be
864a17c678eSDavid Greenman 	 * zero and must be one bits in this structure and this is the easiest
865a17c678eSDavid Greenman 	 * way to initialize them all to proper values.
866a17c678eSDavid Greenman 	 */
867a17c678eSDavid Greenman 	bcopy(fxp_cb_config_template, cbp, sizeof(struct fxp_cb_config));
868a17c678eSDavid Greenman 
869a17c678eSDavid Greenman 	cbp->cb_status =	0;
870a17c678eSDavid Greenman 	cbp->cb_command =	FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL;
871a17c678eSDavid Greenman 	cbp->link_addr =	-1;	/* (no) next command */
872a17c678eSDavid Greenman 	cbp->byte_count =	22;	/* (22) bytes to config */
873001696daSDavid Greenman 	cbp->rx_fifo_limit =	8;	/* rx fifo threshold (32 bytes) */
874001696daSDavid Greenman 	cbp->tx_fifo_limit =	0;	/* tx fifo threshold (0 bytes) */
875a17c678eSDavid Greenman 	cbp->adaptive_ifs =	0;	/* (no) adaptive interframe spacing */
876001696daSDavid Greenman 	cbp->rx_dma_bytecount =	0;	/* (no) rx DMA max */
877001696daSDavid Greenman 	cbp->tx_dma_bytecount =	0;	/* (no) tx DMA max */
878001696daSDavid Greenman 	cbp->dma_bce =		0;	/* (disable) dma max counters */
879a17c678eSDavid Greenman 	cbp->late_scb =		0;	/* (don't) defer SCB update */
880a17c678eSDavid Greenman 	cbp->tno_int =		0;	/* (disable) tx not okay interrupt */
881001696daSDavid Greenman 	cbp->ci_int =		0;	/* interrupt on CU not active */
882a17c678eSDavid Greenman 	cbp->save_bf =		prm;	/* save bad frames */
883a17c678eSDavid Greenman 	cbp->disc_short_rx =	!prm;	/* discard short packets */
884a17c678eSDavid Greenman 	cbp->underrun_retry =	1;	/* retry mode (1) on DMA underrun */
885dccee1a1SDavid Greenman 	cbp->mediatype =	!sc->phy_10Mbps_only; /* interface mode */
886a17c678eSDavid Greenman 	cbp->nsai =		1;	/* (don't) disable source addr insert */
887a17c678eSDavid Greenman 	cbp->preamble_length =	2;	/* (7 byte) preamble */
888a17c678eSDavid Greenman 	cbp->loopback =		0;	/* (don't) loopback */
889a17c678eSDavid Greenman 	cbp->linear_priority =	0;	/* (normal CSMA/CD operation) */
890a17c678eSDavid Greenman 	cbp->linear_pri_mode =	0;	/* (wait after xmit only) */
891a17c678eSDavid Greenman 	cbp->interfrm_spacing =	6;	/* (96 bits of) interframe spacing */
892a17c678eSDavid Greenman 	cbp->promiscuous =	prm;	/* promiscuous mode */
893a17c678eSDavid Greenman 	cbp->bcast_disable =	0;	/* (don't) disable broadcasts */
894001696daSDavid Greenman 	cbp->crscdt =		0;	/* (CRS only) */
895a17c678eSDavid Greenman 	cbp->stripping =	!prm;	/* truncate rx packet to byte count */
896a17c678eSDavid Greenman 	cbp->padding =		1;	/* (do) pad short tx packets */
897a17c678eSDavid Greenman 	cbp->rcv_crc_xfer =	0;	/* (don't) xfer CRC to host */
898a17c678eSDavid Greenman 	cbp->force_fdx =	0;	/* (don't) force full duplex */
8993ba65732SDavid Greenman 	cbp->fdx_pin_en =	1;	/* (enable) FDX# pin */
900a17c678eSDavid Greenman 	cbp->multi_ia =		0;	/* (don't) accept multiple IAs */
901a17c678eSDavid Greenman 	cbp->mc_all =		mcast;	/* accept all multicasts */
902a17c678eSDavid Greenman 
903a17c678eSDavid Greenman 	/*
904a17c678eSDavid Greenman 	 * Start the config command/DMA.
905a17c678eSDavid Greenman 	 */
9061cd443acSDavid Greenman 	fxp_scb_wait(csr);
907a17c678eSDavid Greenman 	csr->scb_general = vtophys(cbp);
908a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_CU_START;
909a17c678eSDavid Greenman 	/* ...and wait for it to complete. */
910a17c678eSDavid Greenman 	while (!(cbp->cb_status & FXP_CB_STATUS_C));
911a17c678eSDavid Greenman 
912a17c678eSDavid Greenman 	/*
913a17c678eSDavid Greenman 	 * Now initialize the station address. Temporarily use the TxCB
914a17c678eSDavid Greenman 	 * memory area like we did above for the config CB.
915a17c678eSDavid Greenman 	 */
916a17c678eSDavid Greenman 	cb_ias = (struct fxp_cb_ias *) sc->cbl_base;
917a17c678eSDavid Greenman 	cb_ias->cb_status = 0;
918a17c678eSDavid Greenman 	cb_ias->cb_command = FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL;
919a17c678eSDavid Greenman 	cb_ias->link_addr = -1;
920a17c678eSDavid Greenman 	bcopy(sc->arpcom.ac_enaddr, (void *)cb_ias->macaddr,
921a17c678eSDavid Greenman 	    sizeof(sc->arpcom.ac_enaddr));
922a17c678eSDavid Greenman 
923a17c678eSDavid Greenman 	/*
924a17c678eSDavid Greenman 	 * Start the IAS (Individual Address Setup) command/DMA.
925a17c678eSDavid Greenman 	 */
9261cd443acSDavid Greenman 	fxp_scb_wait(csr);
927a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_CU_START;
928a17c678eSDavid Greenman 	/* ...and wait for it to complete. */
929a17c678eSDavid Greenman 	while (!(cb_ias->cb_status & FXP_CB_STATUS_C));
930a17c678eSDavid Greenman 
931a17c678eSDavid Greenman 	/*
932a17c678eSDavid Greenman 	 * Initialize transmit control block (TxCB) list.
933a17c678eSDavid Greenman 	 */
934a17c678eSDavid Greenman 
935a17c678eSDavid Greenman 	txp = sc->cbl_base;
936a17c678eSDavid Greenman 	bzero(txp, sizeof(struct fxp_cb_tx) * FXP_NTXCB);
937a17c678eSDavid Greenman 	for (i = 0; i < FXP_NTXCB; i++) {
938a17c678eSDavid Greenman 		txp[i].cb_status = FXP_CB_STATUS_C | FXP_CB_STATUS_OK;
939a17c678eSDavid Greenman 		txp[i].cb_command = FXP_CB_COMMAND_NOP;
940a17c678eSDavid Greenman 		txp[i].link_addr = vtophys(&txp[(i + 1) & FXP_TXCB_MASK]);
941a17c678eSDavid Greenman 		txp[i].tbd_array_addr = vtophys(&txp[i].tbd[0]);
942a17c678eSDavid Greenman 		txp[i].next = &txp[(i + 1) & FXP_TXCB_MASK];
943a17c678eSDavid Greenman 	}
944a17c678eSDavid Greenman 	/*
945a17c678eSDavid Greenman 	 * Set the stop flag on the first TxCB and start the control
946a17c678eSDavid Greenman 	 * unit. It will execute the NOP and then suspend.
947a17c678eSDavid Greenman 	 */
948a17c678eSDavid Greenman 	txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S;
949a17c678eSDavid Greenman 	sc->cbl_first = sc->cbl_last = txp;
950a17c678eSDavid Greenman 	sc->tx_queued = 0;
951a17c678eSDavid Greenman 
9521cd443acSDavid Greenman 	fxp_scb_wait(csr);
953a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_CU_START;
954a17c678eSDavid Greenman 
955a17c678eSDavid Greenman 	/*
956a17c678eSDavid Greenman 	 * Initialize receiver buffer area - RFA.
957a17c678eSDavid Greenman 	 */
9581cd443acSDavid Greenman 	fxp_scb_wait(csr);
959dfe61cf1SDavid Greenman 	csr->scb_general = vtophys(sc->rfa_headm->m_ext.ext_buf);
960a17c678eSDavid Greenman 	csr->scb_command = FXP_SCB_COMMAND_RU_START;
961a17c678eSDavid Greenman 
962dccee1a1SDavid Greenman 	/*
963dccee1a1SDavid Greenman 	 * Toggle a few bits in the DP83840 PHY.
964dccee1a1SDavid Greenman 	 */
965dccee1a1SDavid Greenman 	if (sc->phy_primary_device == FXP_PHY_DP83840) {
966dccee1a1SDavid Greenman 		fxp_mdi_write(sc->csr, sc->phy_primary_addr, FXP_DP83840_PCR,
967dccee1a1SDavid Greenman 		    fxp_mdi_read(sc->csr, sc->phy_primary_addr, FXP_DP83840_PCR) |
968dccee1a1SDavid Greenman 		    FXP_DP83840_PCR_LED4_MODE |	/* LED4 always indicates duplex */
969dccee1a1SDavid Greenman 		    FXP_DP83840_PCR_F_CONNECT |	/* force link disconnect bypass */
970dccee1a1SDavid Greenman 		    FXP_DP83840_PCR_BIT10);	/* XXX I have no idea */
971dccee1a1SDavid Greenman 	}
972dccee1a1SDavid Greenman 
973a17c678eSDavid Greenman 	ifp->if_flags |= IFF_RUNNING;
974a17c678eSDavid Greenman 	ifp->if_flags &= ~IFF_OACTIVE;
975a17c678eSDavid Greenman 	splx(s);
976a17c678eSDavid Greenman 
977a17c678eSDavid Greenman 	/*
978a17c678eSDavid Greenman 	 * Start stats updater.
979a17c678eSDavid Greenman 	 */
980a17c678eSDavid Greenman 	timeout(fxp_stats_update, sc, hz);
981a17c678eSDavid Greenman }
982a17c678eSDavid Greenman 
983a17c678eSDavid Greenman /*
984a17c678eSDavid Greenman  * Add a buffer to the end of the RFA buffer list.
985a17c678eSDavid Greenman  * Return 0 if successful, 1 for failure. A failure results in
986a17c678eSDavid Greenman  * adding the 'oldm' (if non-NULL) on to the end of the list -
987a17c678eSDavid Greenman  * tossing out it's old contents and recycling it.
988a17c678eSDavid Greenman  * The RFA struct is stuck at the beginning of mbuf cluster and the
989a17c678eSDavid Greenman  * data pointer is fixed up to point just past it.
990a17c678eSDavid Greenman  */
991a17c678eSDavid Greenman static int
992a17c678eSDavid Greenman fxp_add_rfabuf(sc, oldm)
993a17c678eSDavid Greenman 	struct fxp_softc *sc;
994a17c678eSDavid Greenman 	struct mbuf *oldm;
995a17c678eSDavid Greenman {
996a17c678eSDavid Greenman 	struct mbuf *m;
997a17c678eSDavid Greenman 	struct fxp_rfa *rfa, *p_rfa;
998a17c678eSDavid Greenman 
999a17c678eSDavid Greenman 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1000a17c678eSDavid Greenman 	if (m != NULL) {
1001a17c678eSDavid Greenman 		MCLGET(m, M_DONTWAIT);
1002a17c678eSDavid Greenman 		if ((m->m_flags & M_EXT) == 0) {
1003a17c678eSDavid Greenman 			m_freem(m);
1004eadd5e3aSDavid Greenman 			if (oldm == NULL)
1005eadd5e3aSDavid Greenman 				return 1;
1006a17c678eSDavid Greenman 			m = oldm;
1007eadd5e3aSDavid Greenman 			m->m_data = m->m_ext.ext_buf;
1008a17c678eSDavid Greenman 		}
1009a17c678eSDavid Greenman 	} else {
1010eadd5e3aSDavid Greenman 		if (oldm == NULL)
1011a17c678eSDavid Greenman 			return 1;
1012eadd5e3aSDavid Greenman 		m = oldm;
1013eadd5e3aSDavid Greenman 		m->m_data = m->m_ext.ext_buf;
1014eadd5e3aSDavid Greenman 	}
1015eadd5e3aSDavid Greenman 	/*
1016eadd5e3aSDavid Greenman 	 * Get a pointer to the base of the mbuf cluster and move
1017eadd5e3aSDavid Greenman 	 * data start past it.
1018eadd5e3aSDavid Greenman 	 */
1019a17c678eSDavid Greenman 	rfa = mtod(m, struct fxp_rfa *);
1020eadd5e3aSDavid Greenman 	m->m_data += sizeof(struct fxp_rfa);
1021eadd5e3aSDavid Greenman 	rfa->size = MCLBYTES - sizeof(struct fxp_rfa);
1022eadd5e3aSDavid Greenman 
1023a17c678eSDavid Greenman 	rfa->rfa_status = 0;
1024a17c678eSDavid Greenman 	rfa->rfa_control = FXP_RFA_CONTROL_EL;
1025a17c678eSDavid Greenman 	rfa->link_addr = -1;
1026a17c678eSDavid Greenman 	rfa->rbd_addr = -1;
1027a17c678eSDavid Greenman 	rfa->actual_size = 0;
1028dfe61cf1SDavid Greenman 	/*
1029dfe61cf1SDavid Greenman 	 * If there are other buffers already on the list, attach this
1030dfe61cf1SDavid Greenman 	 * one to the end by fixing up the tail to point to this one.
1031dfe61cf1SDavid Greenman 	 */
1032a17c678eSDavid Greenman 	if (sc->rfa_headm != NULL) {
1033dfe61cf1SDavid Greenman 		p_rfa = (struct fxp_rfa *) sc->rfa_tailm->m_ext.ext_buf;
1034a17c678eSDavid Greenman 		sc->rfa_tailm->m_next = m;
1035a17c678eSDavid Greenman 		p_rfa->link_addr = vtophys(rfa);
1036a17c678eSDavid Greenman 		p_rfa->rfa_control &= ~FXP_RFA_CONTROL_EL;
1037a17c678eSDavid Greenman 	} else {
1038a17c678eSDavid Greenman 		sc->rfa_headm = m;
1039a17c678eSDavid Greenman 	}
1040a17c678eSDavid Greenman 	sc->rfa_tailm = m;
1041a17c678eSDavid Greenman 
1042dfe61cf1SDavid Greenman 	return (m == oldm);
1043a17c678eSDavid Greenman }
1044a17c678eSDavid Greenman 
1045a17c678eSDavid Greenman static int
1046dccee1a1SDavid Greenman fxp_mdi_read(csr, phy, reg)
1047dccee1a1SDavid Greenman 	struct fxp_csr *csr;
1048dccee1a1SDavid Greenman 	int phy;
1049dccee1a1SDavid Greenman 	int reg;
1050dccee1a1SDavid Greenman {
1051dccee1a1SDavid Greenman 	int count = 10000;
1052dccee1a1SDavid Greenman 
1053dccee1a1SDavid Greenman 	csr->mdi_control = (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21);
1054dccee1a1SDavid Greenman 
1055dccee1a1SDavid Greenman 	while ((csr->mdi_control & 0x10000000) == 0 && count--)
1056dccee1a1SDavid Greenman 		DELAY(1);
1057dccee1a1SDavid Greenman 
1058dccee1a1SDavid Greenman 	if (count <= 0)
1059dccee1a1SDavid Greenman 		printf("fxp_mdi_read: timed out\n");
1060dccee1a1SDavid Greenman 
1061dccee1a1SDavid Greenman 	return (csr->mdi_control & 0xffff);
1062dccee1a1SDavid Greenman }
1063dccee1a1SDavid Greenman 
1064dccee1a1SDavid Greenman static void
1065dccee1a1SDavid Greenman fxp_mdi_write(csr, phy, reg, value)
1066dccee1a1SDavid Greenman 	struct fxp_csr *csr;
1067dccee1a1SDavid Greenman 	int phy;
1068dccee1a1SDavid Greenman 	int reg;
1069dccee1a1SDavid Greenman 	int value;
1070dccee1a1SDavid Greenman {
1071dccee1a1SDavid Greenman 	int count = 10000;
1072dccee1a1SDavid Greenman 
1073dccee1a1SDavid Greenman 	csr->mdi_control = (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21)
1074dccee1a1SDavid Greenman 	    | (value & 0xffff);
1075dccee1a1SDavid Greenman 
1076dccee1a1SDavid Greenman 	while ((csr->mdi_control & 10000000) == 0 && count--)
1077dccee1a1SDavid Greenman 		DELAY(1);
1078dccee1a1SDavid Greenman 
1079dccee1a1SDavid Greenman 	if (count <= 0)
1080dccee1a1SDavid Greenman 		printf("fxp_mdi_write: timed out\n");
1081dccee1a1SDavid Greenman }
1082dccee1a1SDavid Greenman 
1083dccee1a1SDavid Greenman static int
1084a17c678eSDavid Greenman fxp_ioctl(ifp, command, data)
1085a17c678eSDavid Greenman 	struct ifnet *ifp;
1086a17c678eSDavid Greenman 	int command;
1087a17c678eSDavid Greenman 	caddr_t data;
1088a17c678eSDavid Greenman {
1089a17c678eSDavid Greenman 	struct ifaddr *ifa = (struct ifaddr *) data;
10909b44ff22SGarrett Wollman 	struct fxp_softc *sc = ifp->if_softc;
1091a17c678eSDavid Greenman 	struct ifreq *ifr = (struct ifreq *) data;
1092a17c678eSDavid Greenman 	int s, error = 0;
1093a17c678eSDavid Greenman 
1094a17c678eSDavid Greenman 	s = splimp();
1095a17c678eSDavid Greenman 
1096a17c678eSDavid Greenman 	switch (command) {
1097a17c678eSDavid Greenman 
1098a17c678eSDavid Greenman 	case SIOCSIFADDR:
1099a17c678eSDavid Greenman 	case SIOCGIFADDR:
1100fb583156SDavid Greenman 	case SIOCSIFMTU:
1101fb583156SDavid Greenman 		error = ether_ioctl(ifp, command, data);
1102a17c678eSDavid Greenman 		break;
1103a17c678eSDavid Greenman 
1104a17c678eSDavid Greenman 	case SIOCSIFFLAGS:
1105a17c678eSDavid Greenman 
1106a17c678eSDavid Greenman 		/*
1107a17c678eSDavid Greenman 		 * If interface is marked up and not running, then start it.
1108a17c678eSDavid Greenman 		 * If it is marked down and running, stop it.
1109a17c678eSDavid Greenman 		 * XXX If it's up then re-initialize it. This is so flags
1110a17c678eSDavid Greenman 		 * such as IFF_PROMISC are handled.
1111a17c678eSDavid Greenman 		 */
1112a17c678eSDavid Greenman 		if (ifp->if_flags & IFF_UP) {
1113fb583156SDavid Greenman 			fxp_init(sc);
1114a17c678eSDavid Greenman 		} else {
1115a17c678eSDavid Greenman 			if (ifp->if_flags & IFF_RUNNING)
11164a5f1499SDavid Greenman 				fxp_stop(sc);
1117a17c678eSDavid Greenman 		}
1118a17c678eSDavid Greenman 		break;
1119a17c678eSDavid Greenman 
1120a17c678eSDavid Greenman 	case SIOCADDMULTI:
1121a17c678eSDavid Greenman 	case SIOCDELMULTI:
1122a17c678eSDavid Greenman 		/*
1123a17c678eSDavid Greenman 		 * Multicast list has changed; set the hardware filter
1124a17c678eSDavid Greenman 		 * accordingly.
1125a17c678eSDavid Greenman 		 */
1126fb583156SDavid Greenman 		fxp_init(sc);
1127a17c678eSDavid Greenman 		error = 0;
1128a17c678eSDavid Greenman 		break;
1129a17c678eSDavid Greenman 
1130a17c678eSDavid Greenman 	default:
1131a17c678eSDavid Greenman 		error = EINVAL;
1132a17c678eSDavid Greenman 	}
1133a17c678eSDavid Greenman 	(void) splx(s);
1134a17c678eSDavid Greenman 	return (error);
1135a17c678eSDavid Greenman }
1136