xref: /openbsd-src/sys/dev/fdt/if_fec.c (revision 4b1a56afb1a28c97103da3911d326d1216798a6e)
1*4b1a56afSjsg /* $OpenBSD: if_fec.c,v 1.14 2022/01/09 05:42:37 jsg Exp $ */
24d54f62fSpatrick /*
301946c72Spatrick  * Copyright (c) 2012-2013,2019 Patrick Wildt <patrick@blueri.se>
44d54f62fSpatrick  *
54d54f62fSpatrick  * Permission to use, copy, modify, and distribute this software for any
64d54f62fSpatrick  * purpose with or without fee is hereby granted, provided that the above
74d54f62fSpatrick  * copyright notice and this permission notice appear in all copies.
84d54f62fSpatrick  *
94d54f62fSpatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104d54f62fSpatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114d54f62fSpatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124d54f62fSpatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134d54f62fSpatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144d54f62fSpatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154d54f62fSpatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164d54f62fSpatrick  */
174d54f62fSpatrick 
184d54f62fSpatrick #include <sys/param.h>
194d54f62fSpatrick #include <sys/systm.h>
204d54f62fSpatrick #include <sys/sockio.h>
214d54f62fSpatrick #include <sys/queue.h>
224d54f62fSpatrick #include <sys/malloc.h>
234d54f62fSpatrick #include <sys/device.h>
244d54f62fSpatrick #include <sys/evcount.h>
254d54f62fSpatrick #include <sys/socket.h>
264d54f62fSpatrick #include <sys/timeout.h>
274d54f62fSpatrick #include <sys/mbuf.h>
284d54f62fSpatrick #include <machine/intr.h>
294d54f62fSpatrick #include <machine/bus.h>
304d54f62fSpatrick #include <machine/fdt.h>
314d54f62fSpatrick 
324d54f62fSpatrick #include "bpfilter.h"
334d54f62fSpatrick 
344d54f62fSpatrick #include <net/if.h>
354d54f62fSpatrick #include <net/if_media.h>
364d54f62fSpatrick #if NBPFILTER > 0
374d54f62fSpatrick #include <net/bpf.h>
384d54f62fSpatrick #endif
394d54f62fSpatrick 
404d54f62fSpatrick #include <netinet/in.h>
414d54f62fSpatrick #include <netinet/if_ether.h>
424d54f62fSpatrick 
434d54f62fSpatrick #include <dev/mii/mii.h>
444d54f62fSpatrick #include <dev/mii/miivar.h>
454d54f62fSpatrick #include <dev/mii/miidevs.h>
464d54f62fSpatrick 
474d54f62fSpatrick #include <dev/ofw/openfirm.h>
484d54f62fSpatrick #include <dev/ofw/ofw_clock.h>
494d54f62fSpatrick #include <dev/ofw/ofw_gpio.h>
504d54f62fSpatrick #include <dev/ofw/ofw_pinctrl.h>
514d54f62fSpatrick #include <dev/ofw/fdt.h>
524d54f62fSpatrick 
534d54f62fSpatrick /* configuration registers */
544d54f62fSpatrick #define ENET_EIR		0x004
554d54f62fSpatrick #define ENET_EIMR		0x008
564d54f62fSpatrick #define ENET_RDAR		0x010
574d54f62fSpatrick #define ENET_TDAR		0x014
584d54f62fSpatrick #define ENET_ECR		0x024
594d54f62fSpatrick #define ENET_MMFR		0x040
604d54f62fSpatrick #define ENET_MSCR		0x044
614d54f62fSpatrick #define ENET_MIBC		0x064
624d54f62fSpatrick #define ENET_RCR		0x084
634d54f62fSpatrick #define ENET_TCR		0x0C4
644d54f62fSpatrick #define ENET_PALR		0x0E4
654d54f62fSpatrick #define ENET_PAUR		0x0E8
664d54f62fSpatrick #define ENET_OPD		0x0EC
674d54f62fSpatrick #define ENET_IAUR		0x118
684d54f62fSpatrick #define ENET_IALR		0x11C
694d54f62fSpatrick #define ENET_GAUR		0x120
704d54f62fSpatrick #define ENET_GALR		0x124
714d54f62fSpatrick #define ENET_TFWR		0x144
724d54f62fSpatrick #define ENET_RDSR		0x180
734d54f62fSpatrick #define ENET_TDSR		0x184
744d54f62fSpatrick #define ENET_MRBR		0x188
754d54f62fSpatrick #define ENET_RSFL		0x190
764d54f62fSpatrick #define ENET_RSEM		0x194
774d54f62fSpatrick #define ENET_RAEM		0x198
784d54f62fSpatrick #define ENET_RAFL		0x19C
794d54f62fSpatrick #define ENET_TSEM		0x1A0
804d54f62fSpatrick #define ENET_TAEM		0x1A4
814d54f62fSpatrick #define ENET_TAFL		0x1A8
824d54f62fSpatrick #define ENET_TIPG		0x1AC
834d54f62fSpatrick #define ENET_FTRL		0x1B0
844d54f62fSpatrick #define ENET_TACC		0x1C0
854d54f62fSpatrick #define ENET_RACC		0x1C4
864d54f62fSpatrick 
874d54f62fSpatrick #define ENET_RDAR_RDAR		(1 << 24)
884d54f62fSpatrick #define ENET_TDAR_TDAR		(1 << 24)
894d54f62fSpatrick #define ENET_ECR_RESET		(1 << 0)
904d54f62fSpatrick #define ENET_ECR_ETHEREN	(1 << 1)
914d54f62fSpatrick #define ENET_ECR_EN1588		(1 << 4)
924d54f62fSpatrick #define ENET_ECR_SPEED		(1 << 5)
934d54f62fSpatrick #define ENET_ECR_DBSWP		(1 << 8)
944d54f62fSpatrick #define ENET_MMFR_TA		(2 << 16)
954d54f62fSpatrick #define ENET_MMFR_RA_SHIFT	18
964d54f62fSpatrick #define ENET_MMFR_PA_SHIFT	23
974d54f62fSpatrick #define ENET_MMFR_OP_WR		(1 << 28)
984d54f62fSpatrick #define ENET_MMFR_OP_RD		(2 << 28)
994d54f62fSpatrick #define ENET_MMFR_ST		(1 << 30)
1004d54f62fSpatrick #define ENET_RCR_MII_MODE	(1 << 2)
1014d54f62fSpatrick #define ENET_RCR_PROM		(1 << 3)
1024d54f62fSpatrick #define ENET_RCR_FCE		(1 << 5)
1034d54f62fSpatrick #define ENET_RCR_RGMII_MODE	(1 << 6)
1044d54f62fSpatrick #define ENET_RCR_RMII_10T	(1 << 9)
1054d54f62fSpatrick #define ENET_RCR_MAX_FL(x)	(((x) & 0x3fff) << 16)
1064d54f62fSpatrick #define ENET_TCR_FDEN		(1 << 2)
1074d54f62fSpatrick #define ENET_EIR_MII		(1 << 23)
1084d54f62fSpatrick #define ENET_EIR_RXF		(1 << 25)
1094d54f62fSpatrick #define ENET_EIR_TXF		(1 << 27)
1104d54f62fSpatrick #define ENET_TFWR_STRFWD	(1 << 8)
11101946c72Spatrick #define ENET_RACC_SHIFT16	(1 << 7)
1124d54f62fSpatrick 
1134d54f62fSpatrick /* statistics counters */
1144d54f62fSpatrick 
1154d54f62fSpatrick /* 1588 control */
1164d54f62fSpatrick #define ENET_ATCR		0x400
1174d54f62fSpatrick #define ENET_ATVR		0x404
1184d54f62fSpatrick #define ENET_ATOFF		0x408
1194d54f62fSpatrick #define ENET_ATPER		0x40C
1204d54f62fSpatrick #define ENET_ATCOR		0x410
1214d54f62fSpatrick #define ENET_ATINC		0x414
1224d54f62fSpatrick #define ENET_ATSTMP		0x418
1234d54f62fSpatrick 
1244d54f62fSpatrick /* capture / compare block */
1254d54f62fSpatrick #define ENET_TGSR		0x604
1264d54f62fSpatrick #define ENET_TCSR0		0x608
1274d54f62fSpatrick #define ENET_TCCR0		0x60C
1284d54f62fSpatrick #define ENET_TCSR1		0x610
1294d54f62fSpatrick #define ENET_TCCR1		0x614
1304d54f62fSpatrick #define ENET_TCSR2		0x618
1314d54f62fSpatrick #define ENET_TCCR2		0x61C
1324d54f62fSpatrick #define ENET_TCSR3		0x620
1334d54f62fSpatrick #define ENET_TCCR3		0x624
1344d54f62fSpatrick 
1354d54f62fSpatrick #define ENET_MII_CLK		2500000
1364d54f62fSpatrick #define ENET_ALIGNMENT		16
1374d54f62fSpatrick 
1384d54f62fSpatrick #define HREAD4(sc, reg)							\
1394d54f62fSpatrick 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
1404d54f62fSpatrick #define HWRITE4(sc, reg, val)						\
1414d54f62fSpatrick 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
1424d54f62fSpatrick #define HSET4(sc, reg, bits)						\
1434d54f62fSpatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
1444d54f62fSpatrick #define HCLR4(sc, reg, bits)						\
1454d54f62fSpatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
1464d54f62fSpatrick 
14701946c72Spatrick #define ENET_MAX_BUF_SIZE	1522
1484d54f62fSpatrick #define ENET_MAX_PKT_SIZE	1536
1494d54f62fSpatrick 
1504d54f62fSpatrick #define ENET_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
1514d54f62fSpatrick 
1524d54f62fSpatrick /* buffer descriptor status bits */
1534d54f62fSpatrick #define ENET_RXD_EMPTY		(1 << 15)
1544d54f62fSpatrick #define ENET_RXD_WRAP		(1 << 13)
15501946c72Spatrick #define ENET_RXD_INTR		(1 << 12)
1564d54f62fSpatrick #define ENET_RXD_LAST		(1 << 11)
1574d54f62fSpatrick #define ENET_RXD_MISS		(1 << 8)
1584d54f62fSpatrick #define ENET_RXD_BC		(1 << 7)
1594d54f62fSpatrick #define ENET_RXD_MC		(1 << 6)
1604d54f62fSpatrick #define ENET_RXD_LG		(1 << 5)
1614d54f62fSpatrick #define ENET_RXD_NO		(1 << 4)
1624d54f62fSpatrick #define ENET_RXD_CR		(1 << 2)
1634d54f62fSpatrick #define ENET_RXD_OV		(1 << 1)
1644d54f62fSpatrick #define ENET_RXD_TR		(1 << 0)
1654d54f62fSpatrick 
1664d54f62fSpatrick #define ENET_TXD_READY		(1 << 15)
1674d54f62fSpatrick #define ENET_TXD_WRAP		(1 << 13)
16801946c72Spatrick #define ENET_TXD_INTR		(1 << 12)
1694d54f62fSpatrick #define ENET_TXD_LAST		(1 << 11)
1704d54f62fSpatrick #define ENET_TXD_TC		(1 << 10)
1714d54f62fSpatrick #define ENET_TXD_ABC		(1 << 9)
1724d54f62fSpatrick #define ENET_TXD_STATUS_MASK	0x3ff
1734d54f62fSpatrick 
1744d54f62fSpatrick #ifdef ENET_ENHANCED_BD
1754d54f62fSpatrick /* enhanced */
1764d54f62fSpatrick #define ENET_RXD_INT		(1 << 23)
1774d54f62fSpatrick 
1784d54f62fSpatrick #define ENET_TXD_INT		(1 << 30)
1794d54f62fSpatrick #endif
1804d54f62fSpatrick 
18101946c72Spatrick struct fec_buf {
18201946c72Spatrick 	bus_dmamap_t	 fb_map;
18301946c72Spatrick 	struct mbuf	*fb_m;
18401946c72Spatrick 	struct mbuf	*fb_m0;
1854d54f62fSpatrick };
1864d54f62fSpatrick 
18701946c72Spatrick /* what should we use? */
18801946c72Spatrick #define ENET_NTXDESC	256
18901946c72Spatrick #define ENET_NTXSEGS	16
19001946c72Spatrick #define ENET_NRXDESC	256
19101946c72Spatrick 
19201946c72Spatrick struct fec_dmamem {
19301946c72Spatrick 	bus_dmamap_t		 fdm_map;
19401946c72Spatrick 	bus_dma_segment_t	 fdm_seg;
19501946c72Spatrick 	size_t			 fdm_size;
19601946c72Spatrick 	caddr_t			 fdm_kva;
19701946c72Spatrick };
19801946c72Spatrick #define ENET_DMA_MAP(_fdm)	((_fdm)->fdm_map)
19901946c72Spatrick #define ENET_DMA_LEN(_fdm)	((_fdm)->fdm_size)
20001946c72Spatrick #define ENET_DMA_DVA(_fdm)	((_fdm)->fdm_map->dm_segs[0].ds_addr)
20101946c72Spatrick #define ENET_DMA_KVA(_fdm)	((void *)(_fdm)->fdm_kva)
20201946c72Spatrick 
20301946c72Spatrick struct fec_desc {
20401946c72Spatrick 	uint16_t fd_len;		/* payload's length in bytes */
20501946c72Spatrick 	uint16_t fd_status;		/* BD's status (see datasheet) */
20601946c72Spatrick 	uint32_t fd_addr;		/* payload's buffer address */
2074d54f62fSpatrick #ifdef ENET_ENHANCED_BD
20801946c72Spatrick 	uint32_t fd_enhanced_status;	/* enhanced status with IEEE 1588 */
20901946c72Spatrick 	uint32_t fd_reserved0;		/* reserved */
21001946c72Spatrick 	uint32_t fd_update_done;	/* buffer descriptor update done */
21101946c72Spatrick 	uint32_t fd_timestamp;		/* IEEE 1588 timestamp */
21201946c72Spatrick 	uint32_t fd_reserved1[2];	/* reserved */
2134d54f62fSpatrick #endif
2144d54f62fSpatrick };
2154d54f62fSpatrick 
2164d54f62fSpatrick struct fec_softc {
2174d54f62fSpatrick 	struct device		sc_dev;
2184d54f62fSpatrick 	struct arpcom		sc_ac;
2194d54f62fSpatrick 	struct mii_data		sc_mii;
2204d54f62fSpatrick 	int			sc_node;
2214d54f62fSpatrick 	bus_space_tag_t		sc_iot;
2224d54f62fSpatrick 	bus_space_handle_t	sc_ioh;
22394dcbd5cSpatrick 	void			*sc_ih[3]; /* Interrupt handler */
22401946c72Spatrick 	bus_dma_tag_t		sc_dmat;
22501946c72Spatrick 
22601946c72Spatrick 	struct fec_dmamem	*sc_txring;
22701946c72Spatrick 	struct fec_buf		*sc_txbuf;
22801946c72Spatrick 	struct fec_desc		*sc_txdesc;
22901946c72Spatrick 	int			 sc_tx_prod;
23001946c72Spatrick 	int			 sc_tx_cnt;
23101946c72Spatrick 	int			 sc_tx_cons;
23201946c72Spatrick 	int			 sc_tx_bounce;
23301946c72Spatrick 
23401946c72Spatrick 	struct fec_dmamem	*sc_rxring;
23501946c72Spatrick 	struct fec_buf		*sc_rxbuf;
23601946c72Spatrick 	struct fec_desc		*sc_rxdesc;
23701946c72Spatrick 	int			 sc_rx_prod;
23801946c72Spatrick 	struct if_rxring	 sc_rx_ring;
23901946c72Spatrick 	int			 sc_rx_cons;
24001946c72Spatrick 
2414d54f62fSpatrick 	struct timeout		sc_tick;
2424d54f62fSpatrick 	uint32_t		sc_phy_speed;
2434d54f62fSpatrick };
2444d54f62fSpatrick 
2454d54f62fSpatrick struct fec_softc *fec_sc;
2464d54f62fSpatrick 
2474d54f62fSpatrick int fec_match(struct device *, void *, void *);
2484d54f62fSpatrick void fec_attach(struct device *, struct device *, void *);
2494d54f62fSpatrick void fec_phy_init(struct fec_softc *, struct mii_softc *);
2504d54f62fSpatrick int fec_ioctl(struct ifnet *, u_long, caddr_t);
2514d54f62fSpatrick void fec_start(struct ifnet *);
25201946c72Spatrick int fec_encap(struct fec_softc *, struct mbuf *, int *);
2534d54f62fSpatrick void fec_init_txd(struct fec_softc *);
2544d54f62fSpatrick void fec_init_rxd(struct fec_softc *);
2554d54f62fSpatrick void fec_init(struct fec_softc *);
2564d54f62fSpatrick void fec_stop(struct fec_softc *);
2574d54f62fSpatrick void fec_iff(struct fec_softc *);
2584d54f62fSpatrick int fec_intr(void *);
25901946c72Spatrick void fec_tx_proc(struct fec_softc *);
26001946c72Spatrick void fec_rx_proc(struct fec_softc *);
2614d54f62fSpatrick void fec_tick(void *);
2624d54f62fSpatrick int fec_miibus_readreg(struct device *, int, int);
2634d54f62fSpatrick void fec_miibus_writereg(struct device *, int, int, int);
2644d54f62fSpatrick void fec_miibus_statchg(struct device *);
2654d54f62fSpatrick int fec_ifmedia_upd(struct ifnet *);
2664d54f62fSpatrick void fec_ifmedia_sts(struct ifnet *, struct ifmediareq *);
26701946c72Spatrick struct fec_dmamem *fec_dmamem_alloc(struct fec_softc *, bus_size_t, bus_size_t);
26801946c72Spatrick void fec_dmamem_free(struct fec_softc *, struct fec_dmamem *);
26901946c72Spatrick struct mbuf *fec_alloc_mbuf(struct fec_softc *, bus_dmamap_t);
27001946c72Spatrick void fec_fill_rx_ring(struct fec_softc *);
2714d54f62fSpatrick 
2729fdf0c62Smpi const struct cfattach fec_ca = {
2734d54f62fSpatrick 	sizeof (struct fec_softc), fec_match, fec_attach
2744d54f62fSpatrick };
2754d54f62fSpatrick 
2764d54f62fSpatrick struct cfdriver fec_cd = {
2774d54f62fSpatrick 	NULL, "fec", DV_IFNET
2784d54f62fSpatrick };
2794d54f62fSpatrick 
2804d54f62fSpatrick int
fec_match(struct device * parent,void * match,void * aux)2814d54f62fSpatrick fec_match(struct device *parent, void *match, void *aux)
2824d54f62fSpatrick {
2834d54f62fSpatrick 	struct fdt_attach_args *faa = aux;
2844d54f62fSpatrick 
285f4db5f85Spatrick 	return (OF_is_compatible(faa->fa_node, "fsl,imx6q-fec") ||
28660ab8968Skettenis 	    OF_is_compatible(faa->fa_node, "fsl,imx6sx-fec") ||
287f4db5f85Spatrick 	    OF_is_compatible(faa->fa_node, "fsl,imx8mq-fec"));
2884d54f62fSpatrick }
2894d54f62fSpatrick 
2904d54f62fSpatrick void
fec_attach(struct device * parent,struct device * self,void * aux)2914d54f62fSpatrick fec_attach(struct device *parent, struct device *self, void *aux)
2924d54f62fSpatrick {
2934d54f62fSpatrick 	struct fec_softc *sc = (struct fec_softc *) self;
2944d54f62fSpatrick 	struct fdt_attach_args *faa = aux;
29501946c72Spatrick 	struct fec_buf *txb, *rxb;
2964d54f62fSpatrick 	struct mii_data *mii;
2974d54f62fSpatrick 	struct mii_softc *child;
2984d54f62fSpatrick 	struct ifnet *ifp;
2994d54f62fSpatrick 	uint32_t phy_reset_gpio[3];
3004d54f62fSpatrick 	uint32_t phy_reset_duration;
30101946c72Spatrick 	int i, s;
3024d54f62fSpatrick 
3034d54f62fSpatrick 	if (faa->fa_nreg < 1)
3044d54f62fSpatrick 		return;
3054d54f62fSpatrick 
3064d54f62fSpatrick 	sc->sc_node = faa->fa_node;
3074d54f62fSpatrick 	sc->sc_iot = faa->fa_iot;
3084d54f62fSpatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
3094d54f62fSpatrick 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
3104d54f62fSpatrick 		panic("fec_attach: bus_space_map failed!");
3114d54f62fSpatrick 
31201946c72Spatrick 	sc->sc_dmat = faa->fa_dmat;
3134d54f62fSpatrick 
3144d54f62fSpatrick 	pinctrl_byname(faa->fa_node, "default");
3154d54f62fSpatrick 
3164d54f62fSpatrick 	/* power it up */
3174d54f62fSpatrick 	clock_enable_all(faa->fa_node);
3184d54f62fSpatrick 
3194d54f62fSpatrick 	/* reset PHY */
3204d54f62fSpatrick 	if (OF_getpropintarray(faa->fa_node, "phy-reset-gpios", phy_reset_gpio,
3214d54f62fSpatrick 	    sizeof(phy_reset_gpio)) == sizeof(phy_reset_gpio)) {
3224d54f62fSpatrick 		phy_reset_duration = OF_getpropint(faa->fa_node,
3234d54f62fSpatrick 		    "phy-reset-duration", 1);
3244d54f62fSpatrick 		if (phy_reset_duration > 1000)
3254d54f62fSpatrick 			phy_reset_duration = 1;
3264d54f62fSpatrick 
3274d54f62fSpatrick 		/*
3284d54f62fSpatrick 		 * The Linux people really screwed the pooch here.
3294d54f62fSpatrick 		 * The Linux kernel always treats the gpio as
3304d54f62fSpatrick 		 * active-low, even if it is marked as active-high in
3314d54f62fSpatrick 		 * the device tree.  As a result the device tree for
3324d54f62fSpatrick 		 * many boards incorrectly marks the gpio as
3334d54f62fSpatrick 		 * active-high.
3344d54f62fSpatrick 		 */
3354d54f62fSpatrick 		phy_reset_gpio[2] = GPIO_ACTIVE_LOW;
3364d54f62fSpatrick 		gpio_controller_config_pin(phy_reset_gpio, GPIO_CONFIG_OUTPUT);
3374d54f62fSpatrick 
3384d54f62fSpatrick 		/*
3394d54f62fSpatrick 		 * On some Cubox-i machines we need to hold the PHY in
3404d54f62fSpatrick 		 * reset a little bit longer than specified.
3414d54f62fSpatrick 		 */
3424d54f62fSpatrick 		gpio_controller_set_pin(phy_reset_gpio, 1);
3434d54f62fSpatrick 		delay((phy_reset_duration + 1) * 1000);
3444d54f62fSpatrick 		gpio_controller_set_pin(phy_reset_gpio, 0);
3454d54f62fSpatrick 		delay(1000);
3464d54f62fSpatrick 	}
3474d54f62fSpatrick 	printf("\n");
3484d54f62fSpatrick 
3494d54f62fSpatrick 	/* Figure out the hardware address. Must happen before reset. */
3504d54f62fSpatrick 	OF_getprop(faa->fa_node, "local-mac-address", sc->sc_ac.ac_enaddr,
3514d54f62fSpatrick 	    sizeof(sc->sc_ac.ac_enaddr));
3524d54f62fSpatrick 
3534d54f62fSpatrick 	/* reset the controller */
3544d54f62fSpatrick 	HSET4(sc, ENET_ECR, ENET_ECR_RESET);
3554d54f62fSpatrick 	while (HREAD4(sc, ENET_ECR) & ENET_ECR_ETHEREN)
3564d54f62fSpatrick 		continue;
3574d54f62fSpatrick 
3584d54f62fSpatrick 	HWRITE4(sc, ENET_EIMR, 0);
3594d54f62fSpatrick 	HWRITE4(sc, ENET_EIR, 0xffffffff);
3604d54f62fSpatrick 
36170e69ae2Spatrick 	sc->sc_ih[0] = fdt_intr_establish_idx(faa->fa_node, 0, IPL_NET,
36294dcbd5cSpatrick 	    fec_intr, sc, sc->sc_dev.dv_xname);
36370e69ae2Spatrick 	sc->sc_ih[1] = fdt_intr_establish_idx(faa->fa_node, 1, IPL_NET,
36494dcbd5cSpatrick 	    fec_intr, sc, sc->sc_dev.dv_xname);
36570e69ae2Spatrick 	sc->sc_ih[2] = fdt_intr_establish_idx(faa->fa_node, 2, IPL_NET,
3664d54f62fSpatrick 	    fec_intr, sc, sc->sc_dev.dv_xname);
3674d54f62fSpatrick 
36801946c72Spatrick 	/* Tx bounce buffer to align to 16. */
36901946c72Spatrick 	if (OF_is_compatible(faa->fa_node, "fsl,imx6q-fec"))
37001946c72Spatrick 		sc->sc_tx_bounce = 1;
3714d54f62fSpatrick 
37201946c72Spatrick 	/* Allocate Tx descriptor ring. */
37301946c72Spatrick 	sc->sc_txring = fec_dmamem_alloc(sc,
37401946c72Spatrick 	    ENET_NTXDESC * sizeof(struct fec_desc), 64);
37501946c72Spatrick 	if (sc->sc_txring == NULL) {
37601946c72Spatrick 		printf("%s: could not allocate Tx descriptor ring\n",
3774d54f62fSpatrick 		    sc->sc_dev.dv_xname);
3784d54f62fSpatrick 		goto bad;
3794d54f62fSpatrick 	}
38001946c72Spatrick 	sc->sc_txdesc = ENET_DMA_KVA(sc->sc_txring);
3814d54f62fSpatrick 
38201946c72Spatrick 	/* Allocate Tx descriptors. */
38301946c72Spatrick 	sc->sc_txbuf = malloc(sizeof(struct fec_buf) * ENET_NTXDESC,
38401946c72Spatrick 	    M_DEVBUF, M_WAITOK);
38501946c72Spatrick 	for (i = 0; i < ENET_NTXDESC; i++) {
38601946c72Spatrick 		txb = &sc->sc_txbuf[i];
38701946c72Spatrick 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, ENET_NTXSEGS,
38801946c72Spatrick 		    MCLBYTES, 0, BUS_DMA_WAITOK, &txb->fb_map);
38901946c72Spatrick 		txb->fb_m = txb->fb_m0 = NULL;
3904d54f62fSpatrick 	}
3914d54f62fSpatrick 
39201946c72Spatrick 	/* Allocate Rx descriptor ring. */
39301946c72Spatrick 	sc->sc_rxring = fec_dmamem_alloc(sc,
39401946c72Spatrick 	    ENET_NRXDESC * sizeof(struct fec_desc), 64);
39501946c72Spatrick 	if (sc->sc_rxring == NULL) {
39601946c72Spatrick 		printf("%s: could not allocate Rx descriptor ring\n",
3974d54f62fSpatrick 		    sc->sc_dev.dv_xname);
39801946c72Spatrick 		for (i = 0; i < ENET_NTXDESC; i++) {
39901946c72Spatrick 			txb = &sc->sc_txbuf[i];
40001946c72Spatrick 			bus_dmamap_destroy(sc->sc_dmat, txb->fb_map);
4014d54f62fSpatrick 		}
40201946c72Spatrick 		free(sc->sc_txbuf, M_DEVBUF,
40301946c72Spatrick 		    sizeof(struct fec_buf) * ENET_NTXDESC);
40401946c72Spatrick 		fec_dmamem_free(sc, sc->sc_txring);
40501946c72Spatrick 		goto bad;
4064d54f62fSpatrick 	}
40701946c72Spatrick 	sc->sc_rxdesc = ENET_DMA_KVA(sc->sc_rxring);
4084d54f62fSpatrick 
40901946c72Spatrick 	/* Allocate Rx descriptors. */
41001946c72Spatrick 	sc->sc_rxbuf = malloc(sizeof(struct fec_buf) * ENET_NRXDESC,
41101946c72Spatrick 	    M_DEVBUF, M_WAITOK);
41201946c72Spatrick 	for (i = 0; i < ENET_NRXDESC; i++) {
41301946c72Spatrick 		rxb = &sc->sc_rxbuf[i];
41401946c72Spatrick 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
41501946c72Spatrick 		    MCLBYTES, 0, BUS_DMA_WAITOK, &rxb->fb_map);
41601946c72Spatrick 		rxb->fb_m = NULL;
41701946c72Spatrick 	}
4184d54f62fSpatrick 
4194d54f62fSpatrick 	s = splnet();
4204d54f62fSpatrick 
4214d54f62fSpatrick 	ifp = &sc->sc_ac.ac_if;
4224d54f62fSpatrick 	ifp->if_softc = sc;
4234d54f62fSpatrick 	strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
4244d54f62fSpatrick 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
4254d54f62fSpatrick 	ifp->if_ioctl = fec_ioctl;
4264d54f62fSpatrick 	ifp->if_start = fec_start;
4274d54f62fSpatrick 	ifp->if_capabilities = IFCAP_VLAN_MTU;
4284d54f62fSpatrick 
4294d54f62fSpatrick 	printf("%s: address %s\n", sc->sc_dev.dv_xname,
4304d54f62fSpatrick 	    ether_sprintf(sc->sc_ac.ac_enaddr));
4314d54f62fSpatrick 
4324d54f62fSpatrick 	/*
4334d54f62fSpatrick 	 * Initialize the MII clock.  The formula is:
4344d54f62fSpatrick 	 *
4354d54f62fSpatrick 	 * ENET_MII_CLK = ref_freq / ((phy_speed + 1) x 2)
4364d54f62fSpatrick 	 * phy_speed = (((ref_freq / ENET_MII_CLK) / 2) - 1)
4374d54f62fSpatrick 	 */
4384d54f62fSpatrick 	sc->sc_phy_speed = clock_get_frequency(sc->sc_node, "ipg");
4394d54f62fSpatrick 	sc->sc_phy_speed = (sc->sc_phy_speed + (ENET_MII_CLK - 1)) / ENET_MII_CLK;
4404d54f62fSpatrick 	sc->sc_phy_speed = (sc->sc_phy_speed / 2) - 1;
4414d54f62fSpatrick 	HWRITE4(sc, ENET_MSCR, (sc->sc_phy_speed << 1) | 0x100);
4424d54f62fSpatrick 
4434d54f62fSpatrick 	/* Initialize MII/media info. */
4444d54f62fSpatrick 	mii = &sc->sc_mii;
4454d54f62fSpatrick 	mii->mii_ifp = ifp;
4464d54f62fSpatrick 	mii->mii_readreg = fec_miibus_readreg;
4474d54f62fSpatrick 	mii->mii_writereg = fec_miibus_writereg;
4484d54f62fSpatrick 	mii->mii_statchg = fec_miibus_statchg;
4494d54f62fSpatrick 
4504d54f62fSpatrick 	ifmedia_init(&mii->mii_media, 0, fec_ifmedia_upd, fec_ifmedia_sts);
4514d54f62fSpatrick 	mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
4524d54f62fSpatrick 
4534d54f62fSpatrick 	child = LIST_FIRST(&mii->mii_phys);
4544d54f62fSpatrick 	if (child)
4554d54f62fSpatrick 		fec_phy_init(sc, child);
4564d54f62fSpatrick 
4574d54f62fSpatrick 	if (LIST_FIRST(&mii->mii_phys) == NULL) {
4584d54f62fSpatrick 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL);
4594d54f62fSpatrick 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE);
4604d54f62fSpatrick 	} else
4614d54f62fSpatrick 		ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
4624d54f62fSpatrick 
4634d54f62fSpatrick 	if_attach(ifp);
4644d54f62fSpatrick 	ether_ifattach(ifp);
4654d54f62fSpatrick 	splx(s);
4664d54f62fSpatrick 
4674d54f62fSpatrick 	timeout_set(&sc->sc_tick, fec_tick, sc);
4684d54f62fSpatrick 
4694d54f62fSpatrick 	fec_sc = sc;
4704d54f62fSpatrick 	return;
4714d54f62fSpatrick 
4724d54f62fSpatrick bad:
4734d54f62fSpatrick 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
4744d54f62fSpatrick }
4754d54f62fSpatrick 
4764d54f62fSpatrick void
fec_phy_init(struct fec_softc * sc,struct mii_softc * child)4774d54f62fSpatrick fec_phy_init(struct fec_softc *sc, struct mii_softc *child)
4784d54f62fSpatrick {
4794d54f62fSpatrick 	struct device *dev = (struct device *)sc;
4804d54f62fSpatrick 	int phy = child->mii_phy;
4814d54f62fSpatrick 	uint32_t reg;
4824d54f62fSpatrick 
4834d54f62fSpatrick 	if (child->mii_oui == MII_OUI_ATHEROS &&
4844d54f62fSpatrick 	    child->mii_model == MII_MODEL_ATHEROS_AR8035) {
4854d54f62fSpatrick 		/* disable SmartEEE */
4864d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x0003);
4874d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, 0x805d);
4884d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x4003);
4894d54f62fSpatrick 		reg = fec_miibus_readreg(dev, phy, 0x0e);
4904d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, reg & ~0x0100);
4914d54f62fSpatrick 
4924d54f62fSpatrick 		/* enable 125MHz clk output */
4934d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x0007);
4944d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, 0x8016);
4954d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x4007);
4964d54f62fSpatrick 
4974d54f62fSpatrick 		reg = fec_miibus_readreg(dev, phy, 0x0e) & 0xffe3;
4984d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, reg | 0x18);
4994d54f62fSpatrick 
5004d54f62fSpatrick 		/* tx clock delay */
5014d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x1d, 0x0005);
5024d54f62fSpatrick 		reg = fec_miibus_readreg(dev, phy, 0x1e);
5034d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x1e, reg | 0x0100);
5044d54f62fSpatrick 
5054d54f62fSpatrick 		PHY_RESET(child);
5064d54f62fSpatrick 	}
5074d54f62fSpatrick 
5084d54f62fSpatrick 	if (child->mii_oui == MII_OUI_MICREL &&
5094d54f62fSpatrick 	    child->mii_model == MII_MODEL_MICREL_KSZ9021) {
5104d54f62fSpatrick 		uint32_t rxc, rxdv, txc, txen;
5114d54f62fSpatrick 		uint32_t rxd0, rxd1, rxd2, rxd3;
5124d54f62fSpatrick 		uint32_t txd0, txd1, txd2, txd3;
5138942cbfcSpatrick 		uint32_t val, phy;
5148942cbfcSpatrick 		int node;
5154d54f62fSpatrick 
5168942cbfcSpatrick 		node = sc->sc_node;
5178942cbfcSpatrick 		phy = OF_getpropint(sc->sc_node, "phy-handle", 0);
5188942cbfcSpatrick 		if (phy)
5198942cbfcSpatrick 			node = OF_getnodebyphandle(phy);
5208942cbfcSpatrick 		rxc = OF_getpropint(node, "rxc-skew-ps", 1400) / 200;
5218942cbfcSpatrick 		rxdv = OF_getpropint(node, "rxdv-skew-ps", 1400) / 200;
5228942cbfcSpatrick 		txc = OF_getpropint(node, "txc-skew-ps", 1400) / 200;
5238942cbfcSpatrick 		txen = OF_getpropint(node, "txen-skew-ps", 1400) / 200;
5248942cbfcSpatrick 		rxd0 = OF_getpropint(node, "rxd0-skew-ps", 1400) / 200;
5258942cbfcSpatrick 		rxd1 = OF_getpropint(node, "rxd1-skew-ps", 1400) / 200;
5268942cbfcSpatrick 		rxd2 = OF_getpropint(node, "rxd2-skew-ps", 1400) / 200;
5278942cbfcSpatrick 		rxd3 = OF_getpropint(node, "rxd3-skew-ps", 1400) / 200;
5288942cbfcSpatrick 		txd0 = OF_getpropint(node, "txd0-skew-ps", 1400) / 200;
5298942cbfcSpatrick 		txd1 = OF_getpropint(node, "txd1-skew-ps", 1400) / 200;
5308942cbfcSpatrick 		txd2 = OF_getpropint(node, "txd2-skew-ps", 1400) / 200;
5318942cbfcSpatrick 		txd3 = OF_getpropint(node, "txd3-skew-ps", 1400) / 200;
5324d54f62fSpatrick 
5334d54f62fSpatrick 		val = ((rxc & 0xf) << 12) | ((rxdv & 0xf) << 8) |
5344d54f62fSpatrick 		    ((txc & 0xf) << 4) | ((txen & 0xf) << 0);
5354d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0b, 0x8104);
5364d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0c, val);
5374d54f62fSpatrick 
5384d54f62fSpatrick 		val = ((rxd3 & 0xf) << 12) | ((rxd2 & 0xf) << 8) |
5394d54f62fSpatrick 		    ((rxd1 & 0xf) << 4) | ((rxd0 & 0xf) << 0);
5404d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0b, 0x8105);
5414d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0c, val);
5424d54f62fSpatrick 
5434d54f62fSpatrick 		val = ((txd3 & 0xf) << 12) | ((txd2 & 0xf) << 8) |
5444d54f62fSpatrick 		    ((txd1 & 0xf) << 4) | ((txd0 & 0xf) << 0);
5454d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0b, 0x8106);
5464d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0c, val);
5474d54f62fSpatrick 	}
5484d54f62fSpatrick 
5494d54f62fSpatrick 	if (child->mii_oui == MII_OUI_MICREL &&
5504d54f62fSpatrick 	    child->mii_model == MII_MODEL_MICREL_KSZ9031) {
5514d54f62fSpatrick 		uint32_t rxc, rxdv, txc, txen;
5524d54f62fSpatrick 		uint32_t rxd0, rxd1, rxd2, rxd3;
5534d54f62fSpatrick 		uint32_t txd0, txd1, txd2, txd3;
5548942cbfcSpatrick 		uint32_t val, phy;
5558942cbfcSpatrick 		int node;
5564d54f62fSpatrick 
5578942cbfcSpatrick 		node = sc->sc_node;
5588942cbfcSpatrick 		phy = OF_getpropint(sc->sc_node, "phy-handle", 0);
5598942cbfcSpatrick 		if (phy)
5608942cbfcSpatrick 			node = OF_getnodebyphandle(phy);
5618942cbfcSpatrick 		rxc = OF_getpropint(node, "rxc-skew-ps", 900) / 60;
5628942cbfcSpatrick 		rxdv = OF_getpropint(node, "rxdv-skew-ps", 420) / 60;
5638942cbfcSpatrick 		txc = OF_getpropint(node, "txc-skew-ps", 900) / 60;
5648942cbfcSpatrick 		txen = OF_getpropint(node, "txen-skew-ps", 420) / 60;
5658942cbfcSpatrick 		rxd0 = OF_getpropint(node, "rxd0-skew-ps", 420) / 60;
5668942cbfcSpatrick 		rxd1 = OF_getpropint(node, "rxd1-skew-ps", 420) / 60;
5678942cbfcSpatrick 		rxd2 = OF_getpropint(node, "rxd2-skew-ps", 420) / 60;
5688942cbfcSpatrick 		rxd3 = OF_getpropint(node, "rxd3-skew-ps", 420) / 60;
5698942cbfcSpatrick 		txd0 = OF_getpropint(node, "txd0-skew-ps", 420) / 60;
5708942cbfcSpatrick 		txd1 = OF_getpropint(node, "txd1-skew-ps", 420) / 60;
5718942cbfcSpatrick 		txd2 = OF_getpropint(node, "txd2-skew-ps", 420) / 60;
5728942cbfcSpatrick 		txd3 = OF_getpropint(node, "txd3-skew-ps", 420) / 60;
5734d54f62fSpatrick 
5744d54f62fSpatrick 		val = ((rxdv & 0xf) << 4) || ((txen & 0xf) << 0);
5754d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
5764d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, 0x0004);
5774d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
5784d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, val);
5794d54f62fSpatrick 
5804d54f62fSpatrick 		val = ((rxd3 & 0xf) << 12) | ((rxd2 & 0xf) << 8) |
5814d54f62fSpatrick 		    ((rxd1 & 0xf) << 4) | ((rxd0 & 0xf) << 0);
5824d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
5834d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, 0x0005);
5844d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
5854d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, val);
5864d54f62fSpatrick 
5874d54f62fSpatrick 		val = ((txd3 & 0xf) << 12) | ((txd2 & 0xf) << 8) |
5884d54f62fSpatrick 		    ((txd1 & 0xf) << 4) | ((txd0 & 0xf) << 0);
5894d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
5904d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, 0x0006);
5914d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
5924d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, val);
5934d54f62fSpatrick 
5944d54f62fSpatrick 		val = ((txc & 0x1f) << 5) || ((rxc & 0x1f) << 0);
5954d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x0002);
5964d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, 0x0008);
5974d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0d, 0x4002);
5984d54f62fSpatrick 		fec_miibus_writereg(dev, phy, 0x0e, val);
5994d54f62fSpatrick 	}
6004d54f62fSpatrick }
6014d54f62fSpatrick 
6024d54f62fSpatrick void
fec_init_rxd(struct fec_softc * sc)6034d54f62fSpatrick fec_init_rxd(struct fec_softc *sc)
6044d54f62fSpatrick {
60501946c72Spatrick 	struct fec_desc *rxd;
6064d54f62fSpatrick 
60701946c72Spatrick 	sc->sc_rx_prod = sc->sc_rx_cons = 0;
6084d54f62fSpatrick 
60901946c72Spatrick 	memset(sc->sc_rxdesc, 0, ENET_DMA_LEN(sc->sc_rxring));
61001946c72Spatrick 	rxd = &sc->sc_rxdesc[ENET_NRXDESC - 1];
61101946c72Spatrick 	rxd->fd_status = ENET_RXD_WRAP;
6124d54f62fSpatrick }
6134d54f62fSpatrick 
6144d54f62fSpatrick void
fec_init_txd(struct fec_softc * sc)6154d54f62fSpatrick fec_init_txd(struct fec_softc *sc)
6164d54f62fSpatrick {
61701946c72Spatrick 	struct fec_desc *txd;
6184d54f62fSpatrick 
61901946c72Spatrick 	sc->sc_tx_prod = sc->sc_tx_cons = 0;
62001946c72Spatrick 	sc->sc_tx_cnt = 0;
6214d54f62fSpatrick 
62201946c72Spatrick 	memset(sc->sc_txdesc, 0, ENET_DMA_LEN(sc->sc_txring));
62301946c72Spatrick 	txd = &sc->sc_txdesc[ENET_NTXDESC - 1];
62401946c72Spatrick 	txd->fd_status = ENET_TXD_WRAP;
6254d54f62fSpatrick }
6264d54f62fSpatrick 
6274d54f62fSpatrick void
fec_init(struct fec_softc * sc)6284d54f62fSpatrick fec_init(struct fec_softc *sc)
6294d54f62fSpatrick {
6304d54f62fSpatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
6314d54f62fSpatrick 	int speed = 0;
6324d54f62fSpatrick 
6334d54f62fSpatrick 	/* reset the controller */
6344d54f62fSpatrick 	HSET4(sc, ENET_ECR, ENET_ECR_RESET);
6354d54f62fSpatrick 	while (HREAD4(sc, ENET_ECR) & ENET_ECR_ETHEREN)
6364d54f62fSpatrick 		continue;
6374d54f62fSpatrick 
6384d54f62fSpatrick 	/* set hw address */
6394d54f62fSpatrick 	HWRITE4(sc, ENET_PALR,
6404d54f62fSpatrick 	    (sc->sc_ac.ac_enaddr[0] << 24) |
6414d54f62fSpatrick 	    (sc->sc_ac.ac_enaddr[1] << 16) |
6424d54f62fSpatrick 	    (sc->sc_ac.ac_enaddr[2] << 8) |
6434d54f62fSpatrick 	     sc->sc_ac.ac_enaddr[3]);
6444d54f62fSpatrick 	HWRITE4(sc, ENET_PAUR,
6454d54f62fSpatrick 	    (sc->sc_ac.ac_enaddr[4] << 24) |
6464d54f62fSpatrick 	    (sc->sc_ac.ac_enaddr[5] << 16));
6474d54f62fSpatrick 
6484d54f62fSpatrick 	/* clear outstanding interrupts */
6494d54f62fSpatrick 	HWRITE4(sc, ENET_EIR, 0xffffffff);
6504d54f62fSpatrick 
6514d54f62fSpatrick 	/* set max receive buffer size, 3-0 bits always zero for alignment */
6524d54f62fSpatrick 	HWRITE4(sc, ENET_MRBR, ENET_MAX_PKT_SIZE);
6534d54f62fSpatrick 
6544d54f62fSpatrick 	/* init descriptor */
6554d54f62fSpatrick 	fec_init_txd(sc);
6564d54f62fSpatrick 	fec_init_rxd(sc);
6574d54f62fSpatrick 
65801946c72Spatrick 	/* fill RX ring */
65901946c72Spatrick 	if_rxr_init(&sc->sc_rx_ring, 2, ENET_NRXDESC);
66001946c72Spatrick 	fec_fill_rx_ring(sc);
66101946c72Spatrick 
66201946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring),
66301946c72Spatrick 	    0, ENET_DMA_LEN(sc->sc_txring), BUS_DMASYNC_PREWRITE);
66401946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_rxring),
66501946c72Spatrick 	    0, ENET_DMA_LEN(sc->sc_rxring), BUS_DMASYNC_PREWRITE);
66601946c72Spatrick 
66701946c72Spatrick 	/* set descriptor */
66801946c72Spatrick 	HWRITE4(sc, ENET_TDSR, ENET_DMA_DVA(sc->sc_txring));
66901946c72Spatrick 	HWRITE4(sc, ENET_RDSR, ENET_DMA_DVA(sc->sc_rxring));
67001946c72Spatrick 
6714d54f62fSpatrick 	/* set it to full-duplex */
6724d54f62fSpatrick 	HWRITE4(sc, ENET_TCR, ENET_TCR_FDEN);
6734d54f62fSpatrick 
6744d54f62fSpatrick 	/*
6754d54f62fSpatrick 	 * Set max frame length to 1518 or 1522 with VLANs,
6764d54f62fSpatrick 	 * pause frames and promisc mode.
6774d54f62fSpatrick 	 * XXX: RGMII mode - phy dependant
6784d54f62fSpatrick 	 */
6794d54f62fSpatrick 	HWRITE4(sc, ENET_RCR,
6804d54f62fSpatrick 	    ENET_RCR_MAX_FL(1522) | ENET_RCR_RGMII_MODE | ENET_RCR_MII_MODE |
6814d54f62fSpatrick 	    ENET_RCR_FCE);
6824d54f62fSpatrick 
6834d54f62fSpatrick 	HWRITE4(sc, ENET_MSCR, (sc->sc_phy_speed << 1) | 0x100);
6844d54f62fSpatrick 
68501946c72Spatrick 	HWRITE4(sc, ENET_RACC, ENET_RACC_SHIFT16);
68601946c72Spatrick 	HWRITE4(sc, ENET_FTRL, ENET_MAX_BUF_SIZE);
68701946c72Spatrick 
688*4b1a56afSjsg 	/* RX FIFO threshold and pause */
6894d54f62fSpatrick 	HWRITE4(sc, ENET_RSEM, 0x84);
6904d54f62fSpatrick 	HWRITE4(sc, ENET_RSFL, 16);
6914d54f62fSpatrick 	HWRITE4(sc, ENET_RAEM, 8);
6924d54f62fSpatrick 	HWRITE4(sc, ENET_RAFL, 8);
6934d54f62fSpatrick 	HWRITE4(sc, ENET_OPD, 0xFFF0);
6944d54f62fSpatrick 
6954d54f62fSpatrick 	/* do store and forward, only i.MX6, needs to be set correctly else */
6964d54f62fSpatrick 	HWRITE4(sc, ENET_TFWR, ENET_TFWR_STRFWD);
6974d54f62fSpatrick 
6984d54f62fSpatrick 	/* enable gigabit-ethernet and set it to support little-endian */
6994d54f62fSpatrick 	switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
7004d54f62fSpatrick 	case IFM_1000_T:  /* Gigabit */
7014d54f62fSpatrick 		speed |= ENET_ECR_SPEED;
7024d54f62fSpatrick 		break;
7034d54f62fSpatrick 	default:
7044d54f62fSpatrick 		speed &= ~ENET_ECR_SPEED;
7054d54f62fSpatrick 	}
7064d54f62fSpatrick 	HWRITE4(sc, ENET_ECR, ENET_ECR_ETHEREN | speed | ENET_ECR_DBSWP);
7074d54f62fSpatrick 
7084d54f62fSpatrick #ifdef ENET_ENHANCED_BD
7094d54f62fSpatrick 	HSET4(sc, ENET_ECR, ENET_ECR_EN1588);
7104d54f62fSpatrick #endif
7114d54f62fSpatrick 
7124d54f62fSpatrick 	/* rx descriptors are ready */
7134d54f62fSpatrick 	HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR);
7144d54f62fSpatrick 
7154d54f62fSpatrick 	/* program promiscuous mode and multicast filters */
7164d54f62fSpatrick 	fec_iff(sc);
7174d54f62fSpatrick 
7184d54f62fSpatrick 	timeout_add_sec(&sc->sc_tick, 1);
7194d54f62fSpatrick 
7204d54f62fSpatrick 	/* Indicate we are up and running. */
7214d54f62fSpatrick 	ifp->if_flags |= IFF_RUNNING;
7224d54f62fSpatrick 	ifq_clr_oactive(&ifp->if_snd);
7234d54f62fSpatrick 
7244d54f62fSpatrick 	/* enable interrupts for tx/rx */
7254d54f62fSpatrick 	HWRITE4(sc, ENET_EIMR, ENET_EIR_TXF | ENET_EIR_RXF);
7264d54f62fSpatrick 
7274d54f62fSpatrick 	fec_start(ifp);
7284d54f62fSpatrick }
7294d54f62fSpatrick 
7304d54f62fSpatrick void
fec_stop(struct fec_softc * sc)7314d54f62fSpatrick fec_stop(struct fec_softc *sc)
7324d54f62fSpatrick {
7334d54f62fSpatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
73401946c72Spatrick 	struct fec_buf *txb, *rxb;
73501946c72Spatrick 	int i;
7364d54f62fSpatrick 
7374d54f62fSpatrick 	/*
7384d54f62fSpatrick 	 * Mark the interface down and cancel the watchdog timer.
7394d54f62fSpatrick 	 */
7404d54f62fSpatrick 	ifp->if_flags &= ~IFF_RUNNING;
7414d54f62fSpatrick 	ifp->if_timer = 0;
7424d54f62fSpatrick 	ifq_clr_oactive(&ifp->if_snd);
7434d54f62fSpatrick 
7444d54f62fSpatrick 	timeout_del(&sc->sc_tick);
7454d54f62fSpatrick 
7464d54f62fSpatrick 	/* reset the controller */
7474d54f62fSpatrick 	HSET4(sc, ENET_ECR, ENET_ECR_RESET);
7484d54f62fSpatrick 	while (HREAD4(sc, ENET_ECR) & ENET_ECR_ETHEREN)
7494d54f62fSpatrick 		continue;
7504c63cebaSpatrick 
7514c63cebaSpatrick 	HWRITE4(sc, ENET_MSCR, (sc->sc_phy_speed << 1) | 0x100);
75201946c72Spatrick 
75301946c72Spatrick 	for (i = 0; i < ENET_NTXDESC; i++) {
75401946c72Spatrick 		txb = &sc->sc_txbuf[i];
75501946c72Spatrick 		if (txb->fb_m == NULL)
75601946c72Spatrick 			continue;
75701946c72Spatrick 		bus_dmamap_sync(sc->sc_dmat, txb->fb_map, 0,
75801946c72Spatrick 		    txb->fb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
75901946c72Spatrick 		bus_dmamap_unload(sc->sc_dmat, txb->fb_map);
76001946c72Spatrick 		m_freem(txb->fb_m);
76101946c72Spatrick 		m_freem(txb->fb_m0);
76201946c72Spatrick 		txb->fb_m = txb->fb_m0 = NULL;
76301946c72Spatrick 	}
76401946c72Spatrick 	for (i = 0; i < ENET_NRXDESC; i++) {
76501946c72Spatrick 		rxb = &sc->sc_rxbuf[i];
76601946c72Spatrick 		if (rxb->fb_m == NULL)
76701946c72Spatrick 			continue;
76801946c72Spatrick 		bus_dmamap_sync(sc->sc_dmat, rxb->fb_map, 0,
76901946c72Spatrick 		    rxb->fb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
77001946c72Spatrick 		bus_dmamap_unload(sc->sc_dmat, rxb->fb_map);
77101946c72Spatrick 		if_rxr_put(&sc->sc_rx_ring, 1);
77201946c72Spatrick 		rxb->fb_m = NULL;
77301946c72Spatrick 	}
7744d54f62fSpatrick }
7754d54f62fSpatrick 
7764d54f62fSpatrick void
fec_iff(struct fec_softc * sc)7774d54f62fSpatrick fec_iff(struct fec_softc *sc)
7784d54f62fSpatrick {
7794d54f62fSpatrick 	struct arpcom *ac = &sc->sc_ac;
7804d54f62fSpatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
7814d54f62fSpatrick 	struct ether_multi *enm;
7824d54f62fSpatrick 	struct ether_multistep step;
7834d54f62fSpatrick 	uint64_t ghash = 0, ihash = 0;
7844d54f62fSpatrick 	uint32_t h;
7854d54f62fSpatrick 
7864d54f62fSpatrick 	ifp->if_flags &= ~IFF_ALLMULTI;
7874d54f62fSpatrick 
7884d54f62fSpatrick 	if (ifp->if_flags & IFF_PROMISC) {
7894d54f62fSpatrick 		ifp->if_flags |= IFF_ALLMULTI;
7904d54f62fSpatrick 		ihash = 0xffffffffffffffffLLU;
7914d54f62fSpatrick 	} else if (ac->ac_multirangecnt > 0) {
7924d54f62fSpatrick 		ifp->if_flags |= IFF_ALLMULTI;
7934d54f62fSpatrick 		ghash = 0xffffffffffffffffLLU;
7944d54f62fSpatrick 	} else {
7954d54f62fSpatrick 		ETHER_FIRST_MULTI(step, ac, enm);
7964d54f62fSpatrick 		while (enm != NULL) {
7974d54f62fSpatrick 			h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);
7984d54f62fSpatrick 
7994d54f62fSpatrick 			ghash |= 1LLU << (((uint8_t *)&h)[3] >> 2);
8004d54f62fSpatrick 
8014d54f62fSpatrick 			ETHER_NEXT_MULTI(step, enm);
8024d54f62fSpatrick 		}
8034d54f62fSpatrick 	}
8044d54f62fSpatrick 
8054d54f62fSpatrick 	HWRITE4(sc, ENET_GAUR, (uint32_t)(ghash >> 32));
8064d54f62fSpatrick 	HWRITE4(sc, ENET_GALR, (uint32_t)ghash);
8074d54f62fSpatrick 
8084d54f62fSpatrick 	HWRITE4(sc, ENET_IAUR, (uint32_t)(ihash >> 32));
8094d54f62fSpatrick 	HWRITE4(sc, ENET_IALR, (uint32_t)ihash);
8104d54f62fSpatrick }
8114d54f62fSpatrick 
8124d54f62fSpatrick int
fec_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)8134d54f62fSpatrick fec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
8144d54f62fSpatrick {
8154d54f62fSpatrick 	struct fec_softc *sc = ifp->if_softc;
8164d54f62fSpatrick 	struct ifreq *ifr = (struct ifreq *)data;
8174d54f62fSpatrick 	int s, error = 0;
8184d54f62fSpatrick 
8194d54f62fSpatrick 	s = splnet();
8204d54f62fSpatrick 
8214d54f62fSpatrick 	switch (cmd) {
8224d54f62fSpatrick 	case SIOCSIFADDR:
8234d54f62fSpatrick 		ifp->if_flags |= IFF_UP;
8244d54f62fSpatrick 		if (!(ifp->if_flags & IFF_RUNNING))
8254d54f62fSpatrick 			fec_init(sc);
8264d54f62fSpatrick 		break;
8274d54f62fSpatrick 
8284d54f62fSpatrick 	case SIOCSIFFLAGS:
8294d54f62fSpatrick 		if (ifp->if_flags & IFF_UP) {
8304d54f62fSpatrick 			if (ifp->if_flags & IFF_RUNNING)
8314d54f62fSpatrick 				error = ENETRESET;
8324d54f62fSpatrick 			else
8334d54f62fSpatrick 				fec_init(sc);
8344d54f62fSpatrick 		} else {
8354d54f62fSpatrick 			if (ifp->if_flags & IFF_RUNNING)
8364d54f62fSpatrick 				fec_stop(sc);
8374d54f62fSpatrick 		}
8384d54f62fSpatrick 		break;
8394d54f62fSpatrick 
8404d54f62fSpatrick 	case SIOCGIFMEDIA:
8414d54f62fSpatrick 	case SIOCSIFMEDIA:
8424d54f62fSpatrick 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
8434d54f62fSpatrick 		break;
8444d54f62fSpatrick 
8454d54f62fSpatrick 	default:
8464d54f62fSpatrick 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
8474d54f62fSpatrick 	}
8484d54f62fSpatrick 
8494d54f62fSpatrick 	if (error == ENETRESET) {
8504d54f62fSpatrick 		if (ifp->if_flags & IFF_RUNNING)
8514d54f62fSpatrick 			fec_iff(sc);
8524d54f62fSpatrick 		error = 0;
8534d54f62fSpatrick 	}
8544d54f62fSpatrick 
8554d54f62fSpatrick 	splx(s);
8564d54f62fSpatrick 	return(error);
8574d54f62fSpatrick }
8584d54f62fSpatrick 
8594d54f62fSpatrick void
fec_start(struct ifnet * ifp)8604d54f62fSpatrick fec_start(struct ifnet *ifp)
8614d54f62fSpatrick {
8624d54f62fSpatrick 	struct fec_softc *sc = ifp->if_softc;
86301946c72Spatrick 	struct mbuf *m = NULL;
86401946c72Spatrick 	int error, idx;
8654d54f62fSpatrick 
86601946c72Spatrick 	if (!(ifp->if_flags & IFF_RUNNING))
86701946c72Spatrick 		return;
86801946c72Spatrick 	if (ifq_is_oactive(&ifp->if_snd))
86901946c72Spatrick 		return;
8700cae21bdSpatrick 	if (ifq_empty(&ifp->if_snd))
8714d54f62fSpatrick 		return;
8724d54f62fSpatrick 
87301946c72Spatrick 	idx = sc->sc_tx_prod;
87401946c72Spatrick 	while ((sc->sc_txdesc[idx].fd_status & ENET_TXD_READY) == 0) {
87501946c72Spatrick 		m = ifq_deq_begin(&ifp->if_snd);
87601946c72Spatrick 		if (m == NULL)
8774d54f62fSpatrick 			break;
8784d54f62fSpatrick 
87901946c72Spatrick 		error = fec_encap(sc, m, &idx);
88001946c72Spatrick 		if (error == ENOBUFS) {
88101946c72Spatrick 			ifq_deq_rollback(&ifp->if_snd, m);
8824d54f62fSpatrick 			ifq_set_oactive(&ifp->if_snd);
8834d54f62fSpatrick 			break;
8844d54f62fSpatrick 		}
88501946c72Spatrick 		if (error == EFBIG) {
88601946c72Spatrick 			ifq_deq_commit(&ifp->if_snd, m);
88701946c72Spatrick 			m_freem(m); /* give up: drop it */
88801946c72Spatrick 			ifp->if_oerrors++;
88901946c72Spatrick 			continue;
89001946c72Spatrick 		}
8914d54f62fSpatrick 
89201946c72Spatrick 		ifq_deq_commit(&ifp->if_snd, m);
8934d54f62fSpatrick 
8944d54f62fSpatrick #if NBPFILTER > 0
8954d54f62fSpatrick 		if (ifp->if_bpf)
89601946c72Spatrick 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
8974d54f62fSpatrick #endif
89801946c72Spatrick 	}
8994d54f62fSpatrick 
90001946c72Spatrick 	if (sc->sc_tx_prod != idx) {
90101946c72Spatrick 		sc->sc_tx_prod = idx;
90201946c72Spatrick 
90301946c72Spatrick 		/* Set a timeout in case the chip goes out to lunch. */
90401946c72Spatrick 		ifp->if_timer = 5;
9054d54f62fSpatrick 	}
9064d54f62fSpatrick }
9074d54f62fSpatrick 
9084d54f62fSpatrick int
fec_encap(struct fec_softc * sc,struct mbuf * m0,int * idx)90901946c72Spatrick fec_encap(struct fec_softc *sc, struct mbuf *m0, int *idx)
9104d54f62fSpatrick {
91101946c72Spatrick 	struct fec_desc *txd, *txd_start;
91201946c72Spatrick 	bus_dmamap_t map;
91301946c72Spatrick 	struct mbuf *m;
91401946c72Spatrick 	int cur, frag, i;
91501946c72Spatrick 	int ret;
91601946c72Spatrick 
91701946c72Spatrick 	m = m0;
91801946c72Spatrick 	cur = frag = *idx;
91901946c72Spatrick 	map = sc->sc_txbuf[cur].fb_map;
92001946c72Spatrick 
92101946c72Spatrick 	if (sc->sc_tx_bounce) {
92201946c72Spatrick 		m = m_dup_pkt(m0, 0, M_DONTWAIT);
92301946c72Spatrick 		if (m == NULL) {
92401946c72Spatrick 			ret = ENOBUFS;
92501946c72Spatrick 			goto fail;
92601946c72Spatrick 		}
9274d54f62fSpatrick 	}
9284d54f62fSpatrick 
92901946c72Spatrick 	if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
93001946c72Spatrick 		if (m_defrag(m, M_DONTWAIT)) {
93101946c72Spatrick 			ret = EFBIG;
93201946c72Spatrick 			goto fail;
93301946c72Spatrick 		}
93401946c72Spatrick 		if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
93501946c72Spatrick 			ret = EFBIG;
93601946c72Spatrick 			goto fail;
93701946c72Spatrick 		}
9384d54f62fSpatrick 	}
9394d54f62fSpatrick 
94001946c72Spatrick 	if (map->dm_nsegs > (ENET_NTXDESC - sc->sc_tx_cnt - 2)) {
94101946c72Spatrick 		bus_dmamap_unload(sc->sc_dmat, map);
94201946c72Spatrick 		ret = ENOBUFS;
94301946c72Spatrick 		goto fail;
94401946c72Spatrick 	}
9454d54f62fSpatrick 
94601946c72Spatrick 	/* Sync the DMA map. */
94701946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
94801946c72Spatrick 	    BUS_DMASYNC_PREWRITE);
9494d54f62fSpatrick 
95001946c72Spatrick 	txd = txd_start = &sc->sc_txdesc[frag];
95101946c72Spatrick 	for (i = 0; i < map->dm_nsegs; i++) {
95201946c72Spatrick 		txd->fd_addr = map->dm_segs[i].ds_addr;
95301946c72Spatrick 		txd->fd_len = map->dm_segs[i].ds_len;
95401946c72Spatrick 		txd->fd_status &= ENET_TXD_WRAP;
95501946c72Spatrick 		if (i == (map->dm_nsegs - 1))
95601946c72Spatrick 			txd->fd_status |= ENET_TXD_LAST | ENET_TXD_TC;
95701946c72Spatrick 		if (i != 0)
95801946c72Spatrick 			txd->fd_status |= ENET_TXD_READY;
9594d54f62fSpatrick 
96001946c72Spatrick 		bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring),
96101946c72Spatrick 		    frag * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
9624d54f62fSpatrick 
96301946c72Spatrick 		cur = frag;
96401946c72Spatrick 		if (frag == (ENET_NTXDESC - 1)) {
96501946c72Spatrick 			txd = &sc->sc_txdesc[0];
96601946c72Spatrick 			frag = 0;
96701946c72Spatrick 		} else {
96801946c72Spatrick 			txd++;
96901946c72Spatrick 			frag++;
97001946c72Spatrick 		}
97101946c72Spatrick 		KASSERT(frag != sc->sc_tx_cons);
97201946c72Spatrick 	}
9734d54f62fSpatrick 
97401946c72Spatrick 	txd_start->fd_status |= ENET_TXD_READY;
97501946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring),
97601946c72Spatrick 	    *idx * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
9774d54f62fSpatrick 
9784d54f62fSpatrick 	HWRITE4(sc, ENET_TDAR, ENET_TDAR_TDAR);
9794d54f62fSpatrick 
98001946c72Spatrick 	KASSERT(sc->sc_txbuf[cur].fb_m == NULL);
98101946c72Spatrick 	KASSERT(sc->sc_txbuf[cur].fb_m0 == NULL);
98201946c72Spatrick 	sc->sc_txbuf[*idx].fb_map = sc->sc_txbuf[cur].fb_map;
98301946c72Spatrick 	sc->sc_txbuf[cur].fb_map = map;
98401946c72Spatrick 	sc->sc_txbuf[cur].fb_m = m;
98501946c72Spatrick 	if (m != m0)
98601946c72Spatrick 		sc->sc_txbuf[cur].fb_m0 = m0;
9874d54f62fSpatrick 
98801946c72Spatrick 	sc->sc_tx_cnt += map->dm_nsegs;
98901946c72Spatrick 	*idx = frag;
9904d54f62fSpatrick 
99101946c72Spatrick 	return (0);
9924d54f62fSpatrick 
99301946c72Spatrick fail:
99401946c72Spatrick 	if (m != m0)
9954d54f62fSpatrick 		m_freem(m);
99601946c72Spatrick 	return (ret);
9974d54f62fSpatrick }
9984d54f62fSpatrick 
9994d54f62fSpatrick /*
10004d54f62fSpatrick  * Established by attachment driver at interrupt priority IPL_NET.
10014d54f62fSpatrick  */
10024d54f62fSpatrick int
fec_intr(void * arg)10034d54f62fSpatrick fec_intr(void *arg)
10044d54f62fSpatrick {
10054d54f62fSpatrick 	struct fec_softc *sc = arg;
10064d54f62fSpatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
10074d54f62fSpatrick 	u_int32_t status;
10084d54f62fSpatrick 
10094d54f62fSpatrick 	/* Find out which interrupts are pending. */
10104d54f62fSpatrick 	status = HREAD4(sc, ENET_EIR);
10114d54f62fSpatrick 
10124d54f62fSpatrick 	/* Acknowledge the interrupts we are about to handle. */
1013019e33a8Spatrick 	status &= (ENET_EIR_RXF | ENET_EIR_TXF);
10144d54f62fSpatrick 	HWRITE4(sc, ENET_EIR, status);
10154d54f62fSpatrick 
10164d54f62fSpatrick 	/*
10174d54f62fSpatrick 	 * Handle incoming packets.
10184d54f62fSpatrick 	 */
101901946c72Spatrick 	if (ISSET(status, ENET_EIR_RXF))
102001946c72Spatrick 		fec_rx_proc(sc);
102101946c72Spatrick 
102201946c72Spatrick 	/*
102301946c72Spatrick 	 * Handle transmitted packets.
102401946c72Spatrick 	 */
102501946c72Spatrick 	if (ISSET(status, ENET_EIR_TXF))
102601946c72Spatrick 		fec_tx_proc(sc);
10274d54f62fSpatrick 
10284d54f62fSpatrick 	/* Try to transmit. */
10290cae21bdSpatrick 	if (ifp->if_flags & IFF_RUNNING && !ifq_empty(&ifp->if_snd))
10304d54f62fSpatrick 		fec_start(ifp);
10314d54f62fSpatrick 
10324d54f62fSpatrick 	return 1;
10334d54f62fSpatrick }
10344d54f62fSpatrick 
10354d54f62fSpatrick void
fec_tx_proc(struct fec_softc * sc)103601946c72Spatrick fec_tx_proc(struct fec_softc *sc)
10374d54f62fSpatrick {
10384d54f62fSpatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
103901946c72Spatrick 	struct fec_desc *txd;
104001946c72Spatrick 	struct fec_buf *txb;
104101946c72Spatrick 	int idx;
10424d54f62fSpatrick 
104301946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_txring), 0,
104401946c72Spatrick 	    ENET_DMA_LEN(sc->sc_txring),
104501946c72Spatrick 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
10464d54f62fSpatrick 
104701946c72Spatrick 	while (sc->sc_tx_cnt > 0) {
104801946c72Spatrick 		idx = sc->sc_tx_cons;
104901946c72Spatrick 		KASSERT(idx < ENET_NTXDESC);
10504d54f62fSpatrick 
105101946c72Spatrick 		txd = &sc->sc_txdesc[idx];
105201946c72Spatrick 		if (txd->fd_status & ENET_TXD_READY)
105301946c72Spatrick 			break;
10544d54f62fSpatrick 
105501946c72Spatrick 		txb = &sc->sc_txbuf[idx];
105601946c72Spatrick 		if (txb->fb_m) {
105701946c72Spatrick 			bus_dmamap_sync(sc->sc_dmat, txb->fb_map, 0,
105801946c72Spatrick 			    txb->fb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
105901946c72Spatrick 			bus_dmamap_unload(sc->sc_dmat, txb->fb_map);
106001946c72Spatrick 
106101946c72Spatrick 			m_freem(txb->fb_m);
106201946c72Spatrick 			m_freem(txb->fb_m0);
106301946c72Spatrick 			txb->fb_m = txb->fb_m0 = NULL;
10644d54f62fSpatrick 		}
10654d54f62fSpatrick 
106601946c72Spatrick 		ifq_clr_oactive(&ifp->if_snd);
10674d54f62fSpatrick 
106801946c72Spatrick 		sc->sc_tx_cnt--;
10694d54f62fSpatrick 
107001946c72Spatrick 		if (sc->sc_tx_cons == (ENET_NTXDESC - 1))
107101946c72Spatrick 			sc->sc_tx_cons = 0;
10724d54f62fSpatrick 		else
107301946c72Spatrick 			sc->sc_tx_cons++;
107401946c72Spatrick 
107501946c72Spatrick 		txd->fd_status &= ENET_TXD_WRAP;
107601946c72Spatrick 	}
107701946c72Spatrick 
107801946c72Spatrick 	if (sc->sc_tx_cnt == 0)
107901946c72Spatrick 		ifp->if_timer = 0;
108001946c72Spatrick 	else /* ERR006358 */
108101946c72Spatrick 		HWRITE4(sc, ENET_TDAR, ENET_TDAR_TDAR);
108201946c72Spatrick }
108301946c72Spatrick 
108401946c72Spatrick void
fec_rx_proc(struct fec_softc * sc)108501946c72Spatrick fec_rx_proc(struct fec_softc *sc)
108601946c72Spatrick {
108701946c72Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
108801946c72Spatrick 	struct fec_desc *rxd;
108901946c72Spatrick 	struct fec_buf *rxb;
109001946c72Spatrick 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
109101946c72Spatrick 	struct mbuf *m;
109201946c72Spatrick 	int idx, len;
109301946c72Spatrick 
109401946c72Spatrick 	if ((ifp->if_flags & IFF_RUNNING) == 0)
109501946c72Spatrick 		return;
109601946c72Spatrick 
109701946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_rxring), 0,
109801946c72Spatrick 	    ENET_DMA_LEN(sc->sc_rxring),
109901946c72Spatrick 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
110001946c72Spatrick 
110101946c72Spatrick 	while (if_rxr_inuse(&sc->sc_rx_ring) > 0) {
110201946c72Spatrick 		idx = sc->sc_rx_cons;
110301946c72Spatrick 		KASSERT(idx < ENET_NRXDESC);
110401946c72Spatrick 
110501946c72Spatrick 		rxd = &sc->sc_rxdesc[idx];
110601946c72Spatrick 		if (rxd->fd_status & ENET_RXD_EMPTY)
110701946c72Spatrick 			break;
110801946c72Spatrick 
110901946c72Spatrick 		len = rxd->fd_len;
111001946c72Spatrick 		rxb = &sc->sc_rxbuf[idx];
111101946c72Spatrick 		KASSERT(rxb->fb_m);
111201946c72Spatrick 
111301946c72Spatrick 		bus_dmamap_sync(sc->sc_dmat, rxb->fb_map, 0,
111401946c72Spatrick 		    len, BUS_DMASYNC_POSTREAD);
111501946c72Spatrick 		bus_dmamap_unload(sc->sc_dmat, rxb->fb_map);
111601946c72Spatrick 
111701946c72Spatrick 		/* Strip off CRC. */
111801946c72Spatrick 		len -= ETHER_CRC_LEN;
111901946c72Spatrick 		KASSERT(len > 0);
112001946c72Spatrick 
112101946c72Spatrick 		m = rxb->fb_m;
112201946c72Spatrick 		rxb->fb_m = NULL;
112301946c72Spatrick 
112401946c72Spatrick 		m_adj(m, ETHER_ALIGN);
112501946c72Spatrick 		m->m_pkthdr.len = m->m_len = len;
11264d54f62fSpatrick 
11274d54f62fSpatrick 		ml_enqueue(&ml, m);
112801946c72Spatrick 
112901946c72Spatrick 		if_rxr_put(&sc->sc_rx_ring, 1);
113001946c72Spatrick 		if (sc->sc_rx_cons == (ENET_NRXDESC - 1))
113101946c72Spatrick 			sc->sc_rx_cons = 0;
113201946c72Spatrick 		else
113301946c72Spatrick 			sc->sc_rx_cons++;
11344d54f62fSpatrick 	}
11354d54f62fSpatrick 
113682ea7955Sdlg 	if (ifiq_input(&ifp->if_rcv, &ml))
113782ea7955Sdlg 		if_rxr_livelocked(&sc->sc_rx_ring);
113882ea7955Sdlg 
113901946c72Spatrick 	fec_fill_rx_ring(sc);
114001946c72Spatrick 
114101946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, ENET_DMA_MAP(sc->sc_rxring), 0,
114201946c72Spatrick 	    ENET_DMA_LEN(sc->sc_rxring),
114301946c72Spatrick 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
114401946c72Spatrick 
11454d54f62fSpatrick 	/* rx descriptors are ready */
11464d54f62fSpatrick 	HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR);
11474d54f62fSpatrick }
11484d54f62fSpatrick 
11494d54f62fSpatrick void
fec_tick(void * arg)11504d54f62fSpatrick fec_tick(void *arg)
11514d54f62fSpatrick {
11524d54f62fSpatrick 	struct fec_softc *sc = arg;
11534d54f62fSpatrick 	int s;
11544d54f62fSpatrick 
11554d54f62fSpatrick 	s = splnet();
11564d54f62fSpatrick 	mii_tick(&sc->sc_mii);
11574d54f62fSpatrick 	splx(s);
11584d54f62fSpatrick 
11594d54f62fSpatrick 	timeout_add_sec(&sc->sc_tick, 1);
11604d54f62fSpatrick }
11614d54f62fSpatrick 
11624d54f62fSpatrick /*
11634d54f62fSpatrick  * MII
11644d54f62fSpatrick  * Interrupts need ENET_ECR_ETHEREN to be set,
11654d54f62fSpatrick  * so we just read the interrupt status registers.
11664d54f62fSpatrick  */
11674d54f62fSpatrick int
fec_miibus_readreg(struct device * dev,int phy,int reg)11684d54f62fSpatrick fec_miibus_readreg(struct device *dev, int phy, int reg)
11694d54f62fSpatrick {
11704d54f62fSpatrick 	int r = 0;
11714d54f62fSpatrick 	struct fec_softc *sc = (struct fec_softc *)dev;
11724d54f62fSpatrick 
1173019e33a8Spatrick 	HWRITE4(sc, ENET_EIR, ENET_EIR_MII);
11744d54f62fSpatrick 
11754d54f62fSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR,
11764d54f62fSpatrick 	    ENET_MMFR_ST | ENET_MMFR_OP_RD | ENET_MMFR_TA |
11774d54f62fSpatrick 	    phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT);
11784d54f62fSpatrick 
11794d54f62fSpatrick 	while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII));
11804d54f62fSpatrick 
11814d54f62fSpatrick 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR);
11824d54f62fSpatrick 
11834d54f62fSpatrick 	return (r & 0xffff);
11844d54f62fSpatrick }
11854d54f62fSpatrick 
11864d54f62fSpatrick void
fec_miibus_writereg(struct device * dev,int phy,int reg,int val)11874d54f62fSpatrick fec_miibus_writereg(struct device *dev, int phy, int reg, int val)
11884d54f62fSpatrick {
11894d54f62fSpatrick 	struct fec_softc *sc = (struct fec_softc *)dev;
11904d54f62fSpatrick 
1191019e33a8Spatrick 	HWRITE4(sc, ENET_EIR, ENET_EIR_MII);
11924d54f62fSpatrick 
11934d54f62fSpatrick 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR,
11944d54f62fSpatrick 	    ENET_MMFR_ST | ENET_MMFR_OP_WR | ENET_MMFR_TA |
11954d54f62fSpatrick 	    phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT |
11964d54f62fSpatrick 	    (val & 0xffff));
11974d54f62fSpatrick 
11984d54f62fSpatrick 	while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII));
11994d54f62fSpatrick 
12004d54f62fSpatrick 	return;
12014d54f62fSpatrick }
12024d54f62fSpatrick 
12034d54f62fSpatrick void
fec_miibus_statchg(struct device * dev)12044d54f62fSpatrick fec_miibus_statchg(struct device *dev)
12054d54f62fSpatrick {
12064d54f62fSpatrick 	struct fec_softc *sc = (struct fec_softc *)dev;
12074d54f62fSpatrick 	uint32_t ecr, rcr;
12084d54f62fSpatrick 
12094d54f62fSpatrick 	ecr = HREAD4(sc, ENET_ECR) & ~ENET_ECR_SPEED;
12104d54f62fSpatrick 	rcr = HREAD4(sc, ENET_RCR) & ~ENET_RCR_RMII_10T;
12114d54f62fSpatrick 	switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) {
12124d54f62fSpatrick 	case IFM_1000_T:  /* Gigabit */
12134d54f62fSpatrick 		ecr |= ENET_ECR_SPEED;
12144d54f62fSpatrick 		break;
12154d54f62fSpatrick 	case IFM_100_TX:
12164d54f62fSpatrick 		break;
12174d54f62fSpatrick 	case IFM_10_T:
12184d54f62fSpatrick 		rcr |= ENET_RCR_RMII_10T;
12194d54f62fSpatrick 		break;
12204d54f62fSpatrick 	}
12214d54f62fSpatrick 	HWRITE4(sc, ENET_ECR, ecr);
12224d54f62fSpatrick 	HWRITE4(sc, ENET_RCR, rcr);
12234d54f62fSpatrick 
12244d54f62fSpatrick 	return;
12254d54f62fSpatrick }
12264d54f62fSpatrick 
12274d54f62fSpatrick int
fec_ifmedia_upd(struct ifnet * ifp)12284d54f62fSpatrick fec_ifmedia_upd(struct ifnet *ifp)
12294d54f62fSpatrick {
12304d54f62fSpatrick 	struct fec_softc *sc = ifp->if_softc;
12314d54f62fSpatrick 	struct mii_data *mii = &sc->sc_mii;
12324d54f62fSpatrick 	int err;
12334d54f62fSpatrick 	if (mii->mii_instance) {
12344d54f62fSpatrick 		struct mii_softc *miisc;
12354d54f62fSpatrick 
12364d54f62fSpatrick 		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
12374d54f62fSpatrick 			mii_phy_reset(miisc);
12384d54f62fSpatrick 	}
12394d54f62fSpatrick 	err = mii_mediachg(mii);
12404d54f62fSpatrick 	return (err);
12414d54f62fSpatrick }
12424d54f62fSpatrick 
12434d54f62fSpatrick void
fec_ifmedia_sts(struct ifnet * ifp,struct ifmediareq * ifmr)12444d54f62fSpatrick fec_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
12454d54f62fSpatrick {
12464d54f62fSpatrick 	struct fec_softc *sc = ifp->if_softc;
12474d54f62fSpatrick 	struct mii_data *mii = &sc->sc_mii;
12484d54f62fSpatrick 
12494d54f62fSpatrick 	mii_pollstat(mii);
12504d54f62fSpatrick 
12514d54f62fSpatrick 	ifmr->ifm_active = mii->mii_media_active;
12524d54f62fSpatrick 	ifmr->ifm_status = mii->mii_media_status;
12534d54f62fSpatrick }
12544d54f62fSpatrick 
12554d54f62fSpatrick /*
12564d54f62fSpatrick  * Manage DMA'able memory.
12574d54f62fSpatrick  */
125801946c72Spatrick struct fec_dmamem *
fec_dmamem_alloc(struct fec_softc * sc,bus_size_t size,bus_size_t align)125901946c72Spatrick fec_dmamem_alloc(struct fec_softc *sc, bus_size_t size, bus_size_t align)
12604d54f62fSpatrick {
126101946c72Spatrick 	struct fec_dmamem *fdm;
126201946c72Spatrick 	int nsegs;
12634d54f62fSpatrick 
126401946c72Spatrick 	fdm = malloc(sizeof(*fdm), M_DEVBUF, M_WAITOK | M_ZERO);
126501946c72Spatrick 	fdm->fdm_size = size;
12664d54f62fSpatrick 
126701946c72Spatrick 	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
126801946c72Spatrick 	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &fdm->fdm_map) != 0)
126901946c72Spatrick 		goto fdmfree;
12704d54f62fSpatrick 
127101946c72Spatrick 	if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &fdm->fdm_seg, 1,
127201946c72Spatrick 	    &nsegs, BUS_DMA_WAITOK) != 0)
127301946c72Spatrick 		goto destroy;
12744d54f62fSpatrick 
127501946c72Spatrick 	if (bus_dmamem_map(sc->sc_dmat, &fdm->fdm_seg, nsegs, size,
127601946c72Spatrick 	    &fdm->fdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
127701946c72Spatrick 		goto free;
12784d54f62fSpatrick 
127901946c72Spatrick 	if (bus_dmamap_load(sc->sc_dmat, fdm->fdm_map, fdm->fdm_kva, size,
128001946c72Spatrick 	    NULL, BUS_DMA_WAITOK) != 0)
128101946c72Spatrick 		goto unmap;
12824d54f62fSpatrick 
128301946c72Spatrick 	return (fdm);
12844d54f62fSpatrick 
128501946c72Spatrick unmap:
128601946c72Spatrick 	bus_dmamem_unmap(sc->sc_dmat, fdm->fdm_kva, size);
128701946c72Spatrick free:
128801946c72Spatrick 	bus_dmamem_free(sc->sc_dmat, &fdm->fdm_seg, 1);
128901946c72Spatrick destroy:
129001946c72Spatrick 	bus_dmamap_destroy(sc->sc_dmat, fdm->fdm_map);
129101946c72Spatrick fdmfree:
129201946c72Spatrick 	free(fdm, M_DEVBUF, sizeof(*fdm));
129301946c72Spatrick 
129401946c72Spatrick 	return (NULL);
12954d54f62fSpatrick }
12964d54f62fSpatrick 
12974d54f62fSpatrick void
fec_dmamem_free(struct fec_softc * sc,struct fec_dmamem * fdm)129801946c72Spatrick fec_dmamem_free(struct fec_softc *sc, struct fec_dmamem *fdm)
12994d54f62fSpatrick {
130001946c72Spatrick 	bus_dmamem_unmap(sc->sc_dmat, fdm->fdm_kva, fdm->fdm_size);
130101946c72Spatrick 	bus_dmamem_free(sc->sc_dmat, &fdm->fdm_seg, 1);
130201946c72Spatrick 	bus_dmamap_destroy(sc->sc_dmat, fdm->fdm_map);
130301946c72Spatrick 	free(fdm, M_DEVBUF, sizeof(*fdm));
13044d54f62fSpatrick }
130501946c72Spatrick 
130601946c72Spatrick struct mbuf *
fec_alloc_mbuf(struct fec_softc * sc,bus_dmamap_t map)130701946c72Spatrick fec_alloc_mbuf(struct fec_softc *sc, bus_dmamap_t map)
130801946c72Spatrick {
130901946c72Spatrick 	struct mbuf *m = NULL;
131001946c72Spatrick 
1311471f2571Sjan 	m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
131201946c72Spatrick 	if (!m)
131301946c72Spatrick 		return (NULL);
131401946c72Spatrick 	m->m_len = m->m_pkthdr.len = MCLBYTES;
131501946c72Spatrick 
131601946c72Spatrick 	if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT) != 0) {
131701946c72Spatrick 		printf("%s: could not load mbuf DMA map",
131801946c72Spatrick 		    sc->sc_dev.dv_xname);
131901946c72Spatrick 		m_freem(m);
132001946c72Spatrick 		return (NULL);
132101946c72Spatrick 	}
132201946c72Spatrick 
132301946c72Spatrick 	bus_dmamap_sync(sc->sc_dmat, map, 0,
132401946c72Spatrick 	    m->m_pkthdr.len, BUS_DMASYNC_PREREAD);
132501946c72Spatrick 
132601946c72Spatrick 	return (m);
132701946c72Spatrick }
132801946c72Spatrick 
132901946c72Spatrick void
fec_fill_rx_ring(struct fec_softc * sc)133001946c72Spatrick fec_fill_rx_ring(struct fec_softc *sc)
133101946c72Spatrick {
133201946c72Spatrick 	struct fec_desc *rxd;
133301946c72Spatrick 	struct fec_buf *rxb;
133401946c72Spatrick 	u_int slots;
133501946c72Spatrick 
133601946c72Spatrick 	for (slots = if_rxr_get(&sc->sc_rx_ring, ENET_NRXDESC);
133701946c72Spatrick 	    slots > 0; slots--) {
133801946c72Spatrick 		rxb = &sc->sc_rxbuf[sc->sc_rx_prod];
133901946c72Spatrick 		rxb->fb_m = fec_alloc_mbuf(sc, rxb->fb_map);
134001946c72Spatrick 		if (rxb->fb_m == NULL)
134101946c72Spatrick 			break;
134201946c72Spatrick 		rxd = &sc->sc_rxdesc[sc->sc_rx_prod];
134301946c72Spatrick 		rxd->fd_len = rxb->fb_map->dm_segs[0].ds_len - 1;
134401946c72Spatrick 		rxd->fd_addr = rxb->fb_map->dm_segs[0].ds_addr;
134501946c72Spatrick 		rxd->fd_status &= ENET_RXD_WRAP;
134601946c72Spatrick 		rxd->fd_status |= ENET_RXD_EMPTY;
134701946c72Spatrick 
134801946c72Spatrick 		if (sc->sc_rx_prod == (ENET_NRXDESC - 1))
134901946c72Spatrick 			sc->sc_rx_prod = 0;
135001946c72Spatrick 		else
135101946c72Spatrick 			sc->sc_rx_prod++;
135201946c72Spatrick 	}
135301946c72Spatrick 	if_rxr_put(&sc->sc_rx_ring, slots);
13544d54f62fSpatrick }
1355