xref: /openbsd-src/sys/dev/usb/if_mue.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: if_mue.c,v 1.12 2024/05/23 03:21:08 jsg Exp $	*/
277395c04Skevlo 
377395c04Skevlo /*
477395c04Skevlo  * Copyright (c) 2018 Kevin Lo <kevlo@openbsd.org>
577395c04Skevlo  *
677395c04Skevlo  * Permission to use, copy, modify, and distribute this software for any
777395c04Skevlo  * purpose with or without fee is hereby granted, provided that the above
877395c04Skevlo  * copyright notice and this permission notice appear in all copies.
977395c04Skevlo  *
1077395c04Skevlo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1177395c04Skevlo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1277395c04Skevlo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1377395c04Skevlo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1477395c04Skevlo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1577395c04Skevlo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1677395c04Skevlo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1777395c04Skevlo  */
1877395c04Skevlo 
1977395c04Skevlo /* Driver for Microchip LAN7500/LAN7800 chipsets. */
2077395c04Skevlo 
2177395c04Skevlo #include "bpfilter.h"
2277395c04Skevlo 
2377395c04Skevlo #include <sys/param.h>
2477395c04Skevlo #include <sys/systm.h>
2577395c04Skevlo #include <sys/sockio.h>
2677395c04Skevlo #include <sys/rwlock.h>
2777395c04Skevlo #include <sys/mbuf.h>
2877395c04Skevlo 
2977395c04Skevlo #include <sys/device.h>
3077395c04Skevlo 
3177395c04Skevlo #include <machine/bus.h>
3277395c04Skevlo 
3377395c04Skevlo #include <net/if.h>
3477395c04Skevlo #include <net/if_dl.h>
3577395c04Skevlo #include <net/if_media.h>
3677395c04Skevlo 
3777395c04Skevlo #if NBPFILTER > 0
3877395c04Skevlo #include <net/bpf.h>
3977395c04Skevlo #endif
4077395c04Skevlo 
4177395c04Skevlo #include <netinet/in.h>
4277395c04Skevlo #include <netinet/if_ether.h>
4377395c04Skevlo 
4477395c04Skevlo #include <dev/mii/miivar.h>
4577395c04Skevlo 
4677395c04Skevlo #include <dev/usb/usb.h>
4777395c04Skevlo #include <dev/usb/usbdi.h>
4877395c04Skevlo #include <dev/usb/usbdi_util.h>
4977395c04Skevlo #include <dev/usb/usbdivar.h>
5077395c04Skevlo #include <dev/usb/usbdevs.h>
5177395c04Skevlo 
5277395c04Skevlo #include <dev/usb/if_muereg.h>
5377395c04Skevlo 
5477395c04Skevlo #ifdef MUE_DEBUG
5577395c04Skevlo #define DPRINTF(x)	do { if (muedebug) printf x; } while (0)
5677395c04Skevlo #define DPRINTFN(n,x)	do { if (muedebug >= (n)) printf x; } while (0)
5777395c04Skevlo int	muedebug = 0;
5877395c04Skevlo #else
5977395c04Skevlo #define DPRINTF(x)
6077395c04Skevlo #define DPRINTFN(n,x)
6177395c04Skevlo #endif
6277395c04Skevlo 
6377395c04Skevlo /*
6477395c04Skevlo  * Various supported device vendors/products.
6577395c04Skevlo  */
6677395c04Skevlo struct mue_type {
6777395c04Skevlo 	struct usb_devno	mue_dev;
6877395c04Skevlo 	uint16_t		mue_flags;
6977395c04Skevlo #define LAN7500	0x0001		/* LAN7500 */
7077395c04Skevlo };
7177395c04Skevlo 
7277395c04Skevlo const struct mue_type mue_devs[] = {
7377395c04Skevlo 	{ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN7500 }, LAN7500 },
7477395c04Skevlo 	{ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN7505 }, LAN7500 },
7577395c04Skevlo 	{ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN7800 }, 0 },
7677395c04Skevlo 	{ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN7801 }, 0 },
7777395c04Skevlo 	{ { USB_VENDOR_SMC2, USB_PRODUCT_SMC2_LAN7850 }, 0 }
7877395c04Skevlo };
7977395c04Skevlo 
8077395c04Skevlo #define mue_lookup(v, p)	((struct mue_type *)usb_lookup(mue_devs, v, p))
8177395c04Skevlo 
8277395c04Skevlo int	mue_match(struct device *, void *, void *);
8377395c04Skevlo void	mue_attach(struct device *, struct device *, void *);
8477395c04Skevlo int	mue_detach(struct device *, int);
8577395c04Skevlo 
8677395c04Skevlo struct cfdriver mue_cd = {
8777395c04Skevlo 	NULL, "mue", DV_IFNET
8877395c04Skevlo };
8977395c04Skevlo 
9077395c04Skevlo const struct cfattach mue_ca = {
9177395c04Skevlo 	sizeof(struct mue_softc), mue_match, mue_attach, mue_detach
9277395c04Skevlo };
9377395c04Skevlo 
9477395c04Skevlo uint32_t	mue_csr_read(struct mue_softc *, uint32_t);
9577395c04Skevlo int		mue_csr_write(struct mue_softc *, uint32_t, uint32_t);
9677395c04Skevlo 
9777395c04Skevlo void		mue_lock_mii(struct mue_softc *);
9877395c04Skevlo void		mue_unlock_mii(struct mue_softc *);
9977395c04Skevlo 
10077395c04Skevlo int		mue_mii_wait(struct mue_softc *);
10177395c04Skevlo int		mue_miibus_readreg(struct device *, int, int);
10277395c04Skevlo void		mue_miibus_writereg(struct device *, int, int, int);
10377395c04Skevlo void		mue_miibus_statchg(struct device *);
10477395c04Skevlo int		mue_ifmedia_upd(struct ifnet *);
10577395c04Skevlo void		mue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
10677395c04Skevlo 
10777395c04Skevlo int		mue_eeprom_wait(struct mue_softc *);
10877395c04Skevlo uint8_t		mue_eeprom_getbyte(struct mue_softc *, int, uint8_t *);
10977395c04Skevlo int		mue_read_eeprom(struct mue_softc *, caddr_t, int, int);
11077395c04Skevlo int		mue_dataport_wait(struct mue_softc *);
11177395c04Skevlo void		mue_dataport_write(struct mue_softc *, uint32_t, uint32_t,
11277395c04Skevlo 		    uint32_t, uint32_t *);
11377395c04Skevlo void		mue_init_ltm(struct mue_softc *);
11477395c04Skevlo int		mue_chip_init(struct mue_softc *);
11577395c04Skevlo void		mue_set_macaddr(struct mue_softc *);
11677395c04Skevlo 
11777395c04Skevlo int		mue_rx_list_init(struct mue_softc *);
11877395c04Skevlo int		mue_tx_list_init(struct mue_softc *);
11977395c04Skevlo int		mue_open_pipes(struct mue_softc *);
12077395c04Skevlo int		mue_encap(struct mue_softc *, struct mbuf *, int);
12177395c04Skevlo void		mue_iff(struct mue_softc *);
12277395c04Skevlo void		mue_rxeof(struct usbd_xfer *, void *, usbd_status);
12377395c04Skevlo void		mue_txeof(struct usbd_xfer *, void *, usbd_status);
12477395c04Skevlo 
12577395c04Skevlo void		mue_init(void *);
12677395c04Skevlo int		mue_ioctl(struct ifnet *, u_long, caddr_t);
12777395c04Skevlo void		mue_watchdog(struct ifnet *);
12877395c04Skevlo void		mue_reset(struct mue_softc *);
12977395c04Skevlo void		mue_start(struct ifnet *);
13077395c04Skevlo void		mue_stop(struct mue_softc *);
13177395c04Skevlo void		mue_tick(void *);
13277395c04Skevlo void		mue_tick_task(void *);
13377395c04Skevlo 
13477395c04Skevlo #define MUE_SETBIT(sc, reg, x)	\
13577395c04Skevlo 	mue_csr_write(sc, reg, mue_csr_read(sc, reg) | (x))
13677395c04Skevlo 
13777395c04Skevlo #define MUE_CLRBIT(sc, reg, x)	\
13877395c04Skevlo 	mue_csr_write(sc, reg, mue_csr_read(sc, reg) & ~(x))
13977395c04Skevlo 
140a20dd8f5Skevlo #if defined(__arm__) || defined(__arm64__)
141a20dd8f5Skevlo 
142a20dd8f5Skevlo #include <dev/ofw/openfirm.h>
143a20dd8f5Skevlo 
144a20dd8f5Skevlo void
mue_enaddr_OF(struct mue_softc * sc)145a20dd8f5Skevlo mue_enaddr_OF(struct mue_softc *sc)
146a20dd8f5Skevlo {
147a20dd8f5Skevlo 	char *device = "/axi/usb/hub/ethernet";
148a20dd8f5Skevlo 	char prop[64];
149a20dd8f5Skevlo 	int node;
150a20dd8f5Skevlo 
151a20dd8f5Skevlo 	if (sc->mue_dev.dv_unit != 0)
152a20dd8f5Skevlo 		return;
153a20dd8f5Skevlo 
154a20dd8f5Skevlo 	/* Get the Raspberry Pi MAC address from FDT. */
155a20dd8f5Skevlo 	if ((node = OF_finddevice("/aliases")) == -1)
156a20dd8f5Skevlo 		return;
157a20dd8f5Skevlo 	if (OF_getprop(node, "ethernet0", prop, sizeof(prop)) > 0 ||
158a20dd8f5Skevlo 	    OF_getprop(node, "ethernet", prop, sizeof(prop)) > 0)
159a20dd8f5Skevlo 		device = prop;
160a20dd8f5Skevlo 
161a20dd8f5Skevlo 	if ((node = OF_finddevice(device)) == -1)
162a20dd8f5Skevlo 		return;
163a20dd8f5Skevlo 	if (OF_getprop(node, "local-mac-address", sc->arpcom.ac_enaddr,
164a20dd8f5Skevlo 	    sizeof(sc->arpcom.ac_enaddr)) != sizeof(sc->arpcom.ac_enaddr)) {
165a20dd8f5Skevlo 		OF_getprop(node, "mac-address", sc->arpcom.ac_enaddr,
166a20dd8f5Skevlo 		    sizeof(sc->arpcom.ac_enaddr));
167a20dd8f5Skevlo 	}
168a20dd8f5Skevlo }
169a20dd8f5Skevlo #else
170a20dd8f5Skevlo #define mue_enaddr_OF(x) do {} while(0)
171a20dd8f5Skevlo #endif
172a20dd8f5Skevlo 
17377395c04Skevlo uint32_t
mue_csr_read(struct mue_softc * sc,uint32_t reg)17477395c04Skevlo mue_csr_read(struct mue_softc *sc, uint32_t reg)
17577395c04Skevlo {
17677395c04Skevlo 	usb_device_request_t req;
17777395c04Skevlo 	usbd_status err;
17877395c04Skevlo 	uDWord val;
17977395c04Skevlo 
18077395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
18177395c04Skevlo 		return (0);
18277395c04Skevlo 
18377395c04Skevlo 	USETDW(val, 0);
18477395c04Skevlo 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
18577395c04Skevlo 	req.bRequest = MUE_UR_READREG;
18677395c04Skevlo 	USETW(req.wValue, 0);
18777395c04Skevlo 	USETW(req.wIndex, reg);
18877395c04Skevlo 	USETW(req.wLength, 4);
18977395c04Skevlo 
19077395c04Skevlo 	err = usbd_do_request(sc->mue_udev, &req, &val);
19177395c04Skevlo 	if (err) {
19277395c04Skevlo 		DPRINTF(("%s: mue_csr_read: reg=0x%x err=%s\n",
19377395c04Skevlo 		    sc->mue_dev.dv_xname, reg, usbd_errstr(err)));
19477395c04Skevlo 		return (0);
19577395c04Skevlo 	}
19677395c04Skevlo 
19777395c04Skevlo 	return (UGETDW(val));
19877395c04Skevlo }
19977395c04Skevlo 
20077395c04Skevlo int
mue_csr_write(struct mue_softc * sc,uint32_t reg,uint32_t aval)20177395c04Skevlo mue_csr_write(struct mue_softc *sc, uint32_t reg, uint32_t aval)
20277395c04Skevlo {
20377395c04Skevlo 	usb_device_request_t req;
20477395c04Skevlo 	usbd_status err;
20577395c04Skevlo 	uDWord val;
20677395c04Skevlo 
20777395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
20877395c04Skevlo 		return (0);
20977395c04Skevlo 
21077395c04Skevlo 	USETDW(val, aval);
21177395c04Skevlo 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
21277395c04Skevlo 	req.bRequest = MUE_UR_WRITEREG;
21377395c04Skevlo 	USETW(req.wValue, 0);
21477395c04Skevlo 	USETW(req.wIndex, reg);
21577395c04Skevlo 	USETW(req.wLength, 4);
21677395c04Skevlo 
21777395c04Skevlo 	err = usbd_do_request(sc->mue_udev, &req, &val);
21877395c04Skevlo 	if (err) {
21977395c04Skevlo 		DPRINTF(("%s: mue_csr_write: reg=0x%x err=%s\n",
22077395c04Skevlo 		    sc->mue_dev.dv_xname, reg, usbd_errstr(err)));
22177395c04Skevlo 		return (-1);
22277395c04Skevlo 	}
22377395c04Skevlo 
22477395c04Skevlo 	return (0);
22577395c04Skevlo }
22677395c04Skevlo 
22777395c04Skevlo /*
22877395c04Skevlo  * Get exclusive access to the MII registers.
22977395c04Skevlo  */
23077395c04Skevlo void
mue_lock_mii(struct mue_softc * sc)23177395c04Skevlo mue_lock_mii(struct mue_softc *sc)
23277395c04Skevlo {
23377395c04Skevlo 	sc->mue_refcnt++;
23477395c04Skevlo 	rw_enter_write(&sc->mue_mii_lock);
23577395c04Skevlo }
23677395c04Skevlo 
23777395c04Skevlo void
mue_unlock_mii(struct mue_softc * sc)23877395c04Skevlo mue_unlock_mii(struct mue_softc *sc)
23977395c04Skevlo {
24077395c04Skevlo 	rw_exit_write(&sc->mue_mii_lock);
24177395c04Skevlo 	if (--sc->mue_refcnt < 0)
24277395c04Skevlo 		usb_detach_wakeup(&sc->mue_dev);
24377395c04Skevlo }
24477395c04Skevlo 
24577395c04Skevlo /*
24677395c04Skevlo  * Wait for the MII to become ready.
24777395c04Skevlo  */
24877395c04Skevlo int
mue_mii_wait(struct mue_softc * sc)24977395c04Skevlo mue_mii_wait(struct mue_softc *sc)
25077395c04Skevlo {
25177395c04Skevlo 	int ntries;
25277395c04Skevlo 
25377395c04Skevlo 	for (ntries = 0; ntries < 100; ntries++) {
25477395c04Skevlo 		if (!(mue_csr_read(sc, MUE_MII_ACCESS) & MUE_MII_ACCESS_BUSY))
25577395c04Skevlo 			return (0);
25677395c04Skevlo 		DELAY(5);
25777395c04Skevlo 	}
25877395c04Skevlo 
25977395c04Skevlo 	printf("%s: MII timed out\n", sc->mue_dev.dv_xname);
26077395c04Skevlo 	return (1);
26177395c04Skevlo }
26277395c04Skevlo 
26377395c04Skevlo int
mue_miibus_readreg(struct device * dev,int phy,int reg)26477395c04Skevlo mue_miibus_readreg(struct device *dev, int phy, int reg)
26577395c04Skevlo {
26677395c04Skevlo 	struct mue_softc *sc = (void *)dev;
26777395c04Skevlo 	uint32_t val;
26877395c04Skevlo 
26977395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
27077395c04Skevlo 		return (0);
27177395c04Skevlo 
27277395c04Skevlo 	if (sc->mue_phyno != phy)
27377395c04Skevlo 		return (0);
27477395c04Skevlo 
27577395c04Skevlo 	mue_lock_mii(sc);
27677395c04Skevlo 	if (mue_mii_wait(sc) != 0)
27777395c04Skevlo 		return (0);
27877395c04Skevlo 
27977395c04Skevlo 	mue_csr_write(sc, MUE_MII_ACCESS, MUE_MII_ACCESS_READ |
28077395c04Skevlo 	    MUE_MII_ACCESS_BUSY | MUE_MII_ACCESS_REGADDR(reg) |
28177395c04Skevlo 	    MUE_MII_ACCESS_PHYADDR(phy));
28277395c04Skevlo 
28377395c04Skevlo 	if (mue_mii_wait(sc) != 0)
28477395c04Skevlo 		printf("%s: MII read timed out\n", sc->mue_dev.dv_xname);
28577395c04Skevlo 
28677395c04Skevlo 	val = mue_csr_read(sc, MUE_MII_DATA);
28777395c04Skevlo 	mue_unlock_mii(sc);
28877395c04Skevlo 	return (val & 0xffff);
28977395c04Skevlo }
29077395c04Skevlo 
29177395c04Skevlo void
mue_miibus_writereg(struct device * dev,int phy,int reg,int data)29277395c04Skevlo mue_miibus_writereg(struct device *dev, int phy, int reg, int data)
29377395c04Skevlo {
29477395c04Skevlo 	struct mue_softc *sc = (void *)dev;
29577395c04Skevlo 
29677395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
29777395c04Skevlo 		return;
29877395c04Skevlo 
29977395c04Skevlo 	if (sc->mue_phyno != phy)
30077395c04Skevlo 		return;
30177395c04Skevlo 
30277395c04Skevlo 	mue_lock_mii(sc);
30377395c04Skevlo 	if (mue_mii_wait(sc) != 0)
30477395c04Skevlo 		return;
30577395c04Skevlo 
30677395c04Skevlo 	mue_csr_write(sc, MUE_MII_DATA, data);
30777395c04Skevlo 	mue_csr_write(sc, MUE_MII_ACCESS, MUE_MII_ACCESS_WRITE |
30877395c04Skevlo 	    MUE_MII_ACCESS_BUSY | MUE_MII_ACCESS_REGADDR(reg) |
30977395c04Skevlo 	    MUE_MII_ACCESS_PHYADDR(phy));
31077395c04Skevlo 
31177395c04Skevlo 	if (mue_mii_wait(sc) != 0)
31277395c04Skevlo 		printf("%s: MII write timed out\n", sc->mue_dev.dv_xname);
31377395c04Skevlo 
31477395c04Skevlo 	mue_unlock_mii(sc);
31577395c04Skevlo }
31677395c04Skevlo 
31777395c04Skevlo void
mue_miibus_statchg(struct device * dev)31877395c04Skevlo mue_miibus_statchg(struct device *dev)
31977395c04Skevlo {
32077395c04Skevlo 	struct mue_softc *sc = (void *)dev;
32177395c04Skevlo 	struct mii_data *mii = GET_MII(sc);
32277395c04Skevlo 	struct ifnet *ifp = GET_IFP(sc);
32377395c04Skevlo 	uint32_t flow, threshold;
32477395c04Skevlo 
32577395c04Skevlo 	if (mii == NULL || ifp == NULL ||
32677395c04Skevlo 	    (ifp->if_flags & IFF_RUNNING) == 0)
32777395c04Skevlo 		return;
32877395c04Skevlo 
32977395c04Skevlo 	sc->mue_link = 0;
33077395c04Skevlo 	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
33177395c04Skevlo 	    (IFM_ACTIVE | IFM_AVALID)) {
33277395c04Skevlo 		switch (IFM_SUBTYPE(mii->mii_media_active)) {
33377395c04Skevlo 		case IFM_10_T:
33477395c04Skevlo 		case IFM_100_TX:
33577395c04Skevlo 		case IFM_1000_T:
33677395c04Skevlo 			sc->mue_link++;
33777395c04Skevlo 			break;
33877395c04Skevlo 		default:
33977395c04Skevlo 			break;
34077395c04Skevlo 		}
34177395c04Skevlo 	}
34277395c04Skevlo 
34377395c04Skevlo 	/* Lost link, do nothing. */
34477395c04Skevlo 	if (sc->mue_link == 0)
34577395c04Skevlo 		return;
34677395c04Skevlo 
34777395c04Skevlo 	if (!(sc->mue_flags & LAN7500)) {
34877395c04Skevlo 		if (sc->mue_udev->speed == USB_SPEED_SUPER) {
34977395c04Skevlo 			if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) {
35077395c04Skevlo 				/* Disable U2 and enable U1. */
35177395c04Skevlo 				MUE_CLRBIT(sc, MUE_USB_CFG1,
35277395c04Skevlo 				    MUE_USB_CFG1_DEV_U2_INIT_EN);
35377395c04Skevlo 				MUE_SETBIT(sc, MUE_USB_CFG1,
35477395c04Skevlo 				    MUE_USB_CFG1_DEV_U1_INIT_EN);
35577395c04Skevlo 			} else {
35677395c04Skevlo 				/* Enable U1 and U2. */
35777395c04Skevlo 				MUE_SETBIT(sc, MUE_USB_CFG1,
35877395c04Skevlo 				    MUE_USB_CFG1_DEV_U1_INIT_EN |
35977395c04Skevlo 				    MUE_USB_CFG1_DEV_U2_INIT_EN);
36077395c04Skevlo 			}
36177395c04Skevlo 		}
36277395c04Skevlo 	}
36377395c04Skevlo 
364e64b3f5dSjsg 	threshold = 0;
36577395c04Skevlo 	flow = 0;
36677395c04Skevlo 	if (IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) {
36777395c04Skevlo 		if (IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) {
36877395c04Skevlo 			flow |= MUE_FLOW_TX_FCEN | MUE_FLOW_PAUSE_TIME;
36977395c04Skevlo 
37077395c04Skevlo 			/* XXX magic numbers come from Linux driver. */
37177395c04Skevlo 			if (sc->mue_flags & LAN7500) {
37277395c04Skevlo 				threshold = 0x820;
37377395c04Skevlo 			} else {
37477395c04Skevlo 				threshold =
37577395c04Skevlo 				    (sc->mue_udev->speed == USB_SPEED_SUPER) ?
37677395c04Skevlo 				    0x817 : 0x211;
37777395c04Skevlo 			}
37877395c04Skevlo 		}
37977395c04Skevlo 		if (IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE)
38077395c04Skevlo 			flow |= MUE_FLOW_RX_FCEN;
38177395c04Skevlo 	}
38277395c04Skevlo 	mue_csr_write(sc, (sc->mue_flags & LAN7500) ?
38377395c04Skevlo 	    MUE_FCT_FLOW : MUE_7800_FCT_FLOW, threshold);
38477395c04Skevlo 
38577395c04Skevlo 	/* Threshold value should be set before enabling flow. */
38677395c04Skevlo 	mue_csr_write(sc, MUE_FLOW, flow);
38777395c04Skevlo }
38877395c04Skevlo 
38977395c04Skevlo /*
39077395c04Skevlo  * Set media options.
39177395c04Skevlo  */
39277395c04Skevlo int
mue_ifmedia_upd(struct ifnet * ifp)39377395c04Skevlo mue_ifmedia_upd(struct ifnet *ifp)
39477395c04Skevlo {
39577395c04Skevlo 	struct mue_softc *sc = ifp->if_softc;
39677395c04Skevlo 	struct mii_data *mii = GET_MII(sc);
39777395c04Skevlo 
39877395c04Skevlo 	if (mii->mii_instance) {
39977395c04Skevlo 		struct mii_softc *miisc;
40077395c04Skevlo 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
40177395c04Skevlo 			mii_phy_reset(miisc);
40277395c04Skevlo 	}
40377395c04Skevlo 	return (mii_mediachg(mii));
40477395c04Skevlo }
40577395c04Skevlo 
40677395c04Skevlo /*
40777395c04Skevlo  * Report current media status.
40877395c04Skevlo  */
40977395c04Skevlo void
mue_ifmedia_sts(struct ifnet * ifp,struct ifmediareq * ifmr)41077395c04Skevlo mue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
41177395c04Skevlo {
41277395c04Skevlo 	struct mue_softc *sc = ifp->if_softc;
41377395c04Skevlo 	struct mii_data *mii = GET_MII(sc);
41477395c04Skevlo 
41577395c04Skevlo 	mii_pollstat(mii);
41677395c04Skevlo 	ifmr->ifm_active = mii->mii_media_active;
41777395c04Skevlo 	ifmr->ifm_status = mii->mii_media_status;
41877395c04Skevlo }
41977395c04Skevlo 
42077395c04Skevlo int
mue_eeprom_wait(struct mue_softc * sc)42177395c04Skevlo mue_eeprom_wait(struct mue_softc *sc)
42277395c04Skevlo {
42377395c04Skevlo 	uint32_t val;
42477395c04Skevlo 	int ntries;
42577395c04Skevlo 
42677395c04Skevlo 	for (ntries = 0; ntries < 100; ntries++) {
42777395c04Skevlo 		val = mue_csr_read(sc, MUE_E2P_CMD);
42877395c04Skevlo 		if (!(val & MUE_E2P_CMD_BUSY) || (val & MUE_E2P_CMD_TIMEOUT))
42977395c04Skevlo 			return (0);
43077395c04Skevlo 		DELAY(5);
43177395c04Skevlo 	}
43277395c04Skevlo 
43377395c04Skevlo 	return (1);
43477395c04Skevlo }
43577395c04Skevlo 
43677395c04Skevlo uint8_t
mue_eeprom_getbyte(struct mue_softc * sc,int addr,uint8_t * dest)43777395c04Skevlo mue_eeprom_getbyte(struct mue_softc *sc, int addr, uint8_t *dest)
43877395c04Skevlo {
43977395c04Skevlo 	uint32_t byte = 0;
44077395c04Skevlo 	int ntries;
44177395c04Skevlo 
44277395c04Skevlo 	for (ntries = 0; ntries < 100; ntries++) {
44377395c04Skevlo 		if (!(mue_csr_read(sc, MUE_E2P_CMD) & MUE_E2P_CMD_BUSY))
44477395c04Skevlo 			break;
44577395c04Skevlo 		DELAY(5);
44677395c04Skevlo 	}
44777395c04Skevlo 
44877395c04Skevlo 	if (ntries == 100) {
44977395c04Skevlo 		printf("%s: EEPROM failed to come ready\n",
45077395c04Skevlo 		    sc->mue_dev.dv_xname);
45177395c04Skevlo 		return (ETIMEDOUT);
45277395c04Skevlo 	}
45377395c04Skevlo 
45477395c04Skevlo 	mue_csr_write(sc, MUE_E2P_CMD, MUE_E2P_CMD_READ | MUE_E2P_CMD_BUSY |
45577395c04Skevlo 	    (addr & MUE_E2P_CMD_ADDR_MASK));
45677395c04Skevlo 
45777395c04Skevlo 	if (mue_eeprom_wait(sc) != 0) {
45877395c04Skevlo 		printf("%s: EEPROM read timed out\n", sc->mue_dev.dv_xname);
45977395c04Skevlo 		return (ETIMEDOUT);
46077395c04Skevlo 	}
46177395c04Skevlo 
46277395c04Skevlo 	byte = mue_csr_read(sc, MUE_E2P_DATA);
46377395c04Skevlo 	*dest = byte & 0xff;
46477395c04Skevlo 
46577395c04Skevlo 	return (0);
46677395c04Skevlo }
46777395c04Skevlo 
46877395c04Skevlo int
mue_read_eeprom(struct mue_softc * sc,caddr_t dest,int off,int cnt)46977395c04Skevlo mue_read_eeprom(struct mue_softc *sc, caddr_t dest, int off, int cnt)
47077395c04Skevlo {
47177395c04Skevlo 	uint32_t val;
47277395c04Skevlo 	uint8_t byte = 0;
47377395c04Skevlo 	int i, err = 0;
47477395c04Skevlo 
47577395c04Skevlo 	/*
47677395c04Skevlo 	 * EEPROM pins are muxed with the LED function on LAN7800 device.
47777395c04Skevlo 	 */
47877395c04Skevlo 	val = mue_csr_read(sc, MUE_HW_CFG);
47977395c04Skevlo 	if (sc->mue_product == USB_PRODUCT_SMC2_LAN7800) {
48077395c04Skevlo 		MUE_CLRBIT(sc, MUE_HW_CFG,
48177395c04Skevlo 		    MUE_HW_CFG_LED0_EN | MUE_HW_CFG_LED1_EN);
48277395c04Skevlo 	}
48377395c04Skevlo 
48477395c04Skevlo 	for (i = 0; i < cnt; i++) {
48577395c04Skevlo 		err = mue_eeprom_getbyte(sc, off + i, &byte);
48677395c04Skevlo 		if (err)
48777395c04Skevlo 			break;
48877395c04Skevlo 		*(dest + i) = byte;
48977395c04Skevlo 	}
49077395c04Skevlo 
49177395c04Skevlo 	if (sc->mue_product == USB_PRODUCT_SMC2_LAN7800)
49277395c04Skevlo 		mue_csr_write(sc, MUE_HW_CFG, val);
49377395c04Skevlo 
49477395c04Skevlo 	return (err ? 1 : 0);
49577395c04Skevlo }
49677395c04Skevlo 
49777395c04Skevlo int
mue_dataport_wait(struct mue_softc * sc)49877395c04Skevlo mue_dataport_wait(struct mue_softc *sc)
49977395c04Skevlo {
50077395c04Skevlo 	int ntries;
50177395c04Skevlo 
50277395c04Skevlo 	for (ntries = 0; ntries < 100; ntries++) {
50377395c04Skevlo 		if (mue_csr_read(sc, MUE_DP_SEL) & MUE_DP_SEL_DPRDY)
50477395c04Skevlo 			return (0);
50577395c04Skevlo 		DELAY(5);
50677395c04Skevlo 	}
50777395c04Skevlo 
50877395c04Skevlo 	printf("%s: dataport timed out\n", sc->mue_dev.dv_xname);
50977395c04Skevlo 	return (1);
51077395c04Skevlo }
51177395c04Skevlo 
51277395c04Skevlo void
mue_dataport_write(struct mue_softc * sc,uint32_t sel,uint32_t addr,uint32_t cnt,uint32_t * data)51377395c04Skevlo mue_dataport_write(struct mue_softc *sc, uint32_t sel, uint32_t addr,
51477395c04Skevlo     uint32_t cnt, uint32_t *data)
51577395c04Skevlo {
51677395c04Skevlo 	int i;
51777395c04Skevlo 
51877395c04Skevlo 	if (mue_dataport_wait(sc) != 0)
51977395c04Skevlo 		return;
52077395c04Skevlo 
52177395c04Skevlo 	mue_csr_write(sc, MUE_DP_SEL,
52277395c04Skevlo 	    (mue_csr_read(sc, MUE_DP_SEL) & ~MUE_DP_SEL_RSEL_MASK) | sel);
52377395c04Skevlo 
52477395c04Skevlo 	for (i = 0; i < cnt; i++) {
52577395c04Skevlo 		mue_csr_write(sc, MUE_DP_ADDR, addr + i);
52677395c04Skevlo 		mue_csr_write(sc, MUE_DP_DATA, data[i]);
52777395c04Skevlo 		mue_csr_write(sc, MUE_DP_CMD, MUE_DP_CMD_WRITE);
52877395c04Skevlo 		if (mue_dataport_wait(sc) != 0)
52977395c04Skevlo 			return;
53077395c04Skevlo 	}
53177395c04Skevlo }
53277395c04Skevlo 
53377395c04Skevlo void
mue_init_ltm(struct mue_softc * sc)53477395c04Skevlo mue_init_ltm(struct mue_softc *sc)
53577395c04Skevlo {
53677395c04Skevlo 	uint8_t idx[6] = { 0 };
53777395c04Skevlo 	int i;
53877395c04Skevlo 
53977395c04Skevlo 	if (mue_csr_read(sc, MUE_USB_CFG1) & MUE_USB_CFG1_LTM_ENABLE) {
54077395c04Skevlo 		uint8_t temp[2];
54177395c04Skevlo 
54277395c04Skevlo 		if (mue_read_eeprom(sc, (caddr_t)&temp, MUE_EE_LTM_OFFSET, 2)) {
54377395c04Skevlo 			if (temp[0] != 24)
54477395c04Skevlo 				goto done;
54577395c04Skevlo 			mue_read_eeprom(sc, (caddr_t)&idx, temp[1] << 1, 24);
54677395c04Skevlo 		}
54777395c04Skevlo 	}
54877395c04Skevlo done:
54977395c04Skevlo 	for (i = 0; i < sizeof(idx); i++)
55077395c04Skevlo 		mue_csr_write(sc, MUE_LTM_INDEX(i), idx[i]);
55177395c04Skevlo }
55277395c04Skevlo 
55377395c04Skevlo int
mue_chip_init(struct mue_softc * sc)55477395c04Skevlo mue_chip_init(struct mue_softc *sc)
55577395c04Skevlo {
55677395c04Skevlo 	uint32_t val;
55777395c04Skevlo 	int ntries;
55877395c04Skevlo 
55977395c04Skevlo 	if (sc->mue_flags & LAN7500) {
56077395c04Skevlo 		for (ntries = 0; ntries < 100; ntries++) {
56177395c04Skevlo 			if (mue_csr_read(sc, MUE_PMT_CTL) & MUE_PMT_CTL_READY)
56277395c04Skevlo 				break;
56377395c04Skevlo 			DELAY(1000);	/* 1 msec */
56477395c04Skevlo 		}
56577395c04Skevlo 		if (ntries == 100) {
56677395c04Skevlo 			printf("%s: timeout waiting for device ready\n",
56777395c04Skevlo 			    sc->mue_dev.dv_xname);
56877395c04Skevlo 			return (ETIMEDOUT);
56977395c04Skevlo 		}
57077395c04Skevlo 	}
57177395c04Skevlo 
57277395c04Skevlo 	MUE_SETBIT(sc, MUE_HW_CFG, MUE_HW_CFG_LRST);
57377395c04Skevlo 
57477395c04Skevlo 	for (ntries = 0; ntries < 1000; ntries++) {
57577395c04Skevlo 		if (!(mue_csr_read(sc, MUE_HW_CFG) & MUE_HW_CFG_LRST))
57677395c04Skevlo 			break;
57777395c04Skevlo 		DELAY(5);
57877395c04Skevlo 	}
57977395c04Skevlo 	if (ntries == 1000) {
58077395c04Skevlo 		printf("%s: timeout on lite software reset\n",
58177395c04Skevlo 		    sc->mue_dev.dv_xname);
58277395c04Skevlo 		return (ETIMEDOUT);
58377395c04Skevlo 	}
58477395c04Skevlo 
58577395c04Skevlo 	/* Respond to the IN token with a NAK. */
58677395c04Skevlo 	if (sc->mue_flags & LAN7500)
58777395c04Skevlo 		MUE_SETBIT(sc, MUE_HW_CFG, MUE_HW_CFG_BIR);
58877395c04Skevlo 	else
58977395c04Skevlo 		MUE_SETBIT(sc, MUE_USB_CFG0, MUE_USB_CFG0_BIR);
59077395c04Skevlo 
59177395c04Skevlo 	if (sc->mue_flags & LAN7500) {
59277395c04Skevlo 		mue_csr_write(sc, MUE_BURST_CAP,
59377395c04Skevlo 		    (sc->mue_udev->speed == USB_SPEED_HIGH) ?
59477395c04Skevlo 		    MUE_BURST_MIN_BUFSZ : MUE_BURST_MAX_BUFSZ);
59577395c04Skevlo 		mue_csr_write(sc, MUE_BULK_IN_DELAY, MUE_DEFAULT_BULKIN_DELAY);
59677395c04Skevlo 
59777395c04Skevlo 		MUE_SETBIT(sc, MUE_HW_CFG, MUE_HW_CFG_BCE | MUE_HW_CFG_MEF);
59877395c04Skevlo 
59977395c04Skevlo 		/* Set undocumented FIFO sizes. */
60077395c04Skevlo 		mue_csr_write(sc, MUE_FCT_RX_FIFO_END, 0x27);
60177395c04Skevlo 		mue_csr_write(sc, MUE_FCT_TX_FIFO_END, 0x17);
60277395c04Skevlo 	} else {
60377395c04Skevlo 		/* Init LTM. */
60477395c04Skevlo 		mue_init_ltm(sc);
60577395c04Skevlo 
60677395c04Skevlo 		val = (sc->mue_udev->speed == USB_SPEED_SUPER) ?
60777395c04Skevlo 		    MUE_7800_BURST_MIN_BUFSZ : MUE_7800_BURST_MAX_BUFSZ;
60877395c04Skevlo 		mue_csr_write(sc, MUE_7800_BURST_CAP, val);
60977395c04Skevlo 		mue_csr_write(sc, MUE_7800_BULK_IN_DELAY,
61077395c04Skevlo 		    MUE_7800_DEFAULT_BULKIN_DELAY);
61177395c04Skevlo 
61277395c04Skevlo 		MUE_SETBIT(sc, MUE_HW_CFG, MUE_HW_CFG_MEF);
61377395c04Skevlo 		MUE_SETBIT(sc, MUE_USB_CFG0, MUE_USB_CFG0_BCE);
61477395c04Skevlo 	}
61577395c04Skevlo 
61677395c04Skevlo 	mue_csr_write(sc, MUE_INT_STATUS, 0xffffffff);
61777395c04Skevlo 	mue_csr_write(sc, (sc->mue_flags & LAN7500) ?
61877395c04Skevlo 	    MUE_FCT_FLOW : MUE_7800_FCT_FLOW, 0);
61977395c04Skevlo 	mue_csr_write(sc, MUE_FLOW, 0);
62077395c04Skevlo 
62177395c04Skevlo 	/* Reset PHY. */
62277395c04Skevlo 	MUE_SETBIT(sc, MUE_PMT_CTL, MUE_PMT_CTL_PHY_RST);
62377395c04Skevlo 	for (ntries = 0; ntries < 100; ntries++) {
62477395c04Skevlo 		val = mue_csr_read(sc, MUE_PMT_CTL);
62577395c04Skevlo 		if (!(val & MUE_PMT_CTL_PHY_RST) && (val & MUE_PMT_CTL_READY))
62677395c04Skevlo 			break;
62777395c04Skevlo 		DELAY(10000);
62877395c04Skevlo 	}
62977395c04Skevlo 	if (ntries == 100) {
63077395c04Skevlo 		printf("%s: timeout waiting for PHY reset\n",
63177395c04Skevlo 		    sc->mue_dev.dv_xname);
63277395c04Skevlo 		return (ETIMEDOUT);
63377395c04Skevlo 	}
63477395c04Skevlo 
63577395c04Skevlo 	/* LAN7801 only has RGMII mode. */
63677395c04Skevlo 	if (sc->mue_product == USB_PRODUCT_SMC2_LAN7801)
63777395c04Skevlo 		MUE_CLRBIT(sc, MUE_MAC_CR, MUE_MAC_CR_GMII_EN);
63877395c04Skevlo 
639a20dd8f5Skevlo 	if (sc->mue_flags & LAN7500 || !sc->mue_eeprom_present) {
64077395c04Skevlo 		/* Allow MAC to detect speed and duplex from PHY. */
64177395c04Skevlo 		MUE_SETBIT(sc, MUE_MAC_CR, MUE_MAC_CR_AUTO_SPEED |
64277395c04Skevlo 		    MUE_MAC_CR_AUTO_DUPLEX);
64377395c04Skevlo 	}
64477395c04Skevlo 
64577395c04Skevlo 	MUE_SETBIT(sc, MUE_MAC_TX, MUE_MAC_TX_TXEN);
64677395c04Skevlo 	MUE_SETBIT(sc, (sc->mue_flags & LAN7500) ?
64777395c04Skevlo 	    MUE_FCT_TX_CTL : MUE_7800_FCT_TX_CTL, MUE_FCT_TX_CTL_EN);
64877395c04Skevlo 
64977395c04Skevlo 	/* Set the maximum frame size. */
65077395c04Skevlo 	MUE_CLRBIT(sc, MUE_MAC_RX, MUE_MAC_RX_RXEN);
65177395c04Skevlo 	MUE_SETBIT(sc, MUE_MAC_RX, MUE_MAC_RX_MAX_LEN(ETHER_MAX_LEN));
65277395c04Skevlo 	MUE_SETBIT(sc, MUE_MAC_RX, MUE_MAC_RX_RXEN);
65377395c04Skevlo 
65477395c04Skevlo 	MUE_SETBIT(sc, (sc->mue_flags & LAN7500) ?
65577395c04Skevlo 	    MUE_FCT_RX_CTL : MUE_7800_FCT_RX_CTL, MUE_FCT_RX_CTL_EN);
65677395c04Skevlo 
65743a723fbSmglocker 	/* Enable LEDs. */
65843a723fbSmglocker 	if (sc->mue_product == USB_PRODUCT_SMC2_LAN7800 &&
65943a723fbSmglocker 	    sc->mue_eeprom_present == 0) {
66043a723fbSmglocker 		MUE_SETBIT(sc, MUE_HW_CFG,
66143a723fbSmglocker 		    MUE_HW_CFG_LED0_EN | MUE_HW_CFG_LED1_EN);
66243a723fbSmglocker 	}
66343a723fbSmglocker 
66477395c04Skevlo 	return (0);
66577395c04Skevlo }
66677395c04Skevlo 
66777395c04Skevlo void
mue_set_macaddr(struct mue_softc * sc)66877395c04Skevlo mue_set_macaddr(struct mue_softc *sc)
66977395c04Skevlo {
67077395c04Skevlo 	struct ifnet *ifp = &sc->arpcom.ac_if;
67177395c04Skevlo 	const uint8_t *eaddr = LLADDR(ifp->if_sadl);
67277395c04Skevlo 	uint32_t val, reg;
67377395c04Skevlo 
67477395c04Skevlo 	reg = (sc->mue_flags & LAN7500) ? MUE_ADDR_FILTX : MUE_7800_ADDR_FILTX;
67577395c04Skevlo 
67677395c04Skevlo 	val = (eaddr[3] << 24) | (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0];
67777395c04Skevlo 	mue_csr_write(sc, MUE_RX_ADDRL, val);
67877395c04Skevlo 	mue_csr_write(sc, reg + 4, val);
67977395c04Skevlo 	val = (eaddr[5] << 8) | eaddr[4];
68077395c04Skevlo 	mue_csr_write(sc, MUE_RX_ADDRH, val);
68177395c04Skevlo 	mue_csr_write(sc, reg, val | MUE_ADDR_FILTX_VALID);
68277395c04Skevlo }
68377395c04Skevlo 
68477395c04Skevlo /*
68577395c04Skevlo  * Probe for a Microchip chip.
68677395c04Skevlo  */
68777395c04Skevlo int
mue_match(struct device * parent,void * match,void * aux)68877395c04Skevlo mue_match(struct device *parent, void *match, void *aux)
68977395c04Skevlo {
69077395c04Skevlo 	struct usb_attach_arg *uaa = aux;
69177395c04Skevlo 
69277395c04Skevlo 	if (uaa->iface == NULL || uaa->configno != 1)
69377395c04Skevlo 		return (UMATCH_NONE);
69477395c04Skevlo 
69577395c04Skevlo 	return (mue_lookup(uaa->vendor, uaa->product) != NULL ?
69677395c04Skevlo 	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE);
69777395c04Skevlo }
69877395c04Skevlo 
69977395c04Skevlo void
mue_attach(struct device * parent,struct device * self,void * aux)70077395c04Skevlo mue_attach(struct device *parent, struct device *self, void *aux)
70177395c04Skevlo {
70277395c04Skevlo 	struct mue_softc *sc = (struct mue_softc *)self;
70377395c04Skevlo 	struct usb_attach_arg *uaa = aux;
70477395c04Skevlo 	usb_interface_descriptor_t *id;
70577395c04Skevlo 	usb_endpoint_descriptor_t *ed;
70677395c04Skevlo 	struct mii_data	*mii;
70777395c04Skevlo 	struct ifnet *ifp;
70877395c04Skevlo 	int i, s;
70977395c04Skevlo 
71077395c04Skevlo 	sc->mue_udev = uaa->device;
71177395c04Skevlo 	sc->mue_iface = uaa->iface;
71277395c04Skevlo 	sc->mue_product = uaa->product;
71377395c04Skevlo 	sc->mue_flags = mue_lookup(uaa->vendor, uaa->product)->mue_flags;
71477395c04Skevlo 
71577395c04Skevlo 	usb_init_task(&sc->mue_tick_task, mue_tick_task, sc,
71677395c04Skevlo 	    USB_TASK_TYPE_GENERIC);
71777395c04Skevlo 	rw_init(&sc->mue_mii_lock, "muemii");
71877395c04Skevlo 	usb_init_task(&sc->mue_stop_task, (void (*)(void *))mue_stop, sc,
71977395c04Skevlo 	    USB_TASK_TYPE_GENERIC);
72077395c04Skevlo 
72177395c04Skevlo 	/* Decide on what our bufsize will be. */
72277395c04Skevlo 	if (sc->mue_flags & LAN7500)
72377395c04Skevlo 		sc->mue_bufsz = (sc->mue_udev->speed == USB_SPEED_HIGH) ?
72477395c04Skevlo 		    MUE_MAX_BUFSZ : MUE_MIN_BUFSZ;
72577395c04Skevlo 	else
72677395c04Skevlo 		sc->mue_bufsz = MUE_7800_BUFSZ;
72777395c04Skevlo 
72877395c04Skevlo 	/* Find endpoints. */
72977395c04Skevlo 	id = usbd_get_interface_descriptor(sc->mue_iface);
73077395c04Skevlo 	for (i = 0; i < id->bNumEndpoints; i++) {
73177395c04Skevlo 		ed = usbd_interface2endpoint_descriptor(sc->mue_iface, i);
73277395c04Skevlo 		if (ed == NULL) {
73377395c04Skevlo 			printf("%s: couldn't get ep %d\n",
73477395c04Skevlo 			    sc->mue_dev.dv_xname, i);
73577395c04Skevlo 			return;
73677395c04Skevlo 		}
73777395c04Skevlo 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
73877395c04Skevlo 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
73977395c04Skevlo 			sc->mue_ed[MUE_ENDPT_RX] = ed->bEndpointAddress;
74077395c04Skevlo 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
74177395c04Skevlo 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
74277395c04Skevlo 			sc->mue_ed[MUE_ENDPT_TX] = ed->bEndpointAddress;
74377395c04Skevlo 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
74477395c04Skevlo 			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
74577395c04Skevlo 			sc->mue_ed[MUE_ENDPT_INTR] = ed->bEndpointAddress;
74677395c04Skevlo 		}
74777395c04Skevlo 	}
74877395c04Skevlo 
74977395c04Skevlo 	s = splnet();
75077395c04Skevlo 
75177395c04Skevlo 	sc->mue_phyno = 1;
75277395c04Skevlo 
753a20dd8f5Skevlo 	/* Check if the EEPROM programmed indicator is present. */
754a20dd8f5Skevlo 	mue_read_eeprom(sc, (caddr_t)&i, MUE_EE_IND_OFFSET, 1);
755a20dd8f5Skevlo 	sc->mue_eeprom_present = (i == MUE_EEPROM_INDICATOR) ? 1 : 0;
756a20dd8f5Skevlo 
75777395c04Skevlo 	if (mue_chip_init(sc) != 0) {
75877395c04Skevlo 		printf("%s: chip initialization failed\n",
75977395c04Skevlo 		    sc->mue_dev.dv_xname);
76077395c04Skevlo 		splx(s);
76177395c04Skevlo 		return;
76277395c04Skevlo 	}
76377395c04Skevlo 
76477395c04Skevlo 	/* Get station address from the EEPROM. */
765a20dd8f5Skevlo 	if (sc->mue_eeprom_present) {
76677395c04Skevlo 		if (mue_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr,
76777395c04Skevlo 		    MUE_EE_MAC_OFFSET, ETHER_ADDR_LEN)) {
76877395c04Skevlo 			printf("%s: failed to read station address\n",
76977395c04Skevlo 			    sc->mue_dev.dv_xname);
77077395c04Skevlo 			splx(s);
77177395c04Skevlo 			return;
77277395c04Skevlo 		}
773a20dd8f5Skevlo 	} else
774a20dd8f5Skevlo 		mue_enaddr_OF(sc);
77577395c04Skevlo 
77677395c04Skevlo 	/* A Microchip chip was detected.  Inform the world. */
77777395c04Skevlo 	printf("%s:", sc->mue_dev.dv_xname);
77877395c04Skevlo 	if (sc->mue_flags & LAN7500)
77977395c04Skevlo 		printf(" LAN7500");
78077395c04Skevlo 	else
78177395c04Skevlo 		printf(" LAN7800");
78277395c04Skevlo 	printf(", address %s\n", ether_sprintf(sc->arpcom.ac_enaddr));
78377395c04Skevlo 
78477395c04Skevlo 	/* Initialize interface info.*/
78577395c04Skevlo 	ifp = GET_IFP(sc);
78677395c04Skevlo 	ifp->if_softc = sc;
78777395c04Skevlo 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
78877395c04Skevlo 	ifp->if_ioctl = mue_ioctl;
78977395c04Skevlo 	ifp->if_start = mue_start;
79077395c04Skevlo 	ifp->if_watchdog = mue_watchdog;
79177395c04Skevlo 	strlcpy(ifp->if_xname, sc->mue_dev.dv_xname, IFNAMSIZ);
79277395c04Skevlo 
79377395c04Skevlo 	ifp->if_capabilities = IFCAP_VLAN_MTU;
79477395c04Skevlo 
79577395c04Skevlo 	/* Initialize MII/media info. */
79677395c04Skevlo 	mii = GET_MII(sc);
79777395c04Skevlo 	mii->mii_ifp = ifp;
79877395c04Skevlo 	mii->mii_readreg = mue_miibus_readreg;
79977395c04Skevlo 	mii->mii_writereg = mue_miibus_writereg;
80077395c04Skevlo 	mii->mii_statchg = mue_miibus_statchg;
80177395c04Skevlo 	mii->mii_flags = MIIF_AUTOTSLEEP;
80277395c04Skevlo 
80377395c04Skevlo 	ifmedia_init(&mii->mii_media, 0, mue_ifmedia_upd, mue_ifmedia_sts);
80477395c04Skevlo 	mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY,
80577395c04Skevlo 	    MIIF_DOPAUSE);
80677395c04Skevlo 
80777395c04Skevlo 	if (LIST_FIRST(&mii->mii_phys) == NULL) {
80877395c04Skevlo 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
80977395c04Skevlo 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
81077395c04Skevlo 	} else
81177395c04Skevlo 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
81277395c04Skevlo 
81377395c04Skevlo 	/* Attach the interface. */
81477395c04Skevlo 	if_attach(ifp);
81577395c04Skevlo 	ether_ifattach(ifp);
81677395c04Skevlo 
81777395c04Skevlo 	timeout_set(&sc->mue_stat_ch, mue_tick, sc);
81877395c04Skevlo 
81977395c04Skevlo 	splx(s);
82077395c04Skevlo }
82177395c04Skevlo 
82277395c04Skevlo int
mue_detach(struct device * self,int flags)82377395c04Skevlo mue_detach(struct device *self, int flags)
82477395c04Skevlo {
82577395c04Skevlo 	struct mue_softc *sc = (struct mue_softc *)self;
82677395c04Skevlo 	struct ifnet *ifp = GET_IFP(sc);
82777395c04Skevlo 	int s;
82877395c04Skevlo 
82977395c04Skevlo 	if (timeout_initialized(&sc->mue_stat_ch))
83077395c04Skevlo 		timeout_del(&sc->mue_stat_ch);
83177395c04Skevlo 
83277395c04Skevlo 	if (sc->mue_ep[MUE_ENDPT_TX] != NULL)
83377395c04Skevlo 		usbd_abort_pipe(sc->mue_ep[MUE_ENDPT_TX]);
83477395c04Skevlo 	if (sc->mue_ep[MUE_ENDPT_RX] != NULL)
83577395c04Skevlo 		usbd_abort_pipe(sc->mue_ep[MUE_ENDPT_RX]);
83677395c04Skevlo 	if (sc->mue_ep[MUE_ENDPT_INTR] != NULL)
83777395c04Skevlo 		usbd_abort_pipe(sc->mue_ep[MUE_ENDPT_INTR]);
83877395c04Skevlo 
83977395c04Skevlo 	/*
84077395c04Skevlo 	 * Remove any pending tasks.  They cannot be executing because they run
84177395c04Skevlo 	 * in the same thread as detach.
84277395c04Skevlo 	 */
84377395c04Skevlo 	usb_rem_task(sc->mue_udev, &sc->mue_tick_task);
84477395c04Skevlo 	usb_rem_task(sc->mue_udev, &sc->mue_stop_task);
84577395c04Skevlo 
84677395c04Skevlo 	s = splusb();
84777395c04Skevlo 
84877395c04Skevlo 	if (--sc->mue_refcnt >= 0) {
84977395c04Skevlo 		/* Wait for processes to go away */
85077395c04Skevlo 		usb_detach_wait(&sc->mue_dev);
85177395c04Skevlo 	}
85277395c04Skevlo 
85377395c04Skevlo 	if (ifp->if_flags & IFF_RUNNING)
85477395c04Skevlo 		mue_stop(sc);
85577395c04Skevlo 
85677395c04Skevlo 	mii_detach(&sc->mue_mii, MII_PHY_ANY, MII_OFFSET_ANY);
85777395c04Skevlo 	ifmedia_delete_instance(&sc->mue_mii.mii_media, IFM_INST_ANY);
85877395c04Skevlo 	if (ifp->if_softc != NULL) {
85977395c04Skevlo 		ether_ifdetach(ifp);
86077395c04Skevlo 		if_detach(ifp);
86177395c04Skevlo 	}
86277395c04Skevlo 
86377395c04Skevlo 	splx(s);
86477395c04Skevlo 
86577395c04Skevlo 	return (0);
86677395c04Skevlo }
86777395c04Skevlo 
86877395c04Skevlo int
mue_rx_list_init(struct mue_softc * sc)86977395c04Skevlo mue_rx_list_init(struct mue_softc *sc)
87077395c04Skevlo {
87177395c04Skevlo 	struct mue_cdata *cd;
87277395c04Skevlo 	struct mue_chain *c;
87377395c04Skevlo 	int i;
87477395c04Skevlo 
87577395c04Skevlo 	DPRINTF(("%s: %s: enter\n", sc->mue_dev.dv_xname, __func__));
87677395c04Skevlo 
87777395c04Skevlo 	cd = &sc->mue_cdata;
87877395c04Skevlo 	for (i = 0; i < MUE_RX_LIST_CNT; i++) {
87977395c04Skevlo 		c = &cd->mue_rx_chain[i];
88077395c04Skevlo 		c->mue_sc = sc;
88177395c04Skevlo 		c->mue_idx = i;
88277395c04Skevlo 		c->mue_mbuf = NULL;
88377395c04Skevlo 		if (c->mue_xfer == NULL) {
88477395c04Skevlo 			c->mue_xfer = usbd_alloc_xfer(sc->mue_udev);
88577395c04Skevlo 			if (c->mue_xfer == NULL)
88677395c04Skevlo 				return (ENOBUFS);
88777395c04Skevlo 			c->mue_buf = usbd_alloc_buffer(c->mue_xfer,
88877395c04Skevlo 			    sc->mue_bufsz);
88977395c04Skevlo 			if (c->mue_buf == NULL) {
89077395c04Skevlo 				usbd_free_xfer(c->mue_xfer);
89177395c04Skevlo 				return (ENOBUFS);
89277395c04Skevlo 			}
89377395c04Skevlo 		}
89477395c04Skevlo 	}
89577395c04Skevlo 
89677395c04Skevlo 	return (0);
89777395c04Skevlo }
89877395c04Skevlo 
89977395c04Skevlo int
mue_tx_list_init(struct mue_softc * sc)90077395c04Skevlo mue_tx_list_init(struct mue_softc *sc)
90177395c04Skevlo {
90277395c04Skevlo 	struct mue_cdata *cd;
90377395c04Skevlo 	struct mue_chain *c;
90477395c04Skevlo 	int i;
90577395c04Skevlo 
90677395c04Skevlo 	DPRINTF(("%s: %s: enter\n", sc->mue_dev.dv_xname, __func__));
90777395c04Skevlo 
90877395c04Skevlo 	cd = &sc->mue_cdata;
90977395c04Skevlo 	for (i = 0; i < MUE_TX_LIST_CNT; i++) {
91077395c04Skevlo 		c = &cd->mue_tx_chain[i];
91177395c04Skevlo 		c->mue_sc = sc;
91277395c04Skevlo 		c->mue_idx = i;
91377395c04Skevlo 		c->mue_mbuf = NULL;
91477395c04Skevlo 		if (c->mue_xfer == NULL) {
91577395c04Skevlo 			c->mue_xfer = usbd_alloc_xfer(sc->mue_udev);
91677395c04Skevlo 			if (c->mue_xfer == NULL)
91777395c04Skevlo 				return (ENOBUFS);
91877395c04Skevlo 			c->mue_buf = usbd_alloc_buffer(c->mue_xfer,
91977395c04Skevlo 			    sc->mue_bufsz);
92077395c04Skevlo 			if (c->mue_buf == NULL) {
92177395c04Skevlo 				usbd_free_xfer(c->mue_xfer);
92277395c04Skevlo 				return (ENOBUFS);
92377395c04Skevlo 			}
92477395c04Skevlo 		}
92577395c04Skevlo 	}
92677395c04Skevlo 
92777395c04Skevlo 	return (0);
92877395c04Skevlo }
92977395c04Skevlo 
93077395c04Skevlo int
mue_open_pipes(struct mue_softc * sc)93177395c04Skevlo mue_open_pipes(struct mue_softc *sc)
93277395c04Skevlo {
93377395c04Skevlo 	struct mue_chain *c;
93477395c04Skevlo 	usbd_status err;
93577395c04Skevlo 	int i;
93677395c04Skevlo 
93777395c04Skevlo 	/* Open RX and TX pipes. */
93877395c04Skevlo 	err = usbd_open_pipe(sc->mue_iface, sc->mue_ed[MUE_ENDPT_RX],
93977395c04Skevlo 	    USBD_EXCLUSIVE_USE, &sc->mue_ep[MUE_ENDPT_RX]);
94077395c04Skevlo 	if (err) {
94177395c04Skevlo 		printf("%s: open rx pipe failed: %s\n",
94277395c04Skevlo 		    sc->mue_dev.dv_xname, usbd_errstr(err));
94377395c04Skevlo 		return (EIO);
94477395c04Skevlo 	}
94577395c04Skevlo 	err = usbd_open_pipe(sc->mue_iface, sc->mue_ed[MUE_ENDPT_TX],
94677395c04Skevlo 	    USBD_EXCLUSIVE_USE, &sc->mue_ep[MUE_ENDPT_TX]);
94777395c04Skevlo 	if (err) {
94877395c04Skevlo 		printf("%s: open tx pipe failed: %s\n",
94977395c04Skevlo 		    sc->mue_dev.dv_xname, usbd_errstr(err));
95077395c04Skevlo 		return (EIO);
95177395c04Skevlo 	}
95277395c04Skevlo 
95377395c04Skevlo 	/* Start up the receive pipe. */
95477395c04Skevlo 	for (i = 0; i < MUE_RX_LIST_CNT; i++) {
95577395c04Skevlo 		c = &sc->mue_cdata.mue_rx_chain[i];
95677395c04Skevlo 		usbd_setup_xfer(c->mue_xfer, sc->mue_ep[MUE_ENDPT_RX],
95777395c04Skevlo 		    c, c->mue_buf, sc->mue_bufsz,
95877395c04Skevlo 		    USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
95977395c04Skevlo 		    mue_rxeof);
96077395c04Skevlo 		usbd_transfer(c->mue_xfer);
96177395c04Skevlo 	}
96277395c04Skevlo 
96377395c04Skevlo 	return (0);
96477395c04Skevlo }
96577395c04Skevlo 
96677395c04Skevlo int
mue_encap(struct mue_softc * sc,struct mbuf * m,int idx)96777395c04Skevlo mue_encap(struct mue_softc *sc, struct mbuf *m, int idx)
96877395c04Skevlo {
96977395c04Skevlo 	struct mue_chain *c;
97077395c04Skevlo 	usbd_status err;
97177395c04Skevlo 	struct mue_txbuf_hdr hdr;
97277395c04Skevlo 	int length;
97377395c04Skevlo 
97477395c04Skevlo 	c = &sc->mue_cdata.mue_tx_chain[idx];
97577395c04Skevlo 
97677395c04Skevlo 	hdr.tx_cmd_a = htole32((m->m_pkthdr.len & MUE_TX_CMD_A_LEN_MASK) |
97777395c04Skevlo 	    MUE_TX_CMD_A_FCS);
97877395c04Skevlo 	/* Disable segmentation offload. */
97977395c04Skevlo 	hdr.tx_cmd_b = htole32(0);
98077395c04Skevlo 	memcpy(c->mue_buf, &hdr, sizeof(hdr));
98177395c04Skevlo 	length = sizeof(hdr);
98277395c04Skevlo 
98377395c04Skevlo 	m_copydata(m, 0, m->m_pkthdr.len, c->mue_buf + length);
98477395c04Skevlo 	length += m->m_pkthdr.len;
98577395c04Skevlo 
98677395c04Skevlo 	c->mue_mbuf = m;
98777395c04Skevlo 
98877395c04Skevlo 	usbd_setup_xfer(c->mue_xfer, sc->mue_ep[MUE_ENDPT_TX],
98977395c04Skevlo 	    c, c->mue_buf, length, USBD_FORCE_SHORT_XFER | USBD_NO_COPY,
99077395c04Skevlo 	    10000, mue_txeof);
99177395c04Skevlo 
99277395c04Skevlo 	/* Transmit */
99377395c04Skevlo 	err = usbd_transfer(c->mue_xfer);
99477395c04Skevlo 	if (err != USBD_IN_PROGRESS) {
9954ced949bSgerhard 		c->mue_mbuf = NULL;
99677395c04Skevlo 		mue_stop(sc);
99777395c04Skevlo 		return(EIO);
99877395c04Skevlo 	}
99977395c04Skevlo 
100077395c04Skevlo 	sc->mue_cdata.mue_tx_cnt++;
100177395c04Skevlo 
100277395c04Skevlo 	return(0);
100377395c04Skevlo }
100477395c04Skevlo 
100577395c04Skevlo void
mue_iff(struct mue_softc * sc)100677395c04Skevlo mue_iff(struct mue_softc *sc)
100777395c04Skevlo {
100877395c04Skevlo 	struct ifnet *ifp = GET_IFP(sc);
100977395c04Skevlo 	struct arpcom *ac = &sc->arpcom;
101077395c04Skevlo 	struct ether_multi *enm;
101177395c04Skevlo 	struct ether_multistep step;
101277395c04Skevlo 	uint32_t h = 0, hashtbl[MUE_DP_SEL_VHF_HASH_LEN], reg, rxfilt;
101377395c04Skevlo 
101477395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
101577395c04Skevlo 		return;
101677395c04Skevlo 
101777395c04Skevlo 	reg = (sc->mue_flags & LAN7500) ? MUE_RFE_CTL : MUE_7800_RFE_CTL;
101877395c04Skevlo 	rxfilt = mue_csr_read(sc, reg);
101977395c04Skevlo 	rxfilt &= ~(MUE_RFE_CTL_PERFECT | MUE_RFE_CTL_MULTICAST_HASH |
102077395c04Skevlo 	    MUE_RFE_CTL_UNICAST | MUE_RFE_CTL_MULTICAST);
1021bd0c1d2dSmestre 	memset(hashtbl, 0, sizeof(hashtbl));
102277395c04Skevlo 	ifp->if_flags &= ~IFF_ALLMULTI;
102377395c04Skevlo 
102477395c04Skevlo 	/* Always accept broadcast frames. */
102577395c04Skevlo 	rxfilt |= MUE_RFE_CTL_BROADCAST;
102677395c04Skevlo 
102777395c04Skevlo 	if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
102877395c04Skevlo 		ifp->if_flags |= IFF_ALLMULTI;
102977395c04Skevlo 		rxfilt |= MUE_RFE_CTL_MULTICAST;
103077395c04Skevlo 		if (ifp->if_flags & IFF_PROMISC)
103177395c04Skevlo 			rxfilt |= MUE_RFE_CTL_UNICAST | MUE_RFE_CTL_MULTICAST;
103277395c04Skevlo 	} else {
103377395c04Skevlo 		rxfilt |= MUE_RFE_CTL_PERFECT | MUE_RFE_CTL_MULTICAST_HASH;
103477395c04Skevlo 
103577395c04Skevlo 		/* Now program new ones. */
103677395c04Skevlo 		ETHER_FIRST_MULTI(step, ac, enm);
103777395c04Skevlo 		while (enm != NULL) {
103877395c04Skevlo 			h = ether_crc32_be(enm->enm_addrlo,
103977395c04Skevlo 			    ETHER_ADDR_LEN) >> 23;
104077395c04Skevlo 			hashtbl[h / 32] |= 1 << (h % 32);
104177395c04Skevlo 			ETHER_NEXT_MULTI(step, enm);
104277395c04Skevlo 		}
104377395c04Skevlo 	}
104477395c04Skevlo 
104577395c04Skevlo 	mue_dataport_write(sc, MUE_DP_SEL_VHF, MUE_DP_SEL_VHF_VLAN_LEN,
104677395c04Skevlo 	    MUE_DP_SEL_VHF_HASH_LEN, hashtbl);
104777395c04Skevlo 	mue_csr_write(sc, reg, rxfilt);
104877395c04Skevlo }
104977395c04Skevlo 
105077395c04Skevlo void
mue_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)105177395c04Skevlo mue_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
105277395c04Skevlo {
105377395c04Skevlo 	struct mue_chain *c = (struct mue_chain *)priv;
105477395c04Skevlo 	struct mue_softc *sc = c->mue_sc;
105577395c04Skevlo 	struct ifnet *ifp = GET_IFP(sc);
105677395c04Skevlo 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
105777395c04Skevlo 	struct mbuf *m;
105877395c04Skevlo 	struct mue_rxbuf_hdr hdr;
105977395c04Skevlo 	u_char *buf = c->mue_buf;
106077395c04Skevlo 	uint32_t total_len;
106177395c04Skevlo 	int pktlen = 0;
106277395c04Skevlo 	int s;
106377395c04Skevlo 
106477395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
106577395c04Skevlo 		return;
106677395c04Skevlo 
106777395c04Skevlo 	if (!(ifp->if_flags & IFF_RUNNING))
106877395c04Skevlo 		return;
106977395c04Skevlo 
107077395c04Skevlo 	if (status != USBD_NORMAL_COMPLETION) {
107177395c04Skevlo 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
107277395c04Skevlo 			return;
107377395c04Skevlo 		if (usbd_ratecheck(&sc->mue_rx_notice)) {
107477395c04Skevlo 			printf("%s: usb errors on rx: %s\n",
107577395c04Skevlo 			    sc->mue_dev.dv_xname, usbd_errstr(status));
107677395c04Skevlo 		}
107777395c04Skevlo 		if (status == USBD_STALLED)
107877395c04Skevlo 			usbd_clear_endpoint_stall_async(sc->mue_ep[MUE_ENDPT_RX]);
107977395c04Skevlo 		goto done;
108077395c04Skevlo 	}
108177395c04Skevlo 
108277395c04Skevlo 	usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
108377395c04Skevlo 
108477395c04Skevlo 	do {
108577395c04Skevlo 		if (total_len < sizeof(hdr)) {
108677395c04Skevlo 			ifp->if_ierrors++;
108777395c04Skevlo 			goto done;
108877395c04Skevlo 		}
108977395c04Skevlo 
109077395c04Skevlo 		buf += pktlen;
109177395c04Skevlo 
109277395c04Skevlo 		memcpy(&hdr, buf, sizeof(hdr));
109377395c04Skevlo 		total_len -= sizeof(hdr);
109477395c04Skevlo 
109577395c04Skevlo 		if (letoh32(hdr.rx_cmd_a) & MUE_RX_CMD_A_RED) {
109677395c04Skevlo 			ifp->if_ierrors++;
109777395c04Skevlo 			goto done;
109877395c04Skevlo 		}
109977395c04Skevlo 
110077395c04Skevlo 		pktlen = letoh32(hdr.rx_cmd_a) & MUE_RX_CMD_A_LEN_MASK;
110177395c04Skevlo 		if (sc->mue_flags & LAN7500)
110277395c04Skevlo 			pktlen -= 2;
110377395c04Skevlo 
110477395c04Skevlo 		if (pktlen > total_len) {
110577395c04Skevlo 			ifp->if_ierrors++;
110677395c04Skevlo 			goto done;
110777395c04Skevlo 		}
110877395c04Skevlo 
110977395c04Skevlo 		buf += sizeof(hdr);
111077395c04Skevlo 
111177395c04Skevlo 		if (total_len < pktlen)
111277395c04Skevlo 			total_len = 0;
111377395c04Skevlo 		else
111477395c04Skevlo 			total_len -= pktlen;
111577395c04Skevlo 
111677395c04Skevlo 		m = m_devget(buf, pktlen - ETHER_CRC_LEN, ETHER_ALIGN);
111777395c04Skevlo 		if (m == NULL) {
111877395c04Skevlo 			DPRINTF(("unable to allocate mbuf for next packet\n"));
111977395c04Skevlo 			ifp->if_ierrors++;
112077395c04Skevlo 			goto done;
112177395c04Skevlo 		}
112277395c04Skevlo 		ml_enqueue(&ml, m);
112377395c04Skevlo 	} while (total_len > 0);
112477395c04Skevlo 
112577395c04Skevlo done:
112677395c04Skevlo 	s = splnet();
112777395c04Skevlo 	if_input(ifp, &ml);
112877395c04Skevlo 	splx(s);
112977395c04Skevlo 
113077395c04Skevlo 	memset(c->mue_buf, 0, sc->mue_bufsz);
113177395c04Skevlo 
113277395c04Skevlo 	/* Setup new transfer. */
113377395c04Skevlo 	usbd_setup_xfer(xfer, sc->mue_ep[MUE_ENDPT_RX],
113477395c04Skevlo 	    c, c->mue_buf, sc->mue_bufsz, USBD_SHORT_XFER_OK | USBD_NO_COPY,
113577395c04Skevlo 	    USBD_NO_TIMEOUT, mue_rxeof);
113677395c04Skevlo 	usbd_transfer(xfer);
113777395c04Skevlo 
113877395c04Skevlo 	DPRINTFN(10,("%s: %s: start rx\n", sc->mue_dev.dv_xname, __func__));
113977395c04Skevlo }
114077395c04Skevlo 
114177395c04Skevlo void
mue_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)114277395c04Skevlo mue_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
114377395c04Skevlo {
114477395c04Skevlo 	struct mue_chain *c = priv;
114577395c04Skevlo 	struct mue_softc *sc = c->mue_sc;
114677395c04Skevlo 	struct ifnet *ifp = GET_IFP(sc);
114777395c04Skevlo 	int s;
114877395c04Skevlo 
114977395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
115077395c04Skevlo 		return;
115177395c04Skevlo 
115277395c04Skevlo 	s = splnet();
115377395c04Skevlo 
115477395c04Skevlo 	DPRINTFN(10,("%s: %s: enter status=%d\n", sc->mue_dev.dv_xname,
115577395c04Skevlo 	    __func__, status));
115677395c04Skevlo 
115777395c04Skevlo 	if (status != USBD_NORMAL_COMPLETION) {
115877395c04Skevlo 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) {
115977395c04Skevlo 			splx(s);
116077395c04Skevlo 			return;
116177395c04Skevlo 		}
116277395c04Skevlo 		ifp->if_oerrors++;
116377395c04Skevlo 		printf("%s: usb error on tx: %s\n", sc->mue_dev.dv_xname,
116477395c04Skevlo 		    usbd_errstr(status));
116577395c04Skevlo 		if (status == USBD_STALLED)
116677395c04Skevlo 			usbd_clear_endpoint_stall_async(sc->mue_ep[MUE_ENDPT_TX]);
116777395c04Skevlo 		splx(s);
116877395c04Skevlo 		return;
116977395c04Skevlo 	}
117077395c04Skevlo 
117177395c04Skevlo 	ifp->if_timer = 0;
117277395c04Skevlo 	ifq_clr_oactive(&ifp->if_snd);
117377395c04Skevlo 
117477395c04Skevlo 	m_freem(c->mue_mbuf);
117577395c04Skevlo 	c->mue_mbuf = NULL;
117677395c04Skevlo 
11770cae21bdSpatrick 	if (ifq_empty(&ifp->if_snd) == 0)
117877395c04Skevlo 		mue_start(ifp);
117977395c04Skevlo 
118077395c04Skevlo 	splx(s);
118177395c04Skevlo }
118277395c04Skevlo 
118377395c04Skevlo void
mue_init(void * xsc)118477395c04Skevlo mue_init(void *xsc)
118577395c04Skevlo {
118677395c04Skevlo 	struct mue_softc *sc = xsc;
118777395c04Skevlo 	struct ifnet *ifp = GET_IFP(sc);
118877395c04Skevlo 	int s;
118977395c04Skevlo 
119077395c04Skevlo 	s = splnet();
119177395c04Skevlo 
119277395c04Skevlo 	/* Cancel pending I/O and free all TX/RX buffers. */
119377395c04Skevlo 	mue_reset(sc);
119477395c04Skevlo 
119577395c04Skevlo 	/* Set MAC address. */
119677395c04Skevlo 	mue_set_macaddr(sc);
119777395c04Skevlo 
119877395c04Skevlo 	/* Init RX ring. */
119977395c04Skevlo 	if (mue_rx_list_init(sc) == ENOBUFS) {
120077395c04Skevlo 		printf("%s: rx list init failed\n", sc->mue_dev.dv_xname);
120177395c04Skevlo 		splx(s);
120277395c04Skevlo 		return;
120377395c04Skevlo 	}
120477395c04Skevlo 
120577395c04Skevlo 	/* Init TX ring. */
120677395c04Skevlo 	if (mue_tx_list_init(sc) == ENOBUFS) {
120777395c04Skevlo 		printf("%s: tx list init failed\n", sc->mue_dev.dv_xname);
120877395c04Skevlo 		splx(s);
120977395c04Skevlo 		return;
121077395c04Skevlo 	}
121177395c04Skevlo 
121277395c04Skevlo 	/* Program promiscuous mode and multicast filters. */
121377395c04Skevlo 	mue_iff(sc);
121477395c04Skevlo 
121577395c04Skevlo 	if (mue_open_pipes(sc) != 0) {
121677395c04Skevlo 		splx(s);
121777395c04Skevlo 		return;
121877395c04Skevlo 	}
121977395c04Skevlo 
122077395c04Skevlo 	sc->mue_link = 0;
122177395c04Skevlo 	ifp->if_flags |= IFF_RUNNING;
122277395c04Skevlo 	ifq_clr_oactive(&ifp->if_snd);
122377395c04Skevlo 
122477395c04Skevlo 	splx(s);
122577395c04Skevlo 
122677395c04Skevlo 	timeout_add_sec(&sc->mue_stat_ch, 1);
122777395c04Skevlo }
122877395c04Skevlo 
122977395c04Skevlo int
mue_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)123077395c04Skevlo mue_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
123177395c04Skevlo {
123277395c04Skevlo 	struct mue_softc *sc = ifp->if_softc;
123377395c04Skevlo 	struct ifreq *ifr = (struct ifreq *)data;
123477395c04Skevlo 	int s, error = 0;
123577395c04Skevlo 
123677395c04Skevlo 	s = splnet();
123777395c04Skevlo 
123877395c04Skevlo 	switch(cmd) {
123977395c04Skevlo 	case SIOCSIFADDR:
124077395c04Skevlo 		ifp->if_flags |= IFF_UP;
124177395c04Skevlo 		if (!(ifp->if_flags & IFF_RUNNING))
124277395c04Skevlo 			mue_init(sc);
124377395c04Skevlo 		break;
124477395c04Skevlo 	case SIOCSIFFLAGS:
124577395c04Skevlo 		if (ifp->if_flags & IFF_UP) {
124677395c04Skevlo 			if (ifp->if_flags & IFF_RUNNING)
124777395c04Skevlo 				error = ENETRESET;
124877395c04Skevlo 			else
124977395c04Skevlo 				mue_init(sc);
125077395c04Skevlo 		} else {
125177395c04Skevlo 			if (ifp->if_flags & IFF_RUNNING)
125277395c04Skevlo 				mue_stop(sc);
125377395c04Skevlo 		}
125477395c04Skevlo 		break;
125577395c04Skevlo 	case SIOCGIFMEDIA:
125677395c04Skevlo 	case SIOCSIFMEDIA:
125777395c04Skevlo 		error = ifmedia_ioctl(ifp, ifr, &sc->mue_mii.mii_media, cmd);
125877395c04Skevlo 		break;
125977395c04Skevlo 	default:
126077395c04Skevlo 		error = ether_ioctl(ifp, &sc->arpcom, cmd, data);
126177395c04Skevlo 	}
126277395c04Skevlo 
126377395c04Skevlo 	if (error == ENETRESET) {
126477395c04Skevlo 		if (ifp->if_flags & IFF_RUNNING)
126577395c04Skevlo 			mue_iff(sc);
126677395c04Skevlo 		error = 0;
126777395c04Skevlo 	}
126877395c04Skevlo 
126977395c04Skevlo 	splx(s);
127077395c04Skevlo 
127177395c04Skevlo 	return(error);
127277395c04Skevlo }
127377395c04Skevlo 
127477395c04Skevlo void
mue_watchdog(struct ifnet * ifp)127577395c04Skevlo mue_watchdog(struct ifnet *ifp)
127677395c04Skevlo {
127777395c04Skevlo 	struct mue_softc *sc = ifp->if_softc;
127877395c04Skevlo 	struct mue_chain *c;
127977395c04Skevlo 	usbd_status stat;
128077395c04Skevlo 	int s;
128177395c04Skevlo 
128277395c04Skevlo 	ifp->if_oerrors++;
128377395c04Skevlo 	printf("%s: watchdog timeout\n", sc->mue_dev.dv_xname);
128477395c04Skevlo 
128577395c04Skevlo 	s = splusb();
128677395c04Skevlo 	c = &sc->mue_cdata.mue_tx_chain[0];
128777395c04Skevlo 	usbd_get_xfer_status(c->mue_xfer, NULL, NULL, NULL, &stat);
128877395c04Skevlo 	mue_txeof(c->mue_xfer, c, stat);
128977395c04Skevlo 
12900cae21bdSpatrick 	if (!ifq_empty(&ifp->if_snd))
129177395c04Skevlo 		mue_start(ifp);
129277395c04Skevlo 	splx(s);
129377395c04Skevlo }
129477395c04Skevlo 
129577395c04Skevlo void
mue_reset(struct mue_softc * sc)129677395c04Skevlo mue_reset(struct mue_softc *sc)
129777395c04Skevlo {
129877395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
129977395c04Skevlo 		return;
130077395c04Skevlo 
130177395c04Skevlo 	/* Wait a little while for the chip to get its brains in order. */
130277395c04Skevlo 	DELAY(1000);
130377395c04Skevlo }
130477395c04Skevlo 
130577395c04Skevlo void
mue_start(struct ifnet * ifp)130677395c04Skevlo mue_start(struct ifnet *ifp)
130777395c04Skevlo {
130877395c04Skevlo 	struct mue_softc *sc = ifp->if_softc;
130977395c04Skevlo 	struct mbuf *m_head = NULL;
131077395c04Skevlo 
131177395c04Skevlo 	if (!sc->mue_link)
131277395c04Skevlo 		return;
131377395c04Skevlo 
131477395c04Skevlo 	if (ifq_is_oactive(&ifp->if_snd))
131577395c04Skevlo 		return;
131677395c04Skevlo 
13174ced949bSgerhard 	m_head = ifq_dequeue(&ifp->if_snd);
131877395c04Skevlo 	if (m_head == NULL)
131977395c04Skevlo 		return;
132077395c04Skevlo 
132177395c04Skevlo 	if (mue_encap(sc, m_head, 0)) {
13224ced949bSgerhard 		m_freem(m_head);
132377395c04Skevlo 		ifq_set_oactive(&ifp->if_snd);
132477395c04Skevlo 		return;
132577395c04Skevlo 	}
132677395c04Skevlo 
132777395c04Skevlo 	/* If there's a BPF listener, bounce a copy of this frame to him. */
132877395c04Skevlo #if NBPFILTER > 0
132977395c04Skevlo 	if (ifp->if_bpf)
133077395c04Skevlo 		bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
133177395c04Skevlo #endif
133277395c04Skevlo 
133377395c04Skevlo 	ifq_set_oactive(&ifp->if_snd);
133477395c04Skevlo 
133577395c04Skevlo 	/* Set a timeout in case the chip goes out to lunch. */
133677395c04Skevlo 	ifp->if_timer = 5;
133777395c04Skevlo }
133877395c04Skevlo 
133977395c04Skevlo void
mue_stop(struct mue_softc * sc)134077395c04Skevlo mue_stop(struct mue_softc *sc)
134177395c04Skevlo {
134277395c04Skevlo 	struct ifnet *ifp;
13438af398f3Skevlo 	usbd_status err;
13448af398f3Skevlo 	int i;
134577395c04Skevlo 
134677395c04Skevlo 	ifp = GET_IFP(sc);
134777395c04Skevlo 	ifp->if_timer = 0;
134877395c04Skevlo 	ifp->if_flags &= ~IFF_RUNNING;
134977395c04Skevlo 	ifq_clr_oactive(&ifp->if_snd);
135077395c04Skevlo 
135177395c04Skevlo 	timeout_del(&sc->mue_stat_ch);
135277395c04Skevlo 
13538af398f3Skevlo 	/* Stop transfers. */
13548af398f3Skevlo 	if (sc->mue_ep[MUE_ENDPT_RX] != NULL) {
13558af398f3Skevlo 		err = usbd_close_pipe(sc->mue_ep[MUE_ENDPT_RX]);
13568af398f3Skevlo 		if (err) {
13578af398f3Skevlo 			printf("%s: close rx pipe failed: %s\n",
13588af398f3Skevlo 			    sc->mue_dev.dv_xname, usbd_errstr(err));
13598af398f3Skevlo 		}
13608af398f3Skevlo 		sc->mue_ep[MUE_ENDPT_RX] = NULL;
13618af398f3Skevlo 	}
13628af398f3Skevlo 
13638af398f3Skevlo 	if (sc->mue_ep[MUE_ENDPT_TX] != NULL) {
13648af398f3Skevlo 		err = usbd_close_pipe(sc->mue_ep[MUE_ENDPT_TX]);
13658af398f3Skevlo 		if (err) {
13668af398f3Skevlo 			printf("%s: close tx pipe failed: %s\n",
13678af398f3Skevlo 			    sc->mue_dev.dv_xname, usbd_errstr(err));
13688af398f3Skevlo 		}
13698af398f3Skevlo 		sc->mue_ep[MUE_ENDPT_TX] = NULL;
13708af398f3Skevlo 	}
13718af398f3Skevlo 
13728af398f3Skevlo 	if (sc->mue_ep[MUE_ENDPT_INTR] != NULL) {
13738af398f3Skevlo 		err = usbd_close_pipe(sc->mue_ep[MUE_ENDPT_INTR]);
13748af398f3Skevlo 		if (err) {
13758af398f3Skevlo 			printf("%s: close intr pipe failed: %s\n",
13768af398f3Skevlo 			    sc->mue_dev.dv_xname, usbd_errstr(err));
13778af398f3Skevlo 		}
13788af398f3Skevlo 		sc->mue_ep[MUE_ENDPT_INTR] = NULL;
13798af398f3Skevlo 	}
13808af398f3Skevlo 
13818af398f3Skevlo 	/* Free RX resources. */
13828af398f3Skevlo 	for (i = 0; i < MUE_RX_LIST_CNT; i++) {
13838af398f3Skevlo 		if (sc->mue_cdata.mue_rx_chain[i].mue_mbuf != NULL) {
13848af398f3Skevlo 			m_freem(sc->mue_cdata.mue_rx_chain[i].mue_mbuf);
13858af398f3Skevlo 			sc->mue_cdata.mue_rx_chain[i].mue_mbuf = NULL;
13868af398f3Skevlo 		}
13878af398f3Skevlo 		if (sc->mue_cdata.mue_rx_chain[i].mue_xfer != NULL) {
13888af398f3Skevlo 			usbd_free_xfer(sc->mue_cdata.mue_rx_chain[i].mue_xfer);
13898af398f3Skevlo 			sc->mue_cdata.mue_rx_chain[i].mue_xfer = NULL;
13908af398f3Skevlo 		}
13918af398f3Skevlo 	}
13928af398f3Skevlo 
13938af398f3Skevlo 	/* Free TX resources. */
13948af398f3Skevlo 	for (i = 0; i < MUE_TX_LIST_CNT; i++) {
13958af398f3Skevlo 		if (sc->mue_cdata.mue_tx_chain[i].mue_mbuf != NULL) {
13968af398f3Skevlo 			m_freem(sc->mue_cdata.mue_tx_chain[i].mue_mbuf);
13978af398f3Skevlo 			sc->mue_cdata.mue_tx_chain[i].mue_mbuf = NULL;
13988af398f3Skevlo 		}
13998af398f3Skevlo 		if (sc->mue_cdata.mue_tx_chain[i].mue_xfer != NULL) {
14008af398f3Skevlo 			usbd_free_xfer(sc->mue_cdata.mue_tx_chain[i].mue_xfer);
14018af398f3Skevlo 			sc->mue_cdata.mue_tx_chain[i].mue_xfer = NULL;
14028af398f3Skevlo 		}
14038af398f3Skevlo 	}
14048af398f3Skevlo 
140577395c04Skevlo 	sc->mue_link = 0;
140677395c04Skevlo }
140777395c04Skevlo 
140877395c04Skevlo void
mue_tick(void * xsc)140977395c04Skevlo mue_tick(void *xsc)
141077395c04Skevlo {
141177395c04Skevlo 	struct mue_softc *sc = xsc;
141277395c04Skevlo 
141377395c04Skevlo 	if (sc == NULL)
141477395c04Skevlo 		return;
141577395c04Skevlo 
141677395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
141777395c04Skevlo 		return;
141877395c04Skevlo 
141977395c04Skevlo 	/* Perform periodic stuff in process context. */
142077395c04Skevlo 	usb_add_task(sc->mue_udev, &sc->mue_tick_task);
142177395c04Skevlo }
142277395c04Skevlo 
142377395c04Skevlo void
mue_tick_task(void * xsc)142477395c04Skevlo mue_tick_task(void *xsc)
142577395c04Skevlo {
142677395c04Skevlo 	struct mue_softc *sc =xsc;
142777395c04Skevlo 	struct mii_data *mii;
142877395c04Skevlo 	int s;
142977395c04Skevlo 
143077395c04Skevlo 	if (sc == NULL)
143177395c04Skevlo 		return;
143277395c04Skevlo 
143377395c04Skevlo 	if (usbd_is_dying(sc->mue_udev))
143477395c04Skevlo 		return;
143577395c04Skevlo 
143677395c04Skevlo 	mii = GET_MII(sc);
143777395c04Skevlo 
143877395c04Skevlo 	s = splnet();
143977395c04Skevlo 	mii_tick(mii);
144077395c04Skevlo 	if (sc->mue_link == 0)
144177395c04Skevlo 		mue_miibus_statchg(&sc->mue_dev);
144277395c04Skevlo 	timeout_add_sec(&sc->mue_stat_ch, 1);
144377395c04Skevlo 	splx(s);
144477395c04Skevlo }
1445