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