185ae89f4SRuslan Bukin /*-
285ae89f4SRuslan Bukin * SPDX-License-Identifier: BSD-2-Clause
385ae89f4SRuslan Bukin *
4*3969b51fSRuslan Bukin * Copyright (c) 2019-2025 Ruslan Bukin <br@bsdpad.com>
585ae89f4SRuslan Bukin *
685ae89f4SRuslan Bukin * This software was developed by SRI International and the University of
785ae89f4SRuslan Bukin * Cambridge Computer Laboratory (Department of Computer Science and
885ae89f4SRuslan Bukin * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
985ae89f4SRuslan Bukin * DARPA SSITH research programme.
1085ae89f4SRuslan Bukin *
1185ae89f4SRuslan Bukin * Redistribution and use in source and binary forms, with or without
1285ae89f4SRuslan Bukin * modification, are permitted provided that the following conditions
1385ae89f4SRuslan Bukin * are met:
1485ae89f4SRuslan Bukin * 1. Redistributions of source code must retain the above copyright
1585ae89f4SRuslan Bukin * notice, this list of conditions and the following disclaimer.
1685ae89f4SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright
1785ae89f4SRuslan Bukin * notice, this list of conditions and the following disclaimer in the
1885ae89f4SRuslan Bukin * documentation and/or other materials provided with the distribution.
1985ae89f4SRuslan Bukin *
2085ae89f4SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2185ae89f4SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2285ae89f4SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2385ae89f4SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2485ae89f4SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2585ae89f4SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2685ae89f4SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2785ae89f4SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2885ae89f4SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2985ae89f4SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3085ae89f4SRuslan Bukin * SUCH DAMAGE.
3185ae89f4SRuslan Bukin */
3285ae89f4SRuslan Bukin
3385ae89f4SRuslan Bukin #include <sys/param.h>
3485ae89f4SRuslan Bukin #include <sys/systm.h>
3585ae89f4SRuslan Bukin #include <sys/bus.h>
3685ae89f4SRuslan Bukin #include <sys/kernel.h>
3785ae89f4SRuslan Bukin #include <sys/lock.h>
3885ae89f4SRuslan Bukin #include <sys/malloc.h>
3985ae89f4SRuslan Bukin #include <sys/mbuf.h>
4085ae89f4SRuslan Bukin #include <sys/module.h>
4185ae89f4SRuslan Bukin #include <sys/mutex.h>
4285ae89f4SRuslan Bukin #include <sys/rman.h>
4385ae89f4SRuslan Bukin #include <sys/socket.h>
4485ae89f4SRuslan Bukin #include <sys/sockio.h>
4585ae89f4SRuslan Bukin
46*3969b51fSRuslan Bukin #include <vm/vm.h>
47*3969b51fSRuslan Bukin #include <vm/vm_page.h>
48*3969b51fSRuslan Bukin
4985ae89f4SRuslan Bukin #include <net/bpf.h>
5085ae89f4SRuslan Bukin #include <net/if.h>
5185ae89f4SRuslan Bukin #include <net/ethernet.h>
5285ae89f4SRuslan Bukin #include <net/if_dl.h>
5385ae89f4SRuslan Bukin #include <net/if_media.h>
5485ae89f4SRuslan Bukin #include <net/if_types.h>
5585ae89f4SRuslan Bukin #include <net/if_var.h>
5685ae89f4SRuslan Bukin
5785ae89f4SRuslan Bukin #include <machine/bus.h>
5885ae89f4SRuslan Bukin
5985ae89f4SRuslan Bukin #include <dev/mii/mii.h>
6085ae89f4SRuslan Bukin #include <dev/mii/miivar.h>
6185ae89f4SRuslan Bukin #include <dev/mii/tiphy.h>
6285ae89f4SRuslan Bukin #include <dev/ofw/ofw_bus.h>
6385ae89f4SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
6485ae89f4SRuslan Bukin #include <dev/xilinx/if_xaereg.h>
6585ae89f4SRuslan Bukin #include <dev/xilinx/if_xaevar.h>
6685ae89f4SRuslan Bukin
67a8692c16SRuslan Bukin #include <dev/xilinx/axidma.h>
68a8692c16SRuslan Bukin
6985ae89f4SRuslan Bukin #include "miibus_if.h"
7085ae89f4SRuslan Bukin
7185ae89f4SRuslan Bukin #define READ4(_sc, _reg) \
7285ae89f4SRuslan Bukin bus_read_4((_sc)->res[0], _reg)
7385ae89f4SRuslan Bukin #define WRITE4(_sc, _reg, _val) \
7485ae89f4SRuslan Bukin bus_write_4((_sc)->res[0], _reg, _val)
7585ae89f4SRuslan Bukin
7685ae89f4SRuslan Bukin #define READ8(_sc, _reg) \
7785ae89f4SRuslan Bukin bus_read_8((_sc)->res[0], _reg)
7885ae89f4SRuslan Bukin #define WRITE8(_sc, _reg, _val) \
7985ae89f4SRuslan Bukin bus_write_8((_sc)->res[0], _reg, _val)
8085ae89f4SRuslan Bukin
8185ae89f4SRuslan Bukin #define XAE_LOCK(sc) mtx_lock(&(sc)->mtx)
8285ae89f4SRuslan Bukin #define XAE_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
8385ae89f4SRuslan Bukin #define XAE_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED)
8485ae89f4SRuslan Bukin #define XAE_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED)
8585ae89f4SRuslan Bukin
8685ae89f4SRuslan Bukin #define XAE_DEBUG
8785ae89f4SRuslan Bukin #undef XAE_DEBUG
8885ae89f4SRuslan Bukin
8985ae89f4SRuslan Bukin #ifdef XAE_DEBUG
9085ae89f4SRuslan Bukin #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
9185ae89f4SRuslan Bukin #else
9285ae89f4SRuslan Bukin #define dprintf(fmt, ...)
9385ae89f4SRuslan Bukin #endif
9485ae89f4SRuslan Bukin
9585ae89f4SRuslan Bukin #define RX_QUEUE_SIZE 64
9685ae89f4SRuslan Bukin #define TX_QUEUE_SIZE 64
9785ae89f4SRuslan Bukin #define NUM_RX_MBUF 16
9885ae89f4SRuslan Bukin #define BUFRING_SIZE 8192
9985ae89f4SRuslan Bukin #define MDIO_CLK_DIV_DEFAULT 29
100*3969b51fSRuslan Bukin #define BUF_NPAGES 512
10185ae89f4SRuslan Bukin
10285ae89f4SRuslan Bukin #define PHY1_RD(sc, _r) \
10385ae89f4SRuslan Bukin xae_miibus_read_reg(sc->dev, 1, _r)
10485ae89f4SRuslan Bukin #define PHY1_WR(sc, _r, _v) \
10585ae89f4SRuslan Bukin xae_miibus_write_reg(sc->dev, 1, _r, _v)
10685ae89f4SRuslan Bukin
10785ae89f4SRuslan Bukin #define PHY_RD(sc, _r) \
10885ae89f4SRuslan Bukin xae_miibus_read_reg(sc->dev, sc->phy_addr, _r)
10985ae89f4SRuslan Bukin #define PHY_WR(sc, _r, _v) \
11085ae89f4SRuslan Bukin xae_miibus_write_reg(sc->dev, sc->phy_addr, _r, _v)
11185ae89f4SRuslan Bukin
11285ae89f4SRuslan Bukin /* Use this macro to access regs > 0x1f */
11385ae89f4SRuslan Bukin #define WRITE_TI_EREG(sc, reg, data) { \
11485ae89f4SRuslan Bukin PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK); \
11585ae89f4SRuslan Bukin PHY_WR(sc, MII_MMDAADR, reg); \
11685ae89f4SRuslan Bukin PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK | MMDACR_FN_DATANPI); \
11785ae89f4SRuslan Bukin PHY_WR(sc, MII_MMDAADR, data); \
11885ae89f4SRuslan Bukin }
11985ae89f4SRuslan Bukin
12085ae89f4SRuslan Bukin /* Not documented, Xilinx VCU118 workaround */
12185ae89f4SRuslan Bukin #define CFG4_SGMII_TMR 0x160 /* bits 8:7 MUST be '10' */
12285ae89f4SRuslan Bukin #define DP83867_SGMIICTL1 0xD3 /* not documented register */
12385ae89f4SRuslan Bukin #define SGMIICTL1_SGMII_6W (1 << 14) /* no idea what it is */
12485ae89f4SRuslan Bukin
12585ae89f4SRuslan Bukin static struct resource_spec xae_spec[] = {
12685ae89f4SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE },
12785ae89f4SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE },
12885ae89f4SRuslan Bukin { -1, 0 }
12985ae89f4SRuslan Bukin };
13085ae89f4SRuslan Bukin
13185ae89f4SRuslan Bukin static void xae_stop_locked(struct xae_softc *sc);
13285ae89f4SRuslan Bukin static void xae_setup_rxfilter(struct xae_softc *sc);
13385ae89f4SRuslan Bukin
13485ae89f4SRuslan Bukin static int
xae_rx_enqueue(struct xae_softc * sc,uint32_t n)13585ae89f4SRuslan Bukin xae_rx_enqueue(struct xae_softc *sc, uint32_t n)
13685ae89f4SRuslan Bukin {
13785ae89f4SRuslan Bukin struct mbuf *m;
13885ae89f4SRuslan Bukin int i;
13985ae89f4SRuslan Bukin
14085ae89f4SRuslan Bukin for (i = 0; i < n; i++) {
14185ae89f4SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
14285ae89f4SRuslan Bukin if (m == NULL) {
14385ae89f4SRuslan Bukin device_printf(sc->dev,
14485ae89f4SRuslan Bukin "%s: Can't alloc rx mbuf\n", __func__);
14585ae89f4SRuslan Bukin return (-1);
14685ae89f4SRuslan Bukin }
14785ae89f4SRuslan Bukin
14885ae89f4SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
14985ae89f4SRuslan Bukin xdma_enqueue_mbuf(sc->xchan_rx, &m, 0, 4, 4, XDMA_DEV_TO_MEM);
15085ae89f4SRuslan Bukin }
15185ae89f4SRuslan Bukin
15285ae89f4SRuslan Bukin return (0);
15385ae89f4SRuslan Bukin }
15485ae89f4SRuslan Bukin
15585ae89f4SRuslan Bukin static int
xae_get_phyaddr(phandle_t node,int * phy_addr)15685ae89f4SRuslan Bukin xae_get_phyaddr(phandle_t node, int *phy_addr)
15785ae89f4SRuslan Bukin {
15885ae89f4SRuslan Bukin phandle_t phy_node;
15985ae89f4SRuslan Bukin pcell_t phy_handle, phy_reg;
16085ae89f4SRuslan Bukin
16185ae89f4SRuslan Bukin if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
16285ae89f4SRuslan Bukin sizeof(phy_handle)) <= 0)
16385ae89f4SRuslan Bukin return (ENXIO);
16485ae89f4SRuslan Bukin
16585ae89f4SRuslan Bukin phy_node = OF_node_from_xref(phy_handle);
16685ae89f4SRuslan Bukin
16785ae89f4SRuslan Bukin if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
16885ae89f4SRuslan Bukin sizeof(phy_reg)) <= 0)
16985ae89f4SRuslan Bukin return (ENXIO);
17085ae89f4SRuslan Bukin
17185ae89f4SRuslan Bukin *phy_addr = phy_reg;
17285ae89f4SRuslan Bukin
17385ae89f4SRuslan Bukin return (0);
17485ae89f4SRuslan Bukin }
17585ae89f4SRuslan Bukin
17685ae89f4SRuslan Bukin static int
xae_xdma_tx_intr(void * arg,xdma_transfer_status_t * status)17785ae89f4SRuslan Bukin xae_xdma_tx_intr(void *arg, xdma_transfer_status_t *status)
17885ae89f4SRuslan Bukin {
17985ae89f4SRuslan Bukin xdma_transfer_status_t st;
18085ae89f4SRuslan Bukin struct xae_softc *sc;
181dba12f75SJustin Hibbits if_t ifp;
18285ae89f4SRuslan Bukin struct mbuf *m;
18385ae89f4SRuslan Bukin int err;
18485ae89f4SRuslan Bukin
18585ae89f4SRuslan Bukin sc = arg;
18685ae89f4SRuslan Bukin
18785ae89f4SRuslan Bukin XAE_LOCK(sc);
18885ae89f4SRuslan Bukin
18985ae89f4SRuslan Bukin ifp = sc->ifp;
19085ae89f4SRuslan Bukin
19185ae89f4SRuslan Bukin for (;;) {
19285ae89f4SRuslan Bukin err = xdma_dequeue_mbuf(sc->xchan_tx, &m, &st);
19385ae89f4SRuslan Bukin if (err != 0) {
19485ae89f4SRuslan Bukin break;
19585ae89f4SRuslan Bukin }
19685ae89f4SRuslan Bukin
19785ae89f4SRuslan Bukin if (st.error != 0) {
19885ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
19985ae89f4SRuslan Bukin }
20085ae89f4SRuslan Bukin
20185ae89f4SRuslan Bukin m_freem(m);
20285ae89f4SRuslan Bukin }
20385ae89f4SRuslan Bukin
204dba12f75SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
20585ae89f4SRuslan Bukin
20685ae89f4SRuslan Bukin XAE_UNLOCK(sc);
20785ae89f4SRuslan Bukin
20885ae89f4SRuslan Bukin return (0);
20985ae89f4SRuslan Bukin }
21085ae89f4SRuslan Bukin
21185ae89f4SRuslan Bukin static int
xae_xdma_rx_intr(void * arg,xdma_transfer_status_t * status)21285ae89f4SRuslan Bukin xae_xdma_rx_intr(void *arg, xdma_transfer_status_t *status)
21385ae89f4SRuslan Bukin {
21485ae89f4SRuslan Bukin xdma_transfer_status_t st;
21585ae89f4SRuslan Bukin struct xae_softc *sc;
216dba12f75SJustin Hibbits if_t ifp;
21785ae89f4SRuslan Bukin struct mbuf *m;
21885ae89f4SRuslan Bukin int err;
21985ae89f4SRuslan Bukin uint32_t cnt_processed;
22085ae89f4SRuslan Bukin
22185ae89f4SRuslan Bukin sc = arg;
22285ae89f4SRuslan Bukin
22385ae89f4SRuslan Bukin dprintf("%s\n", __func__);
22485ae89f4SRuslan Bukin
22585ae89f4SRuslan Bukin XAE_LOCK(sc);
22685ae89f4SRuslan Bukin
22785ae89f4SRuslan Bukin ifp = sc->ifp;
22885ae89f4SRuslan Bukin
22985ae89f4SRuslan Bukin cnt_processed = 0;
23085ae89f4SRuslan Bukin for (;;) {
23185ae89f4SRuslan Bukin err = xdma_dequeue_mbuf(sc->xchan_rx, &m, &st);
23285ae89f4SRuslan Bukin if (err != 0) {
23385ae89f4SRuslan Bukin break;
23485ae89f4SRuslan Bukin }
23585ae89f4SRuslan Bukin cnt_processed++;
23685ae89f4SRuslan Bukin
23785ae89f4SRuslan Bukin if (st.error != 0) {
23885ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
23985ae89f4SRuslan Bukin m_freem(m);
24085ae89f4SRuslan Bukin continue;
24185ae89f4SRuslan Bukin }
24285ae89f4SRuslan Bukin
24385ae89f4SRuslan Bukin m->m_pkthdr.len = m->m_len = st.transferred;
24485ae89f4SRuslan Bukin m->m_pkthdr.rcvif = ifp;
24585ae89f4SRuslan Bukin XAE_UNLOCK(sc);
246dba12f75SJustin Hibbits if_input(ifp, m);
24785ae89f4SRuslan Bukin XAE_LOCK(sc);
24885ae89f4SRuslan Bukin }
24985ae89f4SRuslan Bukin
25085ae89f4SRuslan Bukin xae_rx_enqueue(sc, cnt_processed);
25185ae89f4SRuslan Bukin
25285ae89f4SRuslan Bukin XAE_UNLOCK(sc);
25385ae89f4SRuslan Bukin
25485ae89f4SRuslan Bukin return (0);
25585ae89f4SRuslan Bukin }
25685ae89f4SRuslan Bukin
25785ae89f4SRuslan Bukin static void
xae_qflush(if_t ifp)258dba12f75SJustin Hibbits xae_qflush(if_t ifp)
25985ae89f4SRuslan Bukin {
26085ae89f4SRuslan Bukin }
26185ae89f4SRuslan Bukin
26285ae89f4SRuslan Bukin static int
xae_transmit_locked(if_t ifp)263dba12f75SJustin Hibbits xae_transmit_locked(if_t ifp)
26485ae89f4SRuslan Bukin {
26585ae89f4SRuslan Bukin struct xae_softc *sc;
26685ae89f4SRuslan Bukin struct mbuf *m;
26785ae89f4SRuslan Bukin struct buf_ring *br;
26885ae89f4SRuslan Bukin int error;
26985ae89f4SRuslan Bukin int enq;
27085ae89f4SRuslan Bukin
27185ae89f4SRuslan Bukin dprintf("%s\n", __func__);
27285ae89f4SRuslan Bukin
273dba12f75SJustin Hibbits sc = if_getsoftc(ifp);
27485ae89f4SRuslan Bukin br = sc->br;
27585ae89f4SRuslan Bukin
27685ae89f4SRuslan Bukin enq = 0;
27785ae89f4SRuslan Bukin
27885ae89f4SRuslan Bukin while ((m = drbr_peek(ifp, br)) != NULL) {
27985ae89f4SRuslan Bukin error = xdma_enqueue_mbuf(sc->xchan_tx,
28085ae89f4SRuslan Bukin &m, 0, 4, 4, XDMA_MEM_TO_DEV);
28185ae89f4SRuslan Bukin if (error != 0) {
28285ae89f4SRuslan Bukin /* No space in request queue available yet. */
28385ae89f4SRuslan Bukin drbr_putback(ifp, br, m);
28485ae89f4SRuslan Bukin break;
28585ae89f4SRuslan Bukin }
28685ae89f4SRuslan Bukin
28785ae89f4SRuslan Bukin drbr_advance(ifp, br);
28885ae89f4SRuslan Bukin
28985ae89f4SRuslan Bukin enq++;
29085ae89f4SRuslan Bukin
29185ae89f4SRuslan Bukin /* If anyone is interested give them a copy. */
29285ae89f4SRuslan Bukin ETHER_BPF_MTAP(ifp, m);
29385ae89f4SRuslan Bukin }
29485ae89f4SRuslan Bukin
29585ae89f4SRuslan Bukin if (enq > 0)
29685ae89f4SRuslan Bukin xdma_queue_submit(sc->xchan_tx);
29785ae89f4SRuslan Bukin
29885ae89f4SRuslan Bukin return (0);
29985ae89f4SRuslan Bukin }
30085ae89f4SRuslan Bukin
30185ae89f4SRuslan Bukin static int
xae_transmit(if_t ifp,struct mbuf * m)302dba12f75SJustin Hibbits xae_transmit(if_t ifp, struct mbuf *m)
30385ae89f4SRuslan Bukin {
30485ae89f4SRuslan Bukin struct xae_softc *sc;
30585ae89f4SRuslan Bukin int error;
30685ae89f4SRuslan Bukin
30785ae89f4SRuslan Bukin dprintf("%s\n", __func__);
30885ae89f4SRuslan Bukin
309dba12f75SJustin Hibbits sc = if_getsoftc(ifp);
31085ae89f4SRuslan Bukin
31185ae89f4SRuslan Bukin XAE_LOCK(sc);
31285ae89f4SRuslan Bukin
31385ae89f4SRuslan Bukin error = drbr_enqueue(ifp, sc->br, m);
31485ae89f4SRuslan Bukin if (error) {
31585ae89f4SRuslan Bukin XAE_UNLOCK(sc);
31685ae89f4SRuslan Bukin return (error);
31785ae89f4SRuslan Bukin }
31885ae89f4SRuslan Bukin
319dba12f75SJustin Hibbits if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
32085ae89f4SRuslan Bukin IFF_DRV_RUNNING) {
32185ae89f4SRuslan Bukin XAE_UNLOCK(sc);
32285ae89f4SRuslan Bukin return (0);
32385ae89f4SRuslan Bukin }
32485ae89f4SRuslan Bukin
32585ae89f4SRuslan Bukin if (!sc->link_is_up) {
32685ae89f4SRuslan Bukin XAE_UNLOCK(sc);
32785ae89f4SRuslan Bukin return (0);
32885ae89f4SRuslan Bukin }
32985ae89f4SRuslan Bukin
33085ae89f4SRuslan Bukin error = xae_transmit_locked(ifp);
33185ae89f4SRuslan Bukin
33285ae89f4SRuslan Bukin XAE_UNLOCK(sc);
33385ae89f4SRuslan Bukin
33485ae89f4SRuslan Bukin return (error);
33585ae89f4SRuslan Bukin }
33685ae89f4SRuslan Bukin
33785ae89f4SRuslan Bukin static void
xae_stop_locked(struct xae_softc * sc)33885ae89f4SRuslan Bukin xae_stop_locked(struct xae_softc *sc)
33985ae89f4SRuslan Bukin {
340dba12f75SJustin Hibbits if_t ifp;
34185ae89f4SRuslan Bukin uint32_t reg;
34285ae89f4SRuslan Bukin
34385ae89f4SRuslan Bukin XAE_ASSERT_LOCKED(sc);
34485ae89f4SRuslan Bukin
34585ae89f4SRuslan Bukin ifp = sc->ifp;
346dba12f75SJustin Hibbits if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
34785ae89f4SRuslan Bukin
34885ae89f4SRuslan Bukin callout_stop(&sc->xae_callout);
34985ae89f4SRuslan Bukin
35085ae89f4SRuslan Bukin /* Stop the transmitter */
35185ae89f4SRuslan Bukin reg = READ4(sc, XAE_TC);
35285ae89f4SRuslan Bukin reg &= ~TC_TX;
35385ae89f4SRuslan Bukin WRITE4(sc, XAE_TC, reg);
35485ae89f4SRuslan Bukin
35585ae89f4SRuslan Bukin /* Stop the receiver. */
35685ae89f4SRuslan Bukin reg = READ4(sc, XAE_RCW1);
35785ae89f4SRuslan Bukin reg &= ~RCW1_RX;
35885ae89f4SRuslan Bukin WRITE4(sc, XAE_RCW1, reg);
35985ae89f4SRuslan Bukin }
36085ae89f4SRuslan Bukin
36185ae89f4SRuslan Bukin static uint64_t
xae_stat(struct xae_softc * sc,int counter_id)36285ae89f4SRuslan Bukin xae_stat(struct xae_softc *sc, int counter_id)
36385ae89f4SRuslan Bukin {
36485ae89f4SRuslan Bukin uint64_t new, old;
36585ae89f4SRuslan Bukin uint64_t delta;
36685ae89f4SRuslan Bukin
36785ae89f4SRuslan Bukin KASSERT(counter_id < XAE_MAX_COUNTERS,
36885ae89f4SRuslan Bukin ("counter %d is out of range", counter_id));
36985ae89f4SRuslan Bukin
37085ae89f4SRuslan Bukin new = READ8(sc, XAE_STATCNT(counter_id));
37185ae89f4SRuslan Bukin old = sc->counters[counter_id];
37285ae89f4SRuslan Bukin
37385ae89f4SRuslan Bukin if (new >= old)
37485ae89f4SRuslan Bukin delta = new - old;
37585ae89f4SRuslan Bukin else
37685ae89f4SRuslan Bukin delta = UINT64_MAX - old + new;
37785ae89f4SRuslan Bukin sc->counters[counter_id] = new;
37885ae89f4SRuslan Bukin
37985ae89f4SRuslan Bukin return (delta);
38085ae89f4SRuslan Bukin }
38185ae89f4SRuslan Bukin
38285ae89f4SRuslan Bukin static void
xae_harvest_stats(struct xae_softc * sc)38385ae89f4SRuslan Bukin xae_harvest_stats(struct xae_softc *sc)
38485ae89f4SRuslan Bukin {
385dba12f75SJustin Hibbits if_t ifp;
38685ae89f4SRuslan Bukin
38785ae89f4SRuslan Bukin ifp = sc->ifp;
38885ae89f4SRuslan Bukin
38985ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IPACKETS, xae_stat(sc, RX_GOOD_FRAMES));
39085ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IMCASTS, xae_stat(sc, RX_GOOD_MCASTS));
39185ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IERRORS,
39285ae89f4SRuslan Bukin xae_stat(sc, RX_FRAME_CHECK_SEQ_ERROR) +
39385ae89f4SRuslan Bukin xae_stat(sc, RX_LEN_OUT_OF_RANGE) +
39485ae89f4SRuslan Bukin xae_stat(sc, RX_ALIGNMENT_ERRORS));
39585ae89f4SRuslan Bukin
39685ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OBYTES, xae_stat(sc, TX_BYTES));
39785ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OPACKETS, xae_stat(sc, TX_GOOD_FRAMES));
39885ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OMCASTS, xae_stat(sc, TX_GOOD_MCASTS));
39985ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OERRORS,
40085ae89f4SRuslan Bukin xae_stat(sc, TX_GOOD_UNDERRUN_ERRORS));
40185ae89f4SRuslan Bukin
40285ae89f4SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
40385ae89f4SRuslan Bukin xae_stat(sc, TX_SINGLE_COLLISION_FRAMES) +
40485ae89f4SRuslan Bukin xae_stat(sc, TX_MULTI_COLLISION_FRAMES) +
40585ae89f4SRuslan Bukin xae_stat(sc, TX_LATE_COLLISIONS) +
40685ae89f4SRuslan Bukin xae_stat(sc, TX_EXCESS_COLLISIONS));
40785ae89f4SRuslan Bukin }
40885ae89f4SRuslan Bukin
40985ae89f4SRuslan Bukin static void
xae_tick(void * arg)41085ae89f4SRuslan Bukin xae_tick(void *arg)
41185ae89f4SRuslan Bukin {
41285ae89f4SRuslan Bukin struct xae_softc *sc;
413dba12f75SJustin Hibbits if_t ifp;
41485ae89f4SRuslan Bukin int link_was_up;
41585ae89f4SRuslan Bukin
41685ae89f4SRuslan Bukin sc = arg;
41785ae89f4SRuslan Bukin
41885ae89f4SRuslan Bukin XAE_ASSERT_LOCKED(sc);
41985ae89f4SRuslan Bukin
42085ae89f4SRuslan Bukin ifp = sc->ifp;
42185ae89f4SRuslan Bukin
422dba12f75SJustin Hibbits if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
42385ae89f4SRuslan Bukin return;
42485ae89f4SRuslan Bukin
42585ae89f4SRuslan Bukin /* Gather stats from hardware counters. */
42685ae89f4SRuslan Bukin xae_harvest_stats(sc);
42785ae89f4SRuslan Bukin
42885ae89f4SRuslan Bukin /* Check the media status. */
42985ae89f4SRuslan Bukin link_was_up = sc->link_is_up;
43085ae89f4SRuslan Bukin mii_tick(sc->mii_softc);
43185ae89f4SRuslan Bukin if (sc->link_is_up && !link_was_up)
43285ae89f4SRuslan Bukin xae_transmit_locked(sc->ifp);
43385ae89f4SRuslan Bukin
43485ae89f4SRuslan Bukin /* Schedule another check one second from now. */
43585ae89f4SRuslan Bukin callout_reset(&sc->xae_callout, hz, xae_tick, sc);
43685ae89f4SRuslan Bukin }
43785ae89f4SRuslan Bukin
43885ae89f4SRuslan Bukin static void
xae_init_locked(struct xae_softc * sc)43985ae89f4SRuslan Bukin xae_init_locked(struct xae_softc *sc)
44085ae89f4SRuslan Bukin {
441dba12f75SJustin Hibbits if_t ifp;
44285ae89f4SRuslan Bukin
44385ae89f4SRuslan Bukin XAE_ASSERT_LOCKED(sc);
44485ae89f4SRuslan Bukin
44585ae89f4SRuslan Bukin ifp = sc->ifp;
446dba12f75SJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
44785ae89f4SRuslan Bukin return;
44885ae89f4SRuslan Bukin
449dba12f75SJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
45085ae89f4SRuslan Bukin
45185ae89f4SRuslan Bukin xae_setup_rxfilter(sc);
45285ae89f4SRuslan Bukin
45385ae89f4SRuslan Bukin /* Enable the transmitter */
45485ae89f4SRuslan Bukin WRITE4(sc, XAE_TC, TC_TX);
45585ae89f4SRuslan Bukin
45685ae89f4SRuslan Bukin /* Enable the receiver. */
45785ae89f4SRuslan Bukin WRITE4(sc, XAE_RCW1, RCW1_RX);
45885ae89f4SRuslan Bukin
45985ae89f4SRuslan Bukin /*
46085ae89f4SRuslan Bukin * Call mii_mediachg() which will call back into xae_miibus_statchg()
46185ae89f4SRuslan Bukin * to set up the remaining config registers based on current media.
46285ae89f4SRuslan Bukin */
46385ae89f4SRuslan Bukin mii_mediachg(sc->mii_softc);
46485ae89f4SRuslan Bukin callout_reset(&sc->xae_callout, hz, xae_tick, sc);
46585ae89f4SRuslan Bukin }
46685ae89f4SRuslan Bukin
46785ae89f4SRuslan Bukin static void
xae_init(void * arg)46885ae89f4SRuslan Bukin xae_init(void *arg)
46985ae89f4SRuslan Bukin {
47085ae89f4SRuslan Bukin struct xae_softc *sc;
47185ae89f4SRuslan Bukin
47285ae89f4SRuslan Bukin sc = arg;
47385ae89f4SRuslan Bukin
47485ae89f4SRuslan Bukin XAE_LOCK(sc);
47585ae89f4SRuslan Bukin xae_init_locked(sc);
47685ae89f4SRuslan Bukin XAE_UNLOCK(sc);
47785ae89f4SRuslan Bukin }
47885ae89f4SRuslan Bukin
47985ae89f4SRuslan Bukin static void
xae_media_status(if_t ifp,struct ifmediareq * ifmr)480dba12f75SJustin Hibbits xae_media_status(if_t ifp, struct ifmediareq *ifmr)
48185ae89f4SRuslan Bukin {
48285ae89f4SRuslan Bukin struct xae_softc *sc;
48385ae89f4SRuslan Bukin struct mii_data *mii;
48485ae89f4SRuslan Bukin
485dba12f75SJustin Hibbits sc = if_getsoftc(ifp);
48685ae89f4SRuslan Bukin mii = sc->mii_softc;
48785ae89f4SRuslan Bukin
48885ae89f4SRuslan Bukin XAE_LOCK(sc);
48985ae89f4SRuslan Bukin mii_pollstat(mii);
49085ae89f4SRuslan Bukin ifmr->ifm_active = mii->mii_media_active;
49185ae89f4SRuslan Bukin ifmr->ifm_status = mii->mii_media_status;
49285ae89f4SRuslan Bukin XAE_UNLOCK(sc);
49385ae89f4SRuslan Bukin }
49485ae89f4SRuslan Bukin
49585ae89f4SRuslan Bukin static int
xae_media_change_locked(struct xae_softc * sc)49685ae89f4SRuslan Bukin xae_media_change_locked(struct xae_softc *sc)
49785ae89f4SRuslan Bukin {
49885ae89f4SRuslan Bukin
49985ae89f4SRuslan Bukin return (mii_mediachg(sc->mii_softc));
50085ae89f4SRuslan Bukin }
50185ae89f4SRuslan Bukin
50285ae89f4SRuslan Bukin static int
xae_media_change(if_t ifp)503dba12f75SJustin Hibbits xae_media_change(if_t ifp)
50485ae89f4SRuslan Bukin {
50585ae89f4SRuslan Bukin struct xae_softc *sc;
50685ae89f4SRuslan Bukin int error;
50785ae89f4SRuslan Bukin
508dba12f75SJustin Hibbits sc = if_getsoftc(ifp);
50985ae89f4SRuslan Bukin
51085ae89f4SRuslan Bukin XAE_LOCK(sc);
51185ae89f4SRuslan Bukin error = xae_media_change_locked(sc);
51285ae89f4SRuslan Bukin XAE_UNLOCK(sc);
51385ae89f4SRuslan Bukin
51485ae89f4SRuslan Bukin return (error);
51585ae89f4SRuslan Bukin }
51685ae89f4SRuslan Bukin
5174d50c26aSGleb Smirnoff static u_int
xae_write_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)5184d50c26aSGleb Smirnoff xae_write_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
5194d50c26aSGleb Smirnoff {
5204d50c26aSGleb Smirnoff struct xae_softc *sc = arg;
5214d50c26aSGleb Smirnoff uint32_t reg;
5224d50c26aSGleb Smirnoff uint8_t *ma;
5234d50c26aSGleb Smirnoff
5244d50c26aSGleb Smirnoff if (cnt >= XAE_MULTICAST_TABLE_SIZE)
5254d50c26aSGleb Smirnoff return (1);
5264d50c26aSGleb Smirnoff
5274d50c26aSGleb Smirnoff ma = LLADDR(sdl);
5284d50c26aSGleb Smirnoff
5294d50c26aSGleb Smirnoff reg = READ4(sc, XAE_FFC) & 0xffffff00;
5304d50c26aSGleb Smirnoff reg |= cnt;
5314d50c26aSGleb Smirnoff WRITE4(sc, XAE_FFC, reg);
5324d50c26aSGleb Smirnoff
5334d50c26aSGleb Smirnoff reg = (ma[0]);
5344d50c26aSGleb Smirnoff reg |= (ma[1] << 8);
5354d50c26aSGleb Smirnoff reg |= (ma[2] << 16);
5364d50c26aSGleb Smirnoff reg |= (ma[3] << 24);
5374d50c26aSGleb Smirnoff WRITE4(sc, XAE_FFV(0), reg);
5384d50c26aSGleb Smirnoff
5394d50c26aSGleb Smirnoff reg = ma[4];
5404d50c26aSGleb Smirnoff reg |= ma[5] << 8;
5414d50c26aSGleb Smirnoff WRITE4(sc, XAE_FFV(1), reg);
5424d50c26aSGleb Smirnoff
5434d50c26aSGleb Smirnoff return (1);
5444d50c26aSGleb Smirnoff }
5454d50c26aSGleb Smirnoff
54685ae89f4SRuslan Bukin static void
xae_setup_rxfilter(struct xae_softc * sc)54785ae89f4SRuslan Bukin xae_setup_rxfilter(struct xae_softc *sc)
54885ae89f4SRuslan Bukin {
549dba12f75SJustin Hibbits if_t ifp;
55085ae89f4SRuslan Bukin uint32_t reg;
55185ae89f4SRuslan Bukin
55285ae89f4SRuslan Bukin XAE_ASSERT_LOCKED(sc);
55385ae89f4SRuslan Bukin
55485ae89f4SRuslan Bukin ifp = sc->ifp;
55585ae89f4SRuslan Bukin
55685ae89f4SRuslan Bukin /*
55785ae89f4SRuslan Bukin * Set the multicast (group) filter hash.
55885ae89f4SRuslan Bukin */
559dba12f75SJustin Hibbits if ((if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
56085ae89f4SRuslan Bukin reg = READ4(sc, XAE_FFC);
56185ae89f4SRuslan Bukin reg |= FFC_PM;
56285ae89f4SRuslan Bukin WRITE4(sc, XAE_FFC, reg);
56385ae89f4SRuslan Bukin } else {
56485ae89f4SRuslan Bukin reg = READ4(sc, XAE_FFC);
56585ae89f4SRuslan Bukin reg &= ~FFC_PM;
56685ae89f4SRuslan Bukin WRITE4(sc, XAE_FFC, reg);
56785ae89f4SRuslan Bukin
5684d50c26aSGleb Smirnoff if_foreach_llmaddr(ifp, xae_write_maddr, sc);
56985ae89f4SRuslan Bukin }
57085ae89f4SRuslan Bukin
57185ae89f4SRuslan Bukin /*
57285ae89f4SRuslan Bukin * Set the primary address.
57385ae89f4SRuslan Bukin */
57485ae89f4SRuslan Bukin reg = sc->macaddr[0];
57585ae89f4SRuslan Bukin reg |= (sc->macaddr[1] << 8);
57685ae89f4SRuslan Bukin reg |= (sc->macaddr[2] << 16);
57785ae89f4SRuslan Bukin reg |= (sc->macaddr[3] << 24);
57885ae89f4SRuslan Bukin WRITE4(sc, XAE_UAW0, reg);
57985ae89f4SRuslan Bukin
58085ae89f4SRuslan Bukin reg = sc->macaddr[4];
58185ae89f4SRuslan Bukin reg |= (sc->macaddr[5] << 8);
58285ae89f4SRuslan Bukin WRITE4(sc, XAE_UAW1, reg);
58385ae89f4SRuslan Bukin }
58485ae89f4SRuslan Bukin
58585ae89f4SRuslan Bukin static int
xae_ioctl(if_t ifp,u_long cmd,caddr_t data)586dba12f75SJustin Hibbits xae_ioctl(if_t ifp, u_long cmd, caddr_t data)
58785ae89f4SRuslan Bukin {
58885ae89f4SRuslan Bukin struct xae_softc *sc;
58985ae89f4SRuslan Bukin struct mii_data *mii;
59085ae89f4SRuslan Bukin struct ifreq *ifr;
59185ae89f4SRuslan Bukin int mask, error;
59285ae89f4SRuslan Bukin
593dba12f75SJustin Hibbits sc = if_getsoftc(ifp);
59485ae89f4SRuslan Bukin ifr = (struct ifreq *)data;
59585ae89f4SRuslan Bukin
59685ae89f4SRuslan Bukin error = 0;
59785ae89f4SRuslan Bukin switch (cmd) {
59885ae89f4SRuslan Bukin case SIOCSIFFLAGS:
59985ae89f4SRuslan Bukin XAE_LOCK(sc);
600dba12f75SJustin Hibbits if (if_getflags(ifp) & IFF_UP) {
601dba12f75SJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
602dba12f75SJustin Hibbits if ((if_getflags(ifp) ^ sc->if_flags) &
60385ae89f4SRuslan Bukin (IFF_PROMISC | IFF_ALLMULTI))
60485ae89f4SRuslan Bukin xae_setup_rxfilter(sc);
60585ae89f4SRuslan Bukin } else {
60685ae89f4SRuslan Bukin if (!sc->is_detaching)
60785ae89f4SRuslan Bukin xae_init_locked(sc);
60885ae89f4SRuslan Bukin }
60985ae89f4SRuslan Bukin } else {
610dba12f75SJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
61185ae89f4SRuslan Bukin xae_stop_locked(sc);
61285ae89f4SRuslan Bukin }
613dba12f75SJustin Hibbits sc->if_flags = if_getflags(ifp);
61485ae89f4SRuslan Bukin XAE_UNLOCK(sc);
61585ae89f4SRuslan Bukin break;
61685ae89f4SRuslan Bukin case SIOCADDMULTI:
61785ae89f4SRuslan Bukin case SIOCDELMULTI:
618dba12f75SJustin Hibbits if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
61985ae89f4SRuslan Bukin XAE_LOCK(sc);
62085ae89f4SRuslan Bukin xae_setup_rxfilter(sc);
62185ae89f4SRuslan Bukin XAE_UNLOCK(sc);
62285ae89f4SRuslan Bukin }
62385ae89f4SRuslan Bukin break;
62485ae89f4SRuslan Bukin case SIOCSIFMEDIA:
62585ae89f4SRuslan Bukin case SIOCGIFMEDIA:
62685ae89f4SRuslan Bukin mii = sc->mii_softc;
62785ae89f4SRuslan Bukin error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
62885ae89f4SRuslan Bukin break;
62985ae89f4SRuslan Bukin case SIOCSIFCAP:
630dba12f75SJustin Hibbits mask = if_getcapenable(ifp) ^ ifr->ifr_reqcap;
63185ae89f4SRuslan Bukin if (mask & IFCAP_VLAN_MTU) {
63285ae89f4SRuslan Bukin /* No work to do except acknowledge the change took */
633dba12f75SJustin Hibbits if_togglecapenable(ifp, IFCAP_VLAN_MTU);
63485ae89f4SRuslan Bukin }
63585ae89f4SRuslan Bukin break;
63685ae89f4SRuslan Bukin
63785ae89f4SRuslan Bukin default:
63885ae89f4SRuslan Bukin error = ether_ioctl(ifp, cmd, data);
63985ae89f4SRuslan Bukin break;
64085ae89f4SRuslan Bukin }
64185ae89f4SRuslan Bukin
64285ae89f4SRuslan Bukin return (error);
64385ae89f4SRuslan Bukin }
64485ae89f4SRuslan Bukin
64585ae89f4SRuslan Bukin static void
xae_intr(void * arg)64685ae89f4SRuslan Bukin xae_intr(void *arg)
64785ae89f4SRuslan Bukin {
64885ae89f4SRuslan Bukin
64985ae89f4SRuslan Bukin }
65085ae89f4SRuslan Bukin
65185ae89f4SRuslan Bukin static int
xae_get_hwaddr(struct xae_softc * sc,uint8_t * hwaddr)65285ae89f4SRuslan Bukin xae_get_hwaddr(struct xae_softc *sc, uint8_t *hwaddr)
65385ae89f4SRuslan Bukin {
65485ae89f4SRuslan Bukin phandle_t node;
65585ae89f4SRuslan Bukin int len;
65685ae89f4SRuslan Bukin
65785ae89f4SRuslan Bukin node = ofw_bus_get_node(sc->dev);
65885ae89f4SRuslan Bukin
65985ae89f4SRuslan Bukin /* Check if there is property */
66085ae89f4SRuslan Bukin if ((len = OF_getproplen(node, "local-mac-address")) <= 0)
66185ae89f4SRuslan Bukin return (EINVAL);
66285ae89f4SRuslan Bukin
66385ae89f4SRuslan Bukin if (len != ETHER_ADDR_LEN)
66485ae89f4SRuslan Bukin return (EINVAL);
66585ae89f4SRuslan Bukin
66685ae89f4SRuslan Bukin OF_getprop(node, "local-mac-address", hwaddr,
66785ae89f4SRuslan Bukin ETHER_ADDR_LEN);
66885ae89f4SRuslan Bukin
66985ae89f4SRuslan Bukin return (0);
67085ae89f4SRuslan Bukin }
67185ae89f4SRuslan Bukin
67285ae89f4SRuslan Bukin static int
mdio_wait(struct xae_softc * sc)67385ae89f4SRuslan Bukin mdio_wait(struct xae_softc *sc)
67485ae89f4SRuslan Bukin {
67585ae89f4SRuslan Bukin uint32_t reg;
67685ae89f4SRuslan Bukin int timeout;
67785ae89f4SRuslan Bukin
67885ae89f4SRuslan Bukin timeout = 200;
67985ae89f4SRuslan Bukin
68085ae89f4SRuslan Bukin do {
68185ae89f4SRuslan Bukin reg = READ4(sc, XAE_MDIO_CTRL);
68285ae89f4SRuslan Bukin if (reg & MDIO_CTRL_READY)
68385ae89f4SRuslan Bukin break;
68485ae89f4SRuslan Bukin DELAY(1);
68585ae89f4SRuslan Bukin } while (timeout--);
68685ae89f4SRuslan Bukin
68785ae89f4SRuslan Bukin if (timeout <= 0) {
68885ae89f4SRuslan Bukin printf("Failed to get MDIO ready\n");
68985ae89f4SRuslan Bukin return (1);
69085ae89f4SRuslan Bukin }
69185ae89f4SRuslan Bukin
69285ae89f4SRuslan Bukin return (0);
69385ae89f4SRuslan Bukin }
69485ae89f4SRuslan Bukin
69585ae89f4SRuslan Bukin static int
xae_miibus_read_reg(device_t dev,int phy,int reg)69685ae89f4SRuslan Bukin xae_miibus_read_reg(device_t dev, int phy, int reg)
69785ae89f4SRuslan Bukin {
69885ae89f4SRuslan Bukin struct xae_softc *sc;
69985ae89f4SRuslan Bukin uint32_t mii;
70085ae89f4SRuslan Bukin int rv;
70185ae89f4SRuslan Bukin
70285ae89f4SRuslan Bukin sc = device_get_softc(dev);
70385ae89f4SRuslan Bukin
70485ae89f4SRuslan Bukin if (mdio_wait(sc))
70585ae89f4SRuslan Bukin return (0);
70685ae89f4SRuslan Bukin
70785ae89f4SRuslan Bukin mii = MDIO_CTRL_TX_OP_READ | MDIO_CTRL_INITIATE;
70885ae89f4SRuslan Bukin mii |= (reg << MDIO_TX_REGAD_S);
70985ae89f4SRuslan Bukin mii |= (phy << MDIO_TX_PHYAD_S);
71085ae89f4SRuslan Bukin
71185ae89f4SRuslan Bukin WRITE4(sc, XAE_MDIO_CTRL, mii);
71285ae89f4SRuslan Bukin
71385ae89f4SRuslan Bukin if (mdio_wait(sc))
71485ae89f4SRuslan Bukin return (0);
71585ae89f4SRuslan Bukin
71685ae89f4SRuslan Bukin rv = READ4(sc, XAE_MDIO_READ);
71785ae89f4SRuslan Bukin
71885ae89f4SRuslan Bukin return (rv);
71985ae89f4SRuslan Bukin }
72085ae89f4SRuslan Bukin
72185ae89f4SRuslan Bukin static int
xae_miibus_write_reg(device_t dev,int phy,int reg,int val)72285ae89f4SRuslan Bukin xae_miibus_write_reg(device_t dev, int phy, int reg, int val)
72385ae89f4SRuslan Bukin {
72485ae89f4SRuslan Bukin struct xae_softc *sc;
72585ae89f4SRuslan Bukin uint32_t mii;
72685ae89f4SRuslan Bukin
72785ae89f4SRuslan Bukin sc = device_get_softc(dev);
72885ae89f4SRuslan Bukin
72985ae89f4SRuslan Bukin if (mdio_wait(sc))
73085ae89f4SRuslan Bukin return (1);
73185ae89f4SRuslan Bukin
73285ae89f4SRuslan Bukin mii = MDIO_CTRL_TX_OP_WRITE | MDIO_CTRL_INITIATE;
73385ae89f4SRuslan Bukin mii |= (reg << MDIO_TX_REGAD_S);
73485ae89f4SRuslan Bukin mii |= (phy << MDIO_TX_PHYAD_S);
73585ae89f4SRuslan Bukin
73685ae89f4SRuslan Bukin WRITE4(sc, XAE_MDIO_WRITE, val);
73785ae89f4SRuslan Bukin WRITE4(sc, XAE_MDIO_CTRL, mii);
73885ae89f4SRuslan Bukin
73985ae89f4SRuslan Bukin if (mdio_wait(sc))
74085ae89f4SRuslan Bukin return (1);
74185ae89f4SRuslan Bukin
74285ae89f4SRuslan Bukin return (0);
74385ae89f4SRuslan Bukin }
74485ae89f4SRuslan Bukin
74585ae89f4SRuslan Bukin static void
xae_phy_fixup(struct xae_softc * sc)74685ae89f4SRuslan Bukin xae_phy_fixup(struct xae_softc *sc)
74785ae89f4SRuslan Bukin {
74885ae89f4SRuslan Bukin uint32_t reg;
74985ae89f4SRuslan Bukin
75085ae89f4SRuslan Bukin do {
75185ae89f4SRuslan Bukin WRITE_TI_EREG(sc, DP83867_SGMIICTL1, SGMIICTL1_SGMII_6W);
75285ae89f4SRuslan Bukin PHY_WR(sc, DP83867_PHYCR, PHYCR_SGMII_EN);
75385ae89f4SRuslan Bukin
75485ae89f4SRuslan Bukin reg = PHY_RD(sc, DP83867_CFG2);
75585ae89f4SRuslan Bukin reg &= ~CFG2_SPEED_OPT_ATTEMPT_CNT_M;
75685ae89f4SRuslan Bukin reg |= (CFG2_SPEED_OPT_ATTEMPT_CNT_4);
75785ae89f4SRuslan Bukin reg |= CFG2_INTERRUPT_POLARITY;
75885ae89f4SRuslan Bukin reg |= CFG2_SPEED_OPT_ENHANCED_EN;
75985ae89f4SRuslan Bukin reg |= CFG2_SPEED_OPT_10M_EN;
76085ae89f4SRuslan Bukin PHY_WR(sc, DP83867_CFG2, reg);
76185ae89f4SRuslan Bukin
76285ae89f4SRuslan Bukin WRITE_TI_EREG(sc, DP83867_CFG4, CFG4_SGMII_TMR);
76385ae89f4SRuslan Bukin PHY_WR(sc, MII_BMCR,
76485ae89f4SRuslan Bukin BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_RESET);
76585ae89f4SRuslan Bukin } while (PHY1_RD(sc, MII_BMCR) == 0x0ffff);
76685ae89f4SRuslan Bukin
76785ae89f4SRuslan Bukin do {
76885ae89f4SRuslan Bukin PHY1_WR(sc, MII_BMCR,
76985ae89f4SRuslan Bukin BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_STARTNEG);
77085ae89f4SRuslan Bukin DELAY(40000);
77185ae89f4SRuslan Bukin } while ((PHY1_RD(sc, MII_BMSR) & BMSR_ACOMP) == 0);
77285ae89f4SRuslan Bukin }
77385ae89f4SRuslan Bukin
77485ae89f4SRuslan Bukin static int
get_xdma_std(struct xae_softc * sc)775a8692c16SRuslan Bukin get_xdma_std(struct xae_softc *sc)
776a8692c16SRuslan Bukin {
777a8692c16SRuslan Bukin
778a8692c16SRuslan Bukin sc->xdma_tx = xdma_ofw_get(sc->dev, "tx");
779a8692c16SRuslan Bukin if (sc->xdma_tx == NULL)
780a8692c16SRuslan Bukin return (ENXIO);
781a8692c16SRuslan Bukin
782a8692c16SRuslan Bukin sc->xdma_rx = xdma_ofw_get(sc->dev, "rx");
783a8692c16SRuslan Bukin if (sc->xdma_rx == NULL) {
784a8692c16SRuslan Bukin xdma_put(sc->xdma_tx);
785a8692c16SRuslan Bukin return (ENXIO);
786a8692c16SRuslan Bukin }
787a8692c16SRuslan Bukin
788a8692c16SRuslan Bukin return (0);
789a8692c16SRuslan Bukin }
790a8692c16SRuslan Bukin
791a8692c16SRuslan Bukin static int
get_xdma_axistream(struct xae_softc * sc)792a8692c16SRuslan Bukin get_xdma_axistream(struct xae_softc *sc)
793a8692c16SRuslan Bukin {
794a8692c16SRuslan Bukin struct axidma_fdt_data *data;
795a8692c16SRuslan Bukin device_t dma_dev;
796a8692c16SRuslan Bukin phandle_t node;
797a8692c16SRuslan Bukin pcell_t prop;
798a8692c16SRuslan Bukin size_t len;
799a8692c16SRuslan Bukin
800a8692c16SRuslan Bukin node = ofw_bus_get_node(sc->dev);
801a8692c16SRuslan Bukin len = OF_getencprop(node, "axistream-connected", &prop, sizeof(prop));
802a8692c16SRuslan Bukin if (len != sizeof(prop)) {
803a8692c16SRuslan Bukin device_printf(sc->dev,
804a8692c16SRuslan Bukin "%s: Couldn't get axistream-connected prop.\n", __func__);
805a8692c16SRuslan Bukin return (ENXIO);
806a8692c16SRuslan Bukin }
807a8692c16SRuslan Bukin dma_dev = OF_device_from_xref(prop);
808a8692c16SRuslan Bukin if (dma_dev == NULL) {
809a8692c16SRuslan Bukin device_printf(sc->dev, "Could not get DMA device by xref.\n");
810a8692c16SRuslan Bukin return (ENXIO);
811a8692c16SRuslan Bukin }
812a8692c16SRuslan Bukin
813a8692c16SRuslan Bukin sc->xdma_tx = xdma_get(sc->dev, dma_dev);
814a8692c16SRuslan Bukin if (sc->xdma_tx == NULL) {
815a8692c16SRuslan Bukin device_printf(sc->dev, "Could not find DMA controller.\n");
816a8692c16SRuslan Bukin return (ENXIO);
817a8692c16SRuslan Bukin }
818a8692c16SRuslan Bukin data = malloc(sizeof(struct axidma_fdt_data),
819a8692c16SRuslan Bukin M_DEVBUF, (M_WAITOK | M_ZERO));
820a8692c16SRuslan Bukin data->id = AXIDMA_TX_CHAN;
821a8692c16SRuslan Bukin sc->xdma_tx->data = data;
822a8692c16SRuslan Bukin
823a8692c16SRuslan Bukin sc->xdma_rx = xdma_get(sc->dev, dma_dev);
824a8692c16SRuslan Bukin if (sc->xdma_rx == NULL) {
825a8692c16SRuslan Bukin device_printf(sc->dev, "Could not find DMA controller.\n");
826a8692c16SRuslan Bukin return (ENXIO);
827a8692c16SRuslan Bukin }
828a8692c16SRuslan Bukin data = malloc(sizeof(struct axidma_fdt_data),
829a8692c16SRuslan Bukin M_DEVBUF, (M_WAITOK | M_ZERO));
830a8692c16SRuslan Bukin data->id = AXIDMA_RX_CHAN;
831a8692c16SRuslan Bukin sc->xdma_rx->data = data;
832a8692c16SRuslan Bukin
833a8692c16SRuslan Bukin return (0);
834a8692c16SRuslan Bukin }
835a8692c16SRuslan Bukin
836a8692c16SRuslan Bukin static int
setup_xdma(struct xae_softc * sc)83785ae89f4SRuslan Bukin setup_xdma(struct xae_softc *sc)
83885ae89f4SRuslan Bukin {
83985ae89f4SRuslan Bukin device_t dev;
84085ae89f4SRuslan Bukin vmem_t *vmem;
841*3969b51fSRuslan Bukin vm_paddr_t phys;
842*3969b51fSRuslan Bukin vm_page_t m;
84385ae89f4SRuslan Bukin int error;
84485ae89f4SRuslan Bukin
84585ae89f4SRuslan Bukin dev = sc->dev;
84685ae89f4SRuslan Bukin
84785ae89f4SRuslan Bukin /* Get xDMA controller */
848a8692c16SRuslan Bukin error = get_xdma_std(sc);
849a8692c16SRuslan Bukin
850a8692c16SRuslan Bukin if (error) {
851a8692c16SRuslan Bukin device_printf(sc->dev,
852a8692c16SRuslan Bukin "Fallback to axistream-connected property\n");
853a8692c16SRuslan Bukin error = get_xdma_axistream(sc);
85485ae89f4SRuslan Bukin }
85585ae89f4SRuslan Bukin
856a8692c16SRuslan Bukin if (error) {
857a8692c16SRuslan Bukin device_printf(dev, "Could not find xDMA controllers.\n");
85885ae89f4SRuslan Bukin return (ENXIO);
85985ae89f4SRuslan Bukin }
86085ae89f4SRuslan Bukin
86185ae89f4SRuslan Bukin /* Alloc xDMA TX virtual channel. */
86285ae89f4SRuslan Bukin sc->xchan_tx = xdma_channel_alloc(sc->xdma_tx, 0);
86385ae89f4SRuslan Bukin if (sc->xchan_tx == NULL) {
86485ae89f4SRuslan Bukin device_printf(dev, "Can't alloc virtual DMA TX channel.\n");
86585ae89f4SRuslan Bukin return (ENXIO);
86685ae89f4SRuslan Bukin }
86785ae89f4SRuslan Bukin
86885ae89f4SRuslan Bukin /* Setup interrupt handler. */
869d987842dSRuslan Bukin error = xdma_setup_intr(sc->xchan_tx, 0,
87085ae89f4SRuslan Bukin xae_xdma_tx_intr, sc, &sc->ih_tx);
87185ae89f4SRuslan Bukin if (error) {
87285ae89f4SRuslan Bukin device_printf(sc->dev,
87385ae89f4SRuslan Bukin "Can't setup xDMA TX interrupt handler.\n");
87485ae89f4SRuslan Bukin return (ENXIO);
87585ae89f4SRuslan Bukin }
87685ae89f4SRuslan Bukin
87785ae89f4SRuslan Bukin /* Alloc xDMA RX virtual channel. */
87885ae89f4SRuslan Bukin sc->xchan_rx = xdma_channel_alloc(sc->xdma_rx, 0);
87985ae89f4SRuslan Bukin if (sc->xchan_rx == NULL) {
88085ae89f4SRuslan Bukin device_printf(dev, "Can't alloc virtual DMA RX channel.\n");
88185ae89f4SRuslan Bukin return (ENXIO);
88285ae89f4SRuslan Bukin }
88385ae89f4SRuslan Bukin
88485ae89f4SRuslan Bukin /* Setup interrupt handler. */
885d987842dSRuslan Bukin error = xdma_setup_intr(sc->xchan_rx, XDMA_INTR_NET,
88685ae89f4SRuslan Bukin xae_xdma_rx_intr, sc, &sc->ih_rx);
88785ae89f4SRuslan Bukin if (error) {
88885ae89f4SRuslan Bukin device_printf(sc->dev,
88985ae89f4SRuslan Bukin "Can't setup xDMA RX interrupt handler.\n");
89085ae89f4SRuslan Bukin return (ENXIO);
89185ae89f4SRuslan Bukin }
89285ae89f4SRuslan Bukin
89385ae89f4SRuslan Bukin /* Setup bounce buffer */
89485ae89f4SRuslan Bukin vmem = xdma_get_memory(dev);
895*3969b51fSRuslan Bukin if (!vmem) {
896*3969b51fSRuslan Bukin m = vm_page_alloc_noobj_contig(VM_ALLOC_WIRED | VM_ALLOC_ZERO,
897*3969b51fSRuslan Bukin BUF_NPAGES, 0, BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE, 0,
898*3969b51fSRuslan Bukin VM_MEMATTR_DEFAULT);
899*3969b51fSRuslan Bukin phys = VM_PAGE_TO_PHYS(m);
900*3969b51fSRuslan Bukin vmem = vmem_create("xdma vmem", 0, 0, PAGE_SIZE, PAGE_SIZE,
901*3969b51fSRuslan Bukin M_BESTFIT | M_WAITOK);
902*3969b51fSRuslan Bukin vmem_add(vmem, phys, BUF_NPAGES * PAGE_SIZE, 0);
903*3969b51fSRuslan Bukin }
904*3969b51fSRuslan Bukin
90585ae89f4SRuslan Bukin xchan_set_memory(sc->xchan_tx, vmem);
90685ae89f4SRuslan Bukin xchan_set_memory(sc->xchan_rx, vmem);
90785ae89f4SRuslan Bukin
90885ae89f4SRuslan Bukin xdma_prep_sg(sc->xchan_tx,
90985ae89f4SRuslan Bukin TX_QUEUE_SIZE, /* xchan requests queue size */
91085ae89f4SRuslan Bukin MCLBYTES, /* maxsegsize */
91185ae89f4SRuslan Bukin 8, /* maxnsegs */
91285ae89f4SRuslan Bukin 16, /* alignment */
91385ae89f4SRuslan Bukin 0, /* boundary */
91485ae89f4SRuslan Bukin BUS_SPACE_MAXADDR_32BIT,
91585ae89f4SRuslan Bukin BUS_SPACE_MAXADDR);
91685ae89f4SRuslan Bukin
91785ae89f4SRuslan Bukin xdma_prep_sg(sc->xchan_rx,
91885ae89f4SRuslan Bukin RX_QUEUE_SIZE, /* xchan requests queue size */
91985ae89f4SRuslan Bukin MCLBYTES, /* maxsegsize */
92085ae89f4SRuslan Bukin 1, /* maxnsegs */
92185ae89f4SRuslan Bukin 16, /* alignment */
92285ae89f4SRuslan Bukin 0, /* boundary */
92385ae89f4SRuslan Bukin BUS_SPACE_MAXADDR_32BIT,
92485ae89f4SRuslan Bukin BUS_SPACE_MAXADDR);
92585ae89f4SRuslan Bukin
92685ae89f4SRuslan Bukin return (0);
92785ae89f4SRuslan Bukin }
92885ae89f4SRuslan Bukin
92985ae89f4SRuslan Bukin static int
xae_probe(device_t dev)93085ae89f4SRuslan Bukin xae_probe(device_t dev)
93185ae89f4SRuslan Bukin {
93285ae89f4SRuslan Bukin
93385ae89f4SRuslan Bukin if (!ofw_bus_status_okay(dev))
93485ae89f4SRuslan Bukin return (ENXIO);
93585ae89f4SRuslan Bukin
93685ae89f4SRuslan Bukin if (!ofw_bus_is_compatible(dev, "xlnx,axi-ethernet-1.00.a"))
93785ae89f4SRuslan Bukin return (ENXIO);
93885ae89f4SRuslan Bukin
93985ae89f4SRuslan Bukin device_set_desc(dev, "Xilinx AXI Ethernet");
94085ae89f4SRuslan Bukin
94185ae89f4SRuslan Bukin return (BUS_PROBE_DEFAULT);
94285ae89f4SRuslan Bukin }
94385ae89f4SRuslan Bukin
94485ae89f4SRuslan Bukin static int
xae_attach(device_t dev)94585ae89f4SRuslan Bukin xae_attach(device_t dev)
94685ae89f4SRuslan Bukin {
94785ae89f4SRuslan Bukin struct xae_softc *sc;
948dba12f75SJustin Hibbits if_t ifp;
94985ae89f4SRuslan Bukin phandle_t node;
95085ae89f4SRuslan Bukin uint32_t reg;
95185ae89f4SRuslan Bukin int error;
95285ae89f4SRuslan Bukin
95385ae89f4SRuslan Bukin sc = device_get_softc(dev);
95485ae89f4SRuslan Bukin sc->dev = dev;
95585ae89f4SRuslan Bukin node = ofw_bus_get_node(dev);
95685ae89f4SRuslan Bukin
95785ae89f4SRuslan Bukin if (setup_xdma(sc) != 0) {
95885ae89f4SRuslan Bukin device_printf(dev, "Could not setup xDMA.\n");
95985ae89f4SRuslan Bukin return (ENXIO);
96085ae89f4SRuslan Bukin }
96185ae89f4SRuslan Bukin
96285ae89f4SRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
96385ae89f4SRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF);
96485ae89f4SRuslan Bukin
96585ae89f4SRuslan Bukin sc->br = buf_ring_alloc(BUFRING_SIZE, M_DEVBUF,
96685ae89f4SRuslan Bukin M_NOWAIT, &sc->mtx);
96785ae89f4SRuslan Bukin if (sc->br == NULL)
96885ae89f4SRuslan Bukin return (ENOMEM);
96985ae89f4SRuslan Bukin
97085ae89f4SRuslan Bukin if (bus_alloc_resources(dev, xae_spec, sc->res)) {
97185ae89f4SRuslan Bukin device_printf(dev, "could not allocate resources\n");
97285ae89f4SRuslan Bukin return (ENXIO);
97385ae89f4SRuslan Bukin }
97485ae89f4SRuslan Bukin
97585ae89f4SRuslan Bukin /* Memory interface */
97685ae89f4SRuslan Bukin sc->bst = rman_get_bustag(sc->res[0]);
97785ae89f4SRuslan Bukin sc->bsh = rman_get_bushandle(sc->res[0]);
97885ae89f4SRuslan Bukin
97985ae89f4SRuslan Bukin device_printf(sc->dev, "Identification: %x\n",
98085ae89f4SRuslan Bukin READ4(sc, XAE_IDENT));
98185ae89f4SRuslan Bukin
98285ae89f4SRuslan Bukin /* Get MAC addr */
98385ae89f4SRuslan Bukin if (xae_get_hwaddr(sc, sc->macaddr)) {
98485ae89f4SRuslan Bukin device_printf(sc->dev, "can't get mac\n");
98585ae89f4SRuslan Bukin return (ENXIO);
98685ae89f4SRuslan Bukin }
98785ae89f4SRuslan Bukin
98885ae89f4SRuslan Bukin /* Enable MII clock */
98985ae89f4SRuslan Bukin reg = (MDIO_CLK_DIV_DEFAULT << MDIO_SETUP_CLK_DIV_S);
99085ae89f4SRuslan Bukin reg |= MDIO_SETUP_ENABLE;
99185ae89f4SRuslan Bukin WRITE4(sc, XAE_MDIO_SETUP, reg);
99285ae89f4SRuslan Bukin if (mdio_wait(sc))
99385ae89f4SRuslan Bukin return (ENXIO);
99485ae89f4SRuslan Bukin
99585ae89f4SRuslan Bukin callout_init_mtx(&sc->xae_callout, &sc->mtx, 0);
99685ae89f4SRuslan Bukin
99785ae89f4SRuslan Bukin /* Setup interrupt handler. */
99885ae89f4SRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
99985ae89f4SRuslan Bukin NULL, xae_intr, sc, &sc->intr_cookie);
100085ae89f4SRuslan Bukin if (error != 0) {
100185ae89f4SRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n");
100285ae89f4SRuslan Bukin return (ENXIO);
100385ae89f4SRuslan Bukin }
100485ae89f4SRuslan Bukin
100585ae89f4SRuslan Bukin /* Set up the ethernet interface. */
100685ae89f4SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER);
1007dba12f75SJustin Hibbits if_setsoftc(ifp, sc);
100885ae89f4SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev));
1009dba12f75SJustin Hibbits if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
1010dba12f75SJustin Hibbits if_setcapabilities(ifp, IFCAP_VLAN_MTU);
1011dba12f75SJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp));
1012dba12f75SJustin Hibbits if_settransmitfn(ifp, xae_transmit);
1013dba12f75SJustin Hibbits if_setqflushfn(ifp, xae_qflush);
1014dba12f75SJustin Hibbits if_setioctlfn(ifp, xae_ioctl);
1015dba12f75SJustin Hibbits if_setinitfn(ifp, xae_init);
1016dba12f75SJustin Hibbits if_setsendqlen(ifp, TX_DESC_COUNT - 1);
1017dba12f75SJustin Hibbits if_setsendqready(ifp);
101885ae89f4SRuslan Bukin
101985ae89f4SRuslan Bukin if (xae_get_phyaddr(node, &sc->phy_addr) != 0)
102085ae89f4SRuslan Bukin return (ENXIO);
102185ae89f4SRuslan Bukin
102285ae89f4SRuslan Bukin /* Attach the mii driver. */
102385ae89f4SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, xae_media_change,
102485ae89f4SRuslan Bukin xae_media_status, BMSR_DEFCAPMASK, sc->phy_addr,
102585ae89f4SRuslan Bukin MII_OFFSET_ANY, 0);
102685ae89f4SRuslan Bukin
102785ae89f4SRuslan Bukin if (error != 0) {
102885ae89f4SRuslan Bukin device_printf(dev, "PHY attach failed\n");
102985ae89f4SRuslan Bukin return (ENXIO);
103085ae89f4SRuslan Bukin }
103185ae89f4SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus);
103285ae89f4SRuslan Bukin
103385ae89f4SRuslan Bukin /* Apply vcu118 workaround. */
103485ae89f4SRuslan Bukin if (OF_getproplen(node, "xlnx,vcu118") >= 0)
103585ae89f4SRuslan Bukin xae_phy_fixup(sc);
103685ae89f4SRuslan Bukin
103785ae89f4SRuslan Bukin /* All ready to run, attach the ethernet interface. */
103885ae89f4SRuslan Bukin ether_ifattach(ifp, sc->macaddr);
103985ae89f4SRuslan Bukin sc->is_attached = true;
104085ae89f4SRuslan Bukin
104185ae89f4SRuslan Bukin xae_rx_enqueue(sc, NUM_RX_MBUF);
104285ae89f4SRuslan Bukin xdma_queue_submit(sc->xchan_rx);
104385ae89f4SRuslan Bukin
104485ae89f4SRuslan Bukin return (0);
104585ae89f4SRuslan Bukin }
104685ae89f4SRuslan Bukin
104785ae89f4SRuslan Bukin static int
xae_detach(device_t dev)104885ae89f4SRuslan Bukin xae_detach(device_t dev)
104985ae89f4SRuslan Bukin {
105085ae89f4SRuslan Bukin struct xae_softc *sc;
1051dba12f75SJustin Hibbits if_t ifp;
105285ae89f4SRuslan Bukin
105385ae89f4SRuslan Bukin sc = device_get_softc(dev);
105485ae89f4SRuslan Bukin
105585ae89f4SRuslan Bukin KASSERT(mtx_initialized(&sc->mtx), ("%s: mutex not initialized",
105685ae89f4SRuslan Bukin device_get_nameunit(dev)));
105785ae89f4SRuslan Bukin
105885ae89f4SRuslan Bukin ifp = sc->ifp;
105985ae89f4SRuslan Bukin
106085ae89f4SRuslan Bukin /* Only cleanup if attach succeeded. */
106185ae89f4SRuslan Bukin if (device_is_attached(dev)) {
106285ae89f4SRuslan Bukin XAE_LOCK(sc);
106385ae89f4SRuslan Bukin xae_stop_locked(sc);
106485ae89f4SRuslan Bukin XAE_UNLOCK(sc);
106585ae89f4SRuslan Bukin callout_drain(&sc->xae_callout);
106685ae89f4SRuslan Bukin ether_ifdetach(ifp);
106785ae89f4SRuslan Bukin }
106885ae89f4SRuslan Bukin
106911a91178SJohn Baldwin bus_generic_detach(dev);
107085ae89f4SRuslan Bukin
107185ae89f4SRuslan Bukin if (ifp != NULL)
107285ae89f4SRuslan Bukin if_free(ifp);
107385ae89f4SRuslan Bukin
107485ae89f4SRuslan Bukin mtx_destroy(&sc->mtx);
107585ae89f4SRuslan Bukin
107685ae89f4SRuslan Bukin bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
107785ae89f4SRuslan Bukin
107885ae89f4SRuslan Bukin bus_release_resources(dev, xae_spec, sc->res);
107985ae89f4SRuslan Bukin
108085ae89f4SRuslan Bukin xdma_channel_free(sc->xchan_tx);
108185ae89f4SRuslan Bukin xdma_channel_free(sc->xchan_rx);
108285ae89f4SRuslan Bukin xdma_put(sc->xdma_tx);
108385ae89f4SRuslan Bukin xdma_put(sc->xdma_rx);
108485ae89f4SRuslan Bukin
108585ae89f4SRuslan Bukin return (0);
108685ae89f4SRuslan Bukin }
108785ae89f4SRuslan Bukin
108885ae89f4SRuslan Bukin static void
xae_miibus_statchg(device_t dev)108985ae89f4SRuslan Bukin xae_miibus_statchg(device_t dev)
109085ae89f4SRuslan Bukin {
109185ae89f4SRuslan Bukin struct xae_softc *sc;
109285ae89f4SRuslan Bukin struct mii_data *mii;
109385ae89f4SRuslan Bukin uint32_t reg;
109485ae89f4SRuslan Bukin
109585ae89f4SRuslan Bukin /*
109685ae89f4SRuslan Bukin * Called by the MII bus driver when the PHY establishes
109785ae89f4SRuslan Bukin * link to set the MAC interface registers.
109885ae89f4SRuslan Bukin */
109985ae89f4SRuslan Bukin
110085ae89f4SRuslan Bukin sc = device_get_softc(dev);
110185ae89f4SRuslan Bukin
110285ae89f4SRuslan Bukin XAE_ASSERT_LOCKED(sc);
110385ae89f4SRuslan Bukin
110485ae89f4SRuslan Bukin mii = sc->mii_softc;
110585ae89f4SRuslan Bukin
110685ae89f4SRuslan Bukin if (mii->mii_media_status & IFM_ACTIVE)
110785ae89f4SRuslan Bukin sc->link_is_up = true;
110885ae89f4SRuslan Bukin else
110985ae89f4SRuslan Bukin sc->link_is_up = false;
111085ae89f4SRuslan Bukin
111185ae89f4SRuslan Bukin switch (IFM_SUBTYPE(mii->mii_media_active)) {
111285ae89f4SRuslan Bukin case IFM_1000_T:
111385ae89f4SRuslan Bukin case IFM_1000_SX:
111485ae89f4SRuslan Bukin reg = SPEED_1000;
111585ae89f4SRuslan Bukin break;
111685ae89f4SRuslan Bukin case IFM_100_TX:
111785ae89f4SRuslan Bukin reg = SPEED_100;
111885ae89f4SRuslan Bukin break;
111985ae89f4SRuslan Bukin case IFM_10_T:
112085ae89f4SRuslan Bukin reg = SPEED_10;
112185ae89f4SRuslan Bukin break;
112285ae89f4SRuslan Bukin case IFM_NONE:
112385ae89f4SRuslan Bukin sc->link_is_up = false;
112485ae89f4SRuslan Bukin return;
112585ae89f4SRuslan Bukin default:
112685ae89f4SRuslan Bukin sc->link_is_up = false;
112785ae89f4SRuslan Bukin device_printf(dev, "Unsupported media %u\n",
112885ae89f4SRuslan Bukin IFM_SUBTYPE(mii->mii_media_active));
112985ae89f4SRuslan Bukin return;
113085ae89f4SRuslan Bukin }
113185ae89f4SRuslan Bukin
113285ae89f4SRuslan Bukin WRITE4(sc, XAE_SPEED, reg);
113385ae89f4SRuslan Bukin }
113485ae89f4SRuslan Bukin
113585ae89f4SRuslan Bukin static device_method_t xae_methods[] = {
113685ae89f4SRuslan Bukin DEVMETHOD(device_probe, xae_probe),
113785ae89f4SRuslan Bukin DEVMETHOD(device_attach, xae_attach),
113885ae89f4SRuslan Bukin DEVMETHOD(device_detach, xae_detach),
113985ae89f4SRuslan Bukin
114085ae89f4SRuslan Bukin /* MII Interface */
114185ae89f4SRuslan Bukin DEVMETHOD(miibus_readreg, xae_miibus_read_reg),
114285ae89f4SRuslan Bukin DEVMETHOD(miibus_writereg, xae_miibus_write_reg),
114385ae89f4SRuslan Bukin DEVMETHOD(miibus_statchg, xae_miibus_statchg),
114485ae89f4SRuslan Bukin { 0, 0 }
114585ae89f4SRuslan Bukin };
114685ae89f4SRuslan Bukin
114785ae89f4SRuslan Bukin driver_t xae_driver = {
114885ae89f4SRuslan Bukin "xae",
114985ae89f4SRuslan Bukin xae_methods,
115085ae89f4SRuslan Bukin sizeof(struct xae_softc),
115185ae89f4SRuslan Bukin };
115285ae89f4SRuslan Bukin
115390b8b224SJohn Baldwin DRIVER_MODULE(xae, simplebus, xae_driver, 0, 0);
11543e38757dSJohn Baldwin DRIVER_MODULE(miibus, xae, miibus_driver, 0, 0);
115585ae89f4SRuslan Bukin
115685ae89f4SRuslan Bukin MODULE_DEPEND(xae, ether, 1, 1, 1);
115785ae89f4SRuslan Bukin MODULE_DEPEND(xae, miibus, 1, 1, 1);
1158