xref: /openbsd-src/sys/arch/octeon/dev/if_cnmac.c (revision a8b581972213782bec450017fe45a05ea431addf)
1*a8b58197Sjsg /*	$OpenBSD: if_cnmac.c,v 1.86 2024/05/20 23:13:33 jsg Exp $	*/
24a04f2fdSsyuu 
3196eba87Syasuoka /*
4196eba87Syasuoka  * Copyright (c) 2007 Internet Initiative Japan, Inc.
5196eba87Syasuoka  * All rights reserved.
6196eba87Syasuoka  *
7196eba87Syasuoka  * Redistribution and use in source and binary forms, with or without
8196eba87Syasuoka  * modification, are permitted provided that the following conditions
9196eba87Syasuoka  * are met:
10196eba87Syasuoka  * 1. Redistributions of source code must retain the above copyright
11196eba87Syasuoka  *    notice, this list of conditions and the following disclaimer.
12196eba87Syasuoka  * 2. Redistributions in binary form must reproduce the above copyright
13196eba87Syasuoka  *    notice, this list of conditions and the following disclaimer in the
14196eba87Syasuoka  *    documentation and/or other materials provided with the distribution.
15196eba87Syasuoka  *
1652334306Syasuoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17196eba87Syasuoka  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18196eba87Syasuoka  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1952334306Syasuoka  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20196eba87Syasuoka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21196eba87Syasuoka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22196eba87Syasuoka  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23196eba87Syasuoka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24196eba87Syasuoka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25196eba87Syasuoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26196eba87Syasuoka  * SUCH DAMAGE.
27196eba87Syasuoka  */
28299029d8Syasuoka #include "bpfilter.h"
294a04f2fdSsyuu 
304a04f2fdSsyuu /*
314a04f2fdSsyuu  * XXXSEIL
324a04f2fdSsyuu  * If no free send buffer is available, free all the sent buffer and bail out.
334a04f2fdSsyuu  */
344a04f2fdSsyuu #define OCTEON_ETH_SEND_QUEUE_CHECK
354a04f2fdSsyuu 
364a04f2fdSsyuu #include <sys/param.h>
374a04f2fdSsyuu #include <sys/systm.h>
384a04f2fdSsyuu #include <sys/pool.h>
394a04f2fdSsyuu #include <sys/proc.h>
404a04f2fdSsyuu #include <sys/mbuf.h>
414a04f2fdSsyuu #include <sys/malloc.h>
424a04f2fdSsyuu #include <sys/kernel.h>
434a04f2fdSsyuu #include <sys/socket.h>
444a04f2fdSsyuu #include <sys/ioctl.h>
454a04f2fdSsyuu #include <sys/errno.h>
464a04f2fdSsyuu #include <sys/device.h>
474a04f2fdSsyuu #include <sys/queue.h>
484a04f2fdSsyuu #include <sys/conf.h>
494a04f2fdSsyuu #include <sys/stdint.h> /* uintptr_t */
504a04f2fdSsyuu #include <sys/syslog.h>
519b18ffb8Sguenther #include <sys/endian.h>
528d999ab0Svisa #include <sys/atomic.h>
534a04f2fdSsyuu 
544a04f2fdSsyuu #include <net/if.h>
554a04f2fdSsyuu #include <net/if_media.h>
564a04f2fdSsyuu #include <netinet/in.h>
574a04f2fdSsyuu #include <netinet/if_ether.h>
584a04f2fdSsyuu 
594a04f2fdSsyuu #if NBPFILTER > 0
604a04f2fdSsyuu #include <net/bpf.h>
614a04f2fdSsyuu #endif
624a04f2fdSsyuu 
634a04f2fdSsyuu #include <machine/bus.h>
644a04f2fdSsyuu #include <machine/intr.h>
654a04f2fdSsyuu #include <machine/octeonvar.h>
664a04f2fdSsyuu #include <machine/octeon_model.h>
674a04f2fdSsyuu 
684a04f2fdSsyuu #include <dev/mii/mii.h>
694a04f2fdSsyuu #include <dev/mii/miivar.h>
704a04f2fdSsyuu 
714a04f2fdSsyuu #include <octeon/dev/cn30xxciureg.h>
724a04f2fdSsyuu #include <octeon/dev/cn30xxnpireg.h>
734a04f2fdSsyuu #include <octeon/dev/cn30xxgmxreg.h>
744a04f2fdSsyuu #include <octeon/dev/cn30xxipdreg.h>
754a04f2fdSsyuu #include <octeon/dev/cn30xxpipreg.h>
764a04f2fdSsyuu #include <octeon/dev/cn30xxpowreg.h>
774a04f2fdSsyuu #include <octeon/dev/cn30xxfaureg.h>
784a04f2fdSsyuu #include <octeon/dev/cn30xxfpareg.h>
794a04f2fdSsyuu #include <octeon/dev/cn30xxbootbusreg.h>
804a04f2fdSsyuu #include <octeon/dev/cn30xxfpavar.h>
814a04f2fdSsyuu #include <octeon/dev/cn30xxgmxvar.h>
824a04f2fdSsyuu #include <octeon/dev/cn30xxfauvar.h>
834a04f2fdSsyuu #include <octeon/dev/cn30xxpowvar.h>
844a04f2fdSsyuu #include <octeon/dev/cn30xxipdvar.h>
854a04f2fdSsyuu #include <octeon/dev/cn30xxpipvar.h>
864a04f2fdSsyuu #include <octeon/dev/cn30xxpkovar.h>
874a04f2fdSsyuu #include <octeon/dev/cn30xxsmivar.h>
884a04f2fdSsyuu #include <octeon/dev/iobusvar.h>
894a04f2fdSsyuu #include <octeon/dev/if_cnmacvar.h>
904a04f2fdSsyuu 
914a04f2fdSsyuu #ifdef OCTEON_ETH_DEBUG
924a04f2fdSsyuu #define	OCTEON_ETH_KASSERT(x)	KASSERT(x)
934a04f2fdSsyuu #define	OCTEON_ETH_KDASSERT(x)	KDASSERT(x)
944a04f2fdSsyuu #else
954a04f2fdSsyuu #define	OCTEON_ETH_KASSERT(x)
964a04f2fdSsyuu #define	OCTEON_ETH_KDASSERT(x)
974a04f2fdSsyuu #endif
984a04f2fdSsyuu 
994a04f2fdSsyuu /*
1004a04f2fdSsyuu  * Set the PKO to think command buffers are an odd length.  This makes it so we
10136fd90dcSjsg  * never have to divide a command across two buffers.
1024a04f2fdSsyuu  */
1034a04f2fdSsyuu #define OCTEON_POOL_NWORDS_CMD	\
1044a04f2fdSsyuu 	    (((uint32_t)OCTEON_POOL_SIZE_CMD / sizeof(uint64_t)) - 1)
1054a04f2fdSsyuu #define FPA_COMMAND_BUFFER_POOL_NWORDS	OCTEON_POOL_NWORDS_CMD	/* XXX */
1064a04f2fdSsyuu 
1078401521dSvisa CTASSERT(MCLBYTES >= OCTEON_POOL_SIZE_PKT + CACHELINESIZE);
1088401521dSvisa 
1091e2bb501Svisa void	cnmac_buf_init(struct cnmac_softc *);
1104a04f2fdSsyuu 
1111e2bb501Svisa int	cnmac_match(struct device *, void *, void *);
1121e2bb501Svisa void	cnmac_attach(struct device *, struct device *, void *);
1131e2bb501Svisa void	cnmac_pip_init(struct cnmac_softc *);
1141e2bb501Svisa void	cnmac_ipd_init(struct cnmac_softc *);
1151e2bb501Svisa void	cnmac_pko_init(struct cnmac_softc *);
1164a04f2fdSsyuu 
1171e2bb501Svisa void	cnmac_board_mac_addr(uint8_t *);
1184a04f2fdSsyuu 
1191e2bb501Svisa int	cnmac_mii_readreg(struct device *, int, int);
1201e2bb501Svisa void	cnmac_mii_writereg(struct device *, int, int, int);
1211e2bb501Svisa void	cnmac_mii_statchg(struct device *);
1224a04f2fdSsyuu 
1231e2bb501Svisa int	cnmac_mediainit(struct cnmac_softc *);
1241e2bb501Svisa void	cnmac_mediastatus(struct ifnet *, struct ifmediareq *);
1251e2bb501Svisa int	cnmac_mediachange(struct ifnet *);
1264a04f2fdSsyuu 
1271e2bb501Svisa void	cnmac_send_queue_flush_prefetch(struct cnmac_softc *);
1281e2bb501Svisa void	cnmac_send_queue_flush_fetch(struct cnmac_softc *);
1291e2bb501Svisa void	cnmac_send_queue_flush(struct cnmac_softc *);
1301e2bb501Svisa int	cnmac_send_queue_is_full(struct cnmac_softc *);
1311e2bb501Svisa void	cnmac_send_queue_add(struct cnmac_softc *,
1324a04f2fdSsyuu 	    struct mbuf *, uint64_t *);
1331e2bb501Svisa void	cnmac_send_queue_del(struct cnmac_softc *,
1344a04f2fdSsyuu 	    struct mbuf **, uint64_t **);
1351e2bb501Svisa int	cnmac_buf_free_work(struct cnmac_softc *, uint64_t *);
1364a04f2fdSsyuu 
1371e2bb501Svisa int	cnmac_ioctl(struct ifnet *, u_long, caddr_t);
1381e2bb501Svisa void	cnmac_watchdog(struct ifnet *);
1391e2bb501Svisa int	cnmac_init(struct ifnet *);
1401e2bb501Svisa int	cnmac_stop(struct ifnet *, int);
1411e2bb501Svisa void	cnmac_start(struct ifqueue *);
1424a04f2fdSsyuu 
1431e2bb501Svisa int	cnmac_send_cmd(struct cnmac_softc *, uint64_t, uint64_t);
1441e2bb501Svisa uint64_t cnmac_send_makecmd_w1(int, paddr_t);
1451e2bb501Svisa uint64_t cnmac_send_makecmd_w0(uint64_t, uint64_t, size_t, int, int);
1461e2bb501Svisa int	cnmac_send_makecmd_gbuf(struct cnmac_softc *,
1474a04f2fdSsyuu 	    struct mbuf *, uint64_t *, int *);
1481e2bb501Svisa int	cnmac_send_makecmd(struct cnmac_softc *,
1494a04f2fdSsyuu 	    struct mbuf *, uint64_t *, uint64_t *, uint64_t *);
1501e2bb501Svisa int	cnmac_send_buf(struct cnmac_softc *,
1514a04f2fdSsyuu 	    struct mbuf *, uint64_t *);
1521e2bb501Svisa int	cnmac_send(struct cnmac_softc *, struct mbuf *);
1534a04f2fdSsyuu 
1541e2bb501Svisa int	cnmac_reset(struct cnmac_softc *);
1551e2bb501Svisa int	cnmac_configure(struct cnmac_softc *);
1561e2bb501Svisa int	cnmac_configure_common(struct cnmac_softc *);
1574a04f2fdSsyuu 
1581e2bb501Svisa void	cnmac_free_task(void *);
1591e2bb501Svisa void	cnmac_tick_free(void *arg);
1601e2bb501Svisa void	cnmac_tick_misc(void *);
1614a04f2fdSsyuu 
1621e2bb501Svisa int	cnmac_recv_mbuf(struct cnmac_softc *,
1638d999ab0Svisa 	    uint64_t *, struct mbuf **, int *);
1641e2bb501Svisa int	cnmac_recv_check(struct cnmac_softc *, uint64_t);
1657e29bba0Svisa int	cnmac_recv(struct cnmac_softc *, uint64_t *, struct mbuf_list *);
1664e19228dSvisa int	cnmac_intr(void *);
1674a04f2fdSsyuu 
1681e2bb501Svisa int	cnmac_mbuf_alloc(int);
1698d999ab0Svisa 
1706ece48ccSvisa #if NKSTAT > 0
1716ece48ccSvisa void	cnmac_kstat_attach(struct cnmac_softc *);
1726ece48ccSvisa int	cnmac_kstat_read(struct kstat *);
1736ece48ccSvisa void	cnmac_kstat_tick(struct cnmac_softc *);
1746ece48ccSvisa #endif
1756ece48ccSvisa 
1762acf2555Svisa /* device parameters */
1771e2bb501Svisa int	cnmac_param_pko_cmd_w0_n2 = 1;
1784a04f2fdSsyuu 
1791e2bb501Svisa const struct cfattach cnmac_ca = {
1801e2bb501Svisa 	sizeof(struct cnmac_softc), cnmac_match, cnmac_attach
1811e2bb501Svisa };
1824a04f2fdSsyuu 
1834a04f2fdSsyuu struct cfdriver cnmac_cd = { NULL, "cnmac", DV_IFNET };
1844a04f2fdSsyuu 
1854a04f2fdSsyuu /* ---- buffer management */
1864a04f2fdSsyuu 
1871e2bb501Svisa const struct cnmac_pool_param {
1884a04f2fdSsyuu 	int			poolno;
1894a04f2fdSsyuu 	size_t			size;
1904a04f2fdSsyuu 	size_t			nelems;
1911e2bb501Svisa } cnmac_pool_params[] = {
1924a04f2fdSsyuu #define	_ENTRY(x)	{ OCTEON_POOL_NO_##x, OCTEON_POOL_SIZE_##x, OCTEON_POOL_NELEMS_##x }
1934a04f2fdSsyuu 	_ENTRY(WQE),
1944a04f2fdSsyuu 	_ENTRY(CMD),
1954a04f2fdSsyuu 	_ENTRY(SG)
1964a04f2fdSsyuu #undef	_ENTRY
1974a04f2fdSsyuu };
1981e2bb501Svisa struct cn30xxfpa_buf	*cnmac_pools[8];
1991e2bb501Svisa #define	cnmac_fb_wqe	cnmac_pools[OCTEON_POOL_NO_WQE]
2001e2bb501Svisa #define	cnmac_fb_cmd	cnmac_pools[OCTEON_POOL_NO_CMD]
2011e2bb501Svisa #define	cnmac_fb_sg	cnmac_pools[OCTEON_POOL_NO_SG]
2024a04f2fdSsyuu 
2031e2bb501Svisa uint64_t cnmac_mac_addr = 0;
2041e2bb501Svisa uint32_t cnmac_mac_addr_offset = 0;
205ba51760cSjasper 
2061e2bb501Svisa int	cnmac_mbufs_to_alloc;
2071e2bb501Svisa int	cnmac_npowgroups = 0;
2084b5ffd6eStedu 
20934a927e5Smiod void
cnmac_buf_init(struct cnmac_softc * sc)2101e2bb501Svisa cnmac_buf_init(struct cnmac_softc *sc)
2114a04f2fdSsyuu {
2124a04f2fdSsyuu 	static int once;
2134a04f2fdSsyuu 	int i;
2141e2bb501Svisa 	const struct cnmac_pool_param *pp;
2154a04f2fdSsyuu 	struct cn30xxfpa_buf *fb;
2164a04f2fdSsyuu 
2174a04f2fdSsyuu 	if (once == 1)
2184a04f2fdSsyuu 		return;
2194a04f2fdSsyuu 	once = 1;
2204a04f2fdSsyuu 
2211e2bb501Svisa 	for (i = 0; i < (int)nitems(cnmac_pool_params); i++) {
2221e2bb501Svisa 		pp = &cnmac_pool_params[i];
2234a04f2fdSsyuu 		cn30xxfpa_buf_init(pp->poolno, pp->size, pp->nelems, &fb);
2241e2bb501Svisa 		cnmac_pools[pp->poolno] = fb;
2254a04f2fdSsyuu 	}
2264a04f2fdSsyuu }
2274a04f2fdSsyuu 
2284a04f2fdSsyuu /* ---- autoconf */
2294a04f2fdSsyuu 
23034a927e5Smiod int
cnmac_match(struct device * parent,void * match,void * aux)2311e2bb501Svisa cnmac_match(struct device *parent, void *match, void *aux)
2324a04f2fdSsyuu {
2334a04f2fdSsyuu 	struct cfdata *cf = (struct cfdata *)match;
2344a04f2fdSsyuu 	struct cn30xxgmx_attach_args *ga = aux;
2354a04f2fdSsyuu 
2364a04f2fdSsyuu 	if (strcmp(cf->cf_driver->cd_name, ga->ga_name) != 0) {
2374a04f2fdSsyuu 		return 0;
2384a04f2fdSsyuu 	}
2394a04f2fdSsyuu 	return 1;
2404a04f2fdSsyuu }
2414a04f2fdSsyuu 
24234a927e5Smiod void
cnmac_attach(struct device * parent,struct device * self,void * aux)2431e2bb501Svisa cnmac_attach(struct device *parent, struct device *self, void *aux)
2444a04f2fdSsyuu {
2451e2bb501Svisa 	struct cnmac_softc *sc = (void *)self;
2464a04f2fdSsyuu 	struct cn30xxgmx_attach_args *ga = aux;
2474a04f2fdSsyuu 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
2484a04f2fdSsyuu 
2491e2bb501Svisa 	if (cnmac_npowgroups >= OCTEON_POW_GROUP_MAX) {
250e842824bSvisa 		printf(": out of POW groups\n");
251e842824bSvisa 		return;
252e842824bSvisa 	}
253e842824bSvisa 
2541e2bb501Svisa 	atomic_add_int(&cnmac_mbufs_to_alloc,
2551e2bb501Svisa 	    cnmac_mbuf_alloc(CNMAC_MBUFS_PER_PORT));
2564b5ffd6eStedu 
2574a04f2fdSsyuu 	sc->sc_regt = ga->ga_regt;
2584a04f2fdSsyuu 	sc->sc_dmat = ga->ga_dmat;
2594a04f2fdSsyuu 	sc->sc_port = ga->ga_portno;
2604a04f2fdSsyuu 	sc->sc_port_type = ga->ga_port_type;
2614a04f2fdSsyuu 	sc->sc_gmx = ga->ga_gmx;
2624a04f2fdSsyuu 	sc->sc_gmx_port = ga->ga_gmx_port;
263ae667e95Svisa 	sc->sc_smi = ga->ga_smi;
2648d5465ddSjmatthew 	sc->sc_phy_addr = ga->ga_phy_addr;
2651e2bb501Svisa 	sc->sc_powgroup = cnmac_npowgroups++;
2664a04f2fdSsyuu 
2674a04f2fdSsyuu 	sc->sc_init_flag = 0;
2684a04f2fdSsyuu 
2694a04f2fdSsyuu 	/*
2704a04f2fdSsyuu 	 * XXX
2714a04f2fdSsyuu 	 * Setting PIP_IP_OFFSET[OFFSET] to 8 causes panic ... why???
2724a04f2fdSsyuu 	 */
2734a04f2fdSsyuu 	sc->sc_ip_offset = 0/* XXX */;
2744a04f2fdSsyuu 
27581e9868dSvisa 	cnmac_board_mac_addr(sc->sc_arpcom.ac_enaddr);
27681e9868dSvisa 	printf(", address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr));
2774a04f2fdSsyuu 
278310b705cSjmatthew 	ml_init(&sc->sc_sendq);
2794a04f2fdSsyuu 	sc->sc_soft_req_thresh = 15/* XXX */;
2804a04f2fdSsyuu 	sc->sc_ext_callback_cnt = 0;
2814a04f2fdSsyuu 
2821e2bb501Svisa 	task_set(&sc->sc_free_task, cnmac_free_task, sc);
2831e2bb501Svisa 	timeout_set(&sc->sc_tick_misc_ch, cnmac_tick_misc, sc);
2841e2bb501Svisa 	timeout_set(&sc->sc_tick_free_ch, cnmac_tick_free, sc);
2854a04f2fdSsyuu 
2864a04f2fdSsyuu 	cn30xxfau_op_init(&sc->sc_fau_done,
287959570ccSvisa 	    OCTEON_CVMSEG_ETHER_OFFSET(sc->sc_dev.dv_unit, csm_ether_fau_done),
288959570ccSvisa 	    OCT_FAU_REG_ADDR_END - (8 * (sc->sc_dev.dv_unit + 1))/* XXX */);
2894a04f2fdSsyuu 	cn30xxfau_op_set_8(&sc->sc_fau_done, 0);
2904a04f2fdSsyuu 
2911e2bb501Svisa 	cnmac_pip_init(sc);
2921e2bb501Svisa 	cnmac_ipd_init(sc);
2931e2bb501Svisa 	cnmac_pko_init(sc);
2944a04f2fdSsyuu 
29521552e0eSvisa 	cnmac_configure_common(sc);
29621552e0eSvisa 
2974a04f2fdSsyuu 	sc->sc_gmx_port->sc_ipd = sc->sc_ipd;
2984a04f2fdSsyuu 	sc->sc_gmx_port->sc_port_mii = &sc->sc_mii;
2994a04f2fdSsyuu 	sc->sc_gmx_port->sc_port_ac = &sc->sc_arpcom;
3004a04f2fdSsyuu 
3014a04f2fdSsyuu 	/* XXX */
3024a04f2fdSsyuu 	sc->sc_pow = &cn30xxpow_softc;
3034a04f2fdSsyuu 
3041e2bb501Svisa 	cnmac_mediainit(sc);
3054a04f2fdSsyuu 
3064a04f2fdSsyuu 	strncpy(ifp->if_xname, sc->sc_dev.dv_xname, sizeof(ifp->if_xname));
3074a04f2fdSsyuu 	ifp->if_softc = sc;
3084a04f2fdSsyuu 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
30940af4038Svisa 	ifp->if_xflags = IFXF_MPSAFE;
3101e2bb501Svisa 	ifp->if_ioctl = cnmac_ioctl;
3111e2bb501Svisa 	ifp->if_qstart = cnmac_start;
3121e2bb501Svisa 	ifp->if_watchdog = cnmac_watchdog;
3131e2bb501Svisa 	ifp->if_hardmtu = CNMAC_MAX_MTU;
314cf96265bSbluhm 	ifq_init_maxlen(&ifp->if_snd, max(GATHER_QUEUE_SIZE, IFQ_MAXLEN));
3154a04f2fdSsyuu 
316db79c34eSvisa 	ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_CSUM_TCPv4 |
317db79c34eSvisa 	    IFCAP_CSUM_UDPv4 | IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6;
3184a04f2fdSsyuu 
3194a04f2fdSsyuu 	cn30xxgmx_set_filter(sc->sc_gmx_port);
3204a04f2fdSsyuu 
3214a04f2fdSsyuu 	if_attach(ifp);
3224a04f2fdSsyuu 	ether_ifattach(ifp);
3234a04f2fdSsyuu 
3241e2bb501Svisa 	cnmac_buf_init(sc);
3254a04f2fdSsyuu 
3266ece48ccSvisa #if NKSTAT > 0
3276ece48ccSvisa 	cnmac_kstat_attach(sc);
3286ece48ccSvisa #endif
3296ece48ccSvisa 
3304e19228dSvisa 	sc->sc_ih = octeon_intr_establish(POW_WORKQ_IRQ(sc->sc_powgroup),
3314e19228dSvisa 	    IPL_NET | IPL_MPSAFE, cnmac_intr, sc, sc->sc_dev.dv_xname);
332e842824bSvisa 	if (sc->sc_ih == NULL)
333e842824bSvisa 		panic("%s: could not set up interrupt", sc->sc_dev.dv_xname);
3344a04f2fdSsyuu }
3354a04f2fdSsyuu 
3364a04f2fdSsyuu /* ---- submodules */
3374a04f2fdSsyuu 
33834a927e5Smiod void
cnmac_pip_init(struct cnmac_softc * sc)3391e2bb501Svisa cnmac_pip_init(struct cnmac_softc *sc)
3404a04f2fdSsyuu {
3414a04f2fdSsyuu 	struct cn30xxpip_attach_args pip_aa;
3424a04f2fdSsyuu 
3434a04f2fdSsyuu 	pip_aa.aa_port = sc->sc_port;
3444a04f2fdSsyuu 	pip_aa.aa_regt = sc->sc_regt;
3454a04f2fdSsyuu 	pip_aa.aa_tag_type = POW_TAG_TYPE_ORDERED/* XXX */;
346e842824bSvisa 	pip_aa.aa_receive_group = sc->sc_powgroup;
3474a04f2fdSsyuu 	pip_aa.aa_ip_offset = sc->sc_ip_offset;
3484a04f2fdSsyuu 	cn30xxpip_init(&pip_aa, &sc->sc_pip);
34921552e0eSvisa 	cn30xxpip_port_config(sc->sc_pip);
3504a04f2fdSsyuu }
3514a04f2fdSsyuu 
35234a927e5Smiod void
cnmac_ipd_init(struct cnmac_softc * sc)3531e2bb501Svisa cnmac_ipd_init(struct cnmac_softc *sc)
3544a04f2fdSsyuu {
3554a04f2fdSsyuu 	struct cn30xxipd_attach_args ipd_aa;
3564a04f2fdSsyuu 
3574a04f2fdSsyuu 	ipd_aa.aa_port = sc->sc_port;
3584a04f2fdSsyuu 	ipd_aa.aa_regt = sc->sc_regt;
3598d999ab0Svisa 	ipd_aa.aa_first_mbuff_skip = 0/* XXX */;
3604a04f2fdSsyuu 	ipd_aa.aa_not_first_mbuff_skip = 0/* XXX */;
3614a04f2fdSsyuu 	cn30xxipd_init(&ipd_aa, &sc->sc_ipd);
3624a04f2fdSsyuu }
3634a04f2fdSsyuu 
36434a927e5Smiod void
cnmac_pko_init(struct cnmac_softc * sc)3651e2bb501Svisa cnmac_pko_init(struct cnmac_softc *sc)
3664a04f2fdSsyuu {
3674a04f2fdSsyuu 	struct cn30xxpko_attach_args pko_aa;
3684a04f2fdSsyuu 
3694a04f2fdSsyuu 	pko_aa.aa_port = sc->sc_port;
3704a04f2fdSsyuu 	pko_aa.aa_regt = sc->sc_regt;
3714a04f2fdSsyuu 	pko_aa.aa_cmdptr = &sc->sc_cmdptr;
3724a04f2fdSsyuu 	pko_aa.aa_cmd_buf_pool = OCTEON_POOL_NO_CMD;
3734a04f2fdSsyuu 	pko_aa.aa_cmd_buf_size = OCTEON_POOL_NWORDS_CMD;
3744a04f2fdSsyuu 	cn30xxpko_init(&pko_aa, &sc->sc_pko);
3754a04f2fdSsyuu }
3764a04f2fdSsyuu 
3774a04f2fdSsyuu /* ---- XXX */
3784a04f2fdSsyuu 
37934a927e5Smiod void
cnmac_board_mac_addr(uint8_t * enaddr)3801e2bb501Svisa cnmac_board_mac_addr(uint8_t *enaddr)
3814a04f2fdSsyuu {
382ba51760cSjasper 	int id;
3834a04f2fdSsyuu 
384ba51760cSjasper 	/* Initialize MAC addresses from the global address base. */
3851e2bb501Svisa 	if (cnmac_mac_addr == 0) {
3861e2bb501Svisa 		memcpy((uint8_t *)&cnmac_mac_addr + 2,
387ba51760cSjasper 		    octeon_boot_info->mac_addr_base, 6);
3884a04f2fdSsyuu 
389ba51760cSjasper 		/*
390ba51760cSjasper 		 * Should be allowed to fail hard if couldn't read the
391ba51760cSjasper 		 * mac_addr_base address...
392ba51760cSjasper 		 */
3931e2bb501Svisa 		if (cnmac_mac_addr == 0)
394ba51760cSjasper 			return;
395ba51760cSjasper 
396ba51760cSjasper 		/*
397ba51760cSjasper 		 * Calculate the offset from the mac_addr_base that will be used
398ba51760cSjasper 		 * for the next sc->sc_port.
399ba51760cSjasper 		 */
400ba51760cSjasper 		id = octeon_get_chipid();
401ba51760cSjasper 
402ba51760cSjasper 		switch (octeon_model_family(id)) {
403ba51760cSjasper 		case OCTEON_MODEL_FAMILY_CN56XX:
4041e2bb501Svisa 			cnmac_mac_addr_offset = 1;
405ba51760cSjasper 			break;
406ba51760cSjasper 		/*
407ba51760cSjasper 		case OCTEON_MODEL_FAMILY_CN52XX:
408ba51760cSjasper 		case OCTEON_MODEL_FAMILY_CN63XX:
4091e2bb501Svisa 			cnmac_mac_addr_offset = 2;
410ba51760cSjasper 			break;
411ba51760cSjasper 		*/
412ba51760cSjasper 		default:
4131e2bb501Svisa 			cnmac_mac_addr_offset = 0;
414ba51760cSjasper 			break;
415ba51760cSjasper 		}
416ba51760cSjasper 
4171e2bb501Svisa 		enaddr += cnmac_mac_addr_offset;
418ba51760cSjasper 	}
419ba51760cSjasper 
420ba51760cSjasper 	/* No more MAC addresses to assign. */
4211e2bb501Svisa 	if (cnmac_mac_addr_offset >= octeon_boot_info->mac_addr_count)
422ba51760cSjasper 		return;
423ba51760cSjasper 
424ba51760cSjasper 	if (enaddr)
4251e2bb501Svisa 		memcpy(enaddr, (uint8_t *)&cnmac_mac_addr + 2, 6);
426ba51760cSjasper 
4271e2bb501Svisa 	cnmac_mac_addr++;
4281e2bb501Svisa 	cnmac_mac_addr_offset++;
4294a04f2fdSsyuu }
4304a04f2fdSsyuu 
4314a04f2fdSsyuu /* ---- media */
4324a04f2fdSsyuu 
43334a927e5Smiod int
cnmac_mii_readreg(struct device * self,int phy_no,int reg)4341e2bb501Svisa cnmac_mii_readreg(struct device *self, int phy_no, int reg)
4354a04f2fdSsyuu {
4361e2bb501Svisa 	struct cnmac_softc *sc = (struct cnmac_softc *)self;
4378d5465ddSjmatthew 	return cn30xxsmi_read(sc->sc_smi, phy_no, reg);
4384a04f2fdSsyuu }
4394a04f2fdSsyuu 
44034a927e5Smiod void
cnmac_mii_writereg(struct device * self,int phy_no,int reg,int value)4411e2bb501Svisa cnmac_mii_writereg(struct device *self, int phy_no, int reg, int value)
4424a04f2fdSsyuu {
4431e2bb501Svisa 	struct cnmac_softc *sc = (struct cnmac_softc *)self;
4448d5465ddSjmatthew 	cn30xxsmi_write(sc->sc_smi, phy_no, reg, value);
4454a04f2fdSsyuu }
4464a04f2fdSsyuu 
44734a927e5Smiod void
cnmac_mii_statchg(struct device * self)4481e2bb501Svisa cnmac_mii_statchg(struct device *self)
4494a04f2fdSsyuu {
4501e2bb501Svisa 	struct cnmac_softc *sc = (struct cnmac_softc *)self;
4514a04f2fdSsyuu 
4524a04f2fdSsyuu 	cn30xxpko_port_enable(sc->sc_pko, 0);
4534a04f2fdSsyuu 	cn30xxgmx_port_enable(sc->sc_gmx_port, 0);
4544a04f2fdSsyuu 
4551e2bb501Svisa 	cnmac_reset(sc);
4564a04f2fdSsyuu 
4574a04f2fdSsyuu 	cn30xxpko_port_enable(sc->sc_pko, 1);
4584a04f2fdSsyuu 	cn30xxgmx_port_enable(sc->sc_gmx_port, 1);
4594a04f2fdSsyuu }
4604a04f2fdSsyuu 
46134a927e5Smiod int
cnmac_mediainit(struct cnmac_softc * sc)4621e2bb501Svisa cnmac_mediainit(struct cnmac_softc *sc)
4634a04f2fdSsyuu {
4644a04f2fdSsyuu 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
465337713feSjasper 	struct mii_softc *child;
4664a04f2fdSsyuu 
4674a04f2fdSsyuu 	sc->sc_mii.mii_ifp = ifp;
4681e2bb501Svisa 	sc->sc_mii.mii_readreg = cnmac_mii_readreg;
4691e2bb501Svisa 	sc->sc_mii.mii_writereg = cnmac_mii_writereg;
4701e2bb501Svisa 	sc->sc_mii.mii_statchg = cnmac_mii_statchg;
4711e2bb501Svisa 	ifmedia_init(&sc->sc_mii.mii_media, 0, cnmac_mediachange,
4721e2bb501Svisa 	    cnmac_mediastatus);
4734a04f2fdSsyuu 
4744a04f2fdSsyuu 	mii_attach(&sc->sc_dev, &sc->sc_mii,
4758d5465ddSjmatthew 	    0xffffffff, sc->sc_phy_addr, MII_OFFSET_ANY, MIIF_DOPAUSE);
4764a04f2fdSsyuu 
477337713feSjasper 	child = LIST_FIRST(&sc->sc_mii.mii_phys);
478337713feSjasper 	if (child == NULL) {
479337713feSjasper                 /* No PHY attached. */
480337713feSjasper 		ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL,
481337713feSjasper 			    0, NULL);
482337713feSjasper 		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL);
4834a04f2fdSsyuu 	} else {
484337713feSjasper 		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
4854a04f2fdSsyuu 	}
4864a04f2fdSsyuu 
4874a04f2fdSsyuu 	return 0;
4884a04f2fdSsyuu }
4894a04f2fdSsyuu 
49034a927e5Smiod void
cnmac_mediastatus(struct ifnet * ifp,struct ifmediareq * ifmr)4911e2bb501Svisa cnmac_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
4924a04f2fdSsyuu {
4931e2bb501Svisa 	struct cnmac_softc *sc = ifp->if_softc;
4944a04f2fdSsyuu 
4954a04f2fdSsyuu 	mii_pollstat(&sc->sc_mii);
4964a04f2fdSsyuu 	ifmr->ifm_status = sc->sc_mii.mii_media_status;
4974a04f2fdSsyuu 	ifmr->ifm_active = sc->sc_mii.mii_media_active;
4984a04f2fdSsyuu 	ifmr->ifm_active = (sc->sc_mii.mii_media_active & ~IFM_ETH_FMASK) |
4994a04f2fdSsyuu 	    sc->sc_gmx_port->sc_port_flowflags;
5004a04f2fdSsyuu }
5014a04f2fdSsyuu 
50234a927e5Smiod int
cnmac_mediachange(struct ifnet * ifp)5031e2bb501Svisa cnmac_mediachange(struct ifnet *ifp)
5044a04f2fdSsyuu {
5051e2bb501Svisa 	struct cnmac_softc *sc = ifp->if_softc;
5064a04f2fdSsyuu 
507337713feSjasper 	if ((ifp->if_flags & IFF_UP) == 0)
5084a04f2fdSsyuu 		return 0;
509337713feSjasper 
510337713feSjasper 	return mii_mediachg(&sc->sc_mii);
5114a04f2fdSsyuu }
5124a04f2fdSsyuu 
5134a04f2fdSsyuu /* ---- send buffer garbage collection */
5144a04f2fdSsyuu 
51534a927e5Smiod void
cnmac_send_queue_flush_prefetch(struct cnmac_softc * sc)5161e2bb501Svisa cnmac_send_queue_flush_prefetch(struct cnmac_softc *sc)
5174a04f2fdSsyuu {
5184a04f2fdSsyuu 	OCTEON_ETH_KASSERT(sc->sc_prefetch == 0);
5194a04f2fdSsyuu 	cn30xxfau_op_inc_fetch_8(&sc->sc_fau_done, 0);
5204a04f2fdSsyuu 	sc->sc_prefetch = 1;
5214a04f2fdSsyuu }
5224a04f2fdSsyuu 
52334a927e5Smiod void
cnmac_send_queue_flush_fetch(struct cnmac_softc * sc)5241e2bb501Svisa cnmac_send_queue_flush_fetch(struct cnmac_softc *sc)
5254a04f2fdSsyuu {
5264a04f2fdSsyuu #ifndef  OCTEON_ETH_DEBUG
5274a04f2fdSsyuu 	if (!sc->sc_prefetch)
5284a04f2fdSsyuu 		return;
5294a04f2fdSsyuu #endif
5304a04f2fdSsyuu 	OCTEON_ETH_KASSERT(sc->sc_prefetch == 1);
5314a04f2fdSsyuu 	sc->sc_hard_done_cnt = cn30xxfau_op_inc_read_8(&sc->sc_fau_done);
5324a04f2fdSsyuu 	OCTEON_ETH_KASSERT(sc->sc_hard_done_cnt <= 0);
5334a04f2fdSsyuu 	sc->sc_prefetch = 0;
5344a04f2fdSsyuu }
5354a04f2fdSsyuu 
53634a927e5Smiod void
cnmac_send_queue_flush(struct cnmac_softc * sc)5371e2bb501Svisa cnmac_send_queue_flush(struct cnmac_softc *sc)
5384a04f2fdSsyuu {
5394a04f2fdSsyuu 	const int64_t sent_count = sc->sc_hard_done_cnt;
5404a04f2fdSsyuu 	int i;
5414a04f2fdSsyuu 
5424a04f2fdSsyuu 	OCTEON_ETH_KASSERT(sent_count <= 0);
5434a04f2fdSsyuu 
5444a04f2fdSsyuu 	for (i = 0; i < 0 - sent_count; i++) {
5454a04f2fdSsyuu 		struct mbuf *m;
5464a04f2fdSsyuu 		uint64_t *gbuf;
5474a04f2fdSsyuu 
5481e2bb501Svisa 		cnmac_send_queue_del(sc, &m, &gbuf);
5494a04f2fdSsyuu 
5501e2bb501Svisa 		cn30xxfpa_buf_put_paddr(cnmac_fb_sg, XKPHYS_TO_PHYS(gbuf));
5514a04f2fdSsyuu 
5524a04f2fdSsyuu 		m_freem(m);
5534a04f2fdSsyuu 	}
5544a04f2fdSsyuu 
555e0081d0cSvisa 	cn30xxfau_op_add_8(&sc->sc_fau_done, i);
5564a04f2fdSsyuu }
5574a04f2fdSsyuu 
55834a927e5Smiod int
cnmac_send_queue_is_full(struct cnmac_softc * sc)5591e2bb501Svisa cnmac_send_queue_is_full(struct cnmac_softc *sc)
5604a04f2fdSsyuu {
5614a04f2fdSsyuu #ifdef OCTEON_ETH_SEND_QUEUE_CHECK
5624a04f2fdSsyuu 	int64_t nofree_cnt;
5634a04f2fdSsyuu 
564e0081d0cSvisa 	nofree_cnt = ml_len(&sc->sc_sendq) + sc->sc_hard_done_cnt;
5654a04f2fdSsyuu 
5664a04f2fdSsyuu 	if (__predict_false(nofree_cnt == GATHER_QUEUE_SIZE - 1)) {
5671e2bb501Svisa 		cnmac_send_queue_flush(sc);
5684a04f2fdSsyuu 		return 1;
5694a04f2fdSsyuu 	}
5704a04f2fdSsyuu 
5714a04f2fdSsyuu #endif
5724a04f2fdSsyuu 	return 0;
5734a04f2fdSsyuu }
5744a04f2fdSsyuu 
57534a927e5Smiod void
cnmac_send_queue_add(struct cnmac_softc * sc,struct mbuf * m,uint64_t * gbuf)5761e2bb501Svisa cnmac_send_queue_add(struct cnmac_softc *sc, struct mbuf *m,
5774a04f2fdSsyuu     uint64_t *gbuf)
5784a04f2fdSsyuu {
5794a04f2fdSsyuu 	OCTEON_ETH_KASSERT(m->m_flags & M_PKTHDR);
5804a04f2fdSsyuu 
581310b705cSjmatthew 	m->m_pkthdr.ph_cookie = gbuf;
582310b705cSjmatthew 	ml_enqueue(&sc->sc_sendq, m);
5834a04f2fdSsyuu 
5844b5ffd6eStedu 	if (m->m_ext.ext_free_fn != 0)
5854a04f2fdSsyuu 		sc->sc_ext_callback_cnt++;
5864a04f2fdSsyuu }
5874a04f2fdSsyuu 
58834a927e5Smiod void
cnmac_send_queue_del(struct cnmac_softc * sc,struct mbuf ** rm,uint64_t ** rgbuf)5891e2bb501Svisa cnmac_send_queue_del(struct cnmac_softc *sc, struct mbuf **rm,
5904a04f2fdSsyuu     uint64_t **rgbuf)
5914a04f2fdSsyuu {
592310b705cSjmatthew 	struct mbuf *m;
593310b705cSjmatthew 	m = ml_dequeue(&sc->sc_sendq);
594310b705cSjmatthew 	OCTEON_ETH_KASSERT(m != NULL);
5954a04f2fdSsyuu 
596310b705cSjmatthew 	*rm = m;
597310b705cSjmatthew 	*rgbuf = m->m_pkthdr.ph_cookie;
5984a04f2fdSsyuu 
5994b5ffd6eStedu 	if (m->m_ext.ext_free_fn != 0) {
6004a04f2fdSsyuu 		sc->sc_ext_callback_cnt--;
6014a04f2fdSsyuu 		OCTEON_ETH_KASSERT(sc->sc_ext_callback_cnt >= 0);
6024a04f2fdSsyuu 	}
6034a04f2fdSsyuu }
6044a04f2fdSsyuu 
60534a927e5Smiod int
cnmac_buf_free_work(struct cnmac_softc * sc,uint64_t * work)6061e2bb501Svisa cnmac_buf_free_work(struct cnmac_softc *sc, uint64_t *work)
6074a04f2fdSsyuu {
6088d999ab0Svisa 	paddr_t addr, pktbuf;
6097e5cda19Svisa 	uint64_t word3;
6107e5cda19Svisa 	unsigned int back, nbufs;
6118d999ab0Svisa 
6127e5cda19Svisa 	nbufs = (work[2] & PIP_WQE_WORD2_IP_BUFS) >>
6137e5cda19Svisa 	    PIP_WQE_WORD2_IP_BUFS_SHIFT;
6147e5cda19Svisa 	word3 = work[3];
6157e5cda19Svisa 	while (nbufs-- > 0) {
6164f54a3e5Svisa 		addr = word3 & PIP_WQE_WORD3_ADDR;
6177e5cda19Svisa 		back = (word3 & PIP_WQE_WORD3_BACK) >>
6188d999ab0Svisa 		    PIP_WQE_WORD3_BACK_SHIFT;
619332a1eb0Svisa 		pktbuf = (addr & ~(CACHELINESIZE - 1)) - back * CACHELINESIZE;
6204a04f2fdSsyuu 
6218d999ab0Svisa 		cn30xxfpa_store(pktbuf, OCTEON_POOL_NO_PKT,
622332a1eb0Svisa 		    OCTEON_POOL_SIZE_PKT / CACHELINESIZE);
6237e5cda19Svisa 
6247e5cda19Svisa 		if (nbufs > 0)
6257e5cda19Svisa 			memcpy(&word3, (void *)PHYS_TO_XKPHYS(addr -
6267e5cda19Svisa 			    sizeof(word3), CCA_CACHED), sizeof(word3));
6274a04f2fdSsyuu 	}
6284a04f2fdSsyuu 
6291e2bb501Svisa 	cn30xxfpa_buf_put_paddr(cnmac_fb_wqe, XKPHYS_TO_PHYS(work));
6304a04f2fdSsyuu 
6314a04f2fdSsyuu 	return 0;
6324a04f2fdSsyuu }
6334a04f2fdSsyuu 
6344a04f2fdSsyuu /* ---- ifnet interfaces */
6354a04f2fdSsyuu 
63634a927e5Smiod int
cnmac_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)6371e2bb501Svisa cnmac_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
6384a04f2fdSsyuu {
6391e2bb501Svisa 	struct cnmac_softc *sc = ifp->if_softc;
6400061a923Syasuoka 	struct ifreq *ifr = (struct ifreq *)data;
6414a04f2fdSsyuu 	int s, error = 0;
6424a04f2fdSsyuu 
6434a04f2fdSsyuu 	s = splnet();
6440061a923Syasuoka 
6454a04f2fdSsyuu 	switch (cmd) {
6464a04f2fdSsyuu 	case SIOCSIFADDR:
6474a04f2fdSsyuu 		ifp->if_flags |= IFF_UP;
6480061a923Syasuoka 		if (!(ifp->if_flags & IFF_RUNNING))
6491e2bb501Svisa 			cnmac_init(ifp);
6504a04f2fdSsyuu 		break;
6514a04f2fdSsyuu 
6524a04f2fdSsyuu 	case SIOCSIFFLAGS:
6534a04f2fdSsyuu 		if (ifp->if_flags & IFF_UP) {
6544a04f2fdSsyuu 			if (ifp->if_flags & IFF_RUNNING)
6554a04f2fdSsyuu 				error = ENETRESET;
6564a04f2fdSsyuu 			else
6571e2bb501Svisa 				cnmac_init(ifp);
6584a04f2fdSsyuu 		} else {
6594a04f2fdSsyuu 			if (ifp->if_flags & IFF_RUNNING)
6601e2bb501Svisa 				cnmac_stop(ifp, 0);
6614a04f2fdSsyuu 		}
6624a04f2fdSsyuu 		break;
6630061a923Syasuoka 
6644a04f2fdSsyuu 	case SIOCSIFMEDIA:
6654a04f2fdSsyuu 		/* Flow control requires full-duplex mode. */
6664a04f2fdSsyuu 		if (IFM_SUBTYPE(ifr->ifr_media) == IFM_AUTO ||
6674a04f2fdSsyuu 		    (ifr->ifr_media & IFM_FDX) == 0) {
6684a04f2fdSsyuu 			ifr->ifr_media &= ~IFM_ETH_FMASK;
6694a04f2fdSsyuu 		}
6704a04f2fdSsyuu 		if (IFM_SUBTYPE(ifr->ifr_media) != IFM_AUTO) {
6714a04f2fdSsyuu 			if ((ifr->ifr_media & IFM_ETH_FMASK) == IFM_FLOW) {
6724a04f2fdSsyuu 				ifr->ifr_media |=
6734a04f2fdSsyuu 				    IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE;
6744a04f2fdSsyuu 			}
6754a04f2fdSsyuu 			sc->sc_gmx_port->sc_port_flowflags =
6764a04f2fdSsyuu 				ifr->ifr_media & IFM_ETH_FMASK;
6774a04f2fdSsyuu 		}
6784a04f2fdSsyuu 		/* FALLTHROUGH */
6794a04f2fdSsyuu 	case SIOCGIFMEDIA:
6804a04f2fdSsyuu 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
6814a04f2fdSsyuu 		break;
6820061a923Syasuoka 
6834a04f2fdSsyuu 	default:
6844a04f2fdSsyuu 		error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data);
6850061a923Syasuoka 	}
6860061a923Syasuoka 
6874a04f2fdSsyuu 	if (error == ENETRESET) {
6884a04f2fdSsyuu 		if (ISSET(ifp->if_flags, IFF_RUNNING))
6894a04f2fdSsyuu 			cn30xxgmx_set_filter(sc->sc_gmx_port);
6904a04f2fdSsyuu 		error = 0;
6914a04f2fdSsyuu 	}
6924a04f2fdSsyuu 
6930061a923Syasuoka 	splx(s);
6944a04f2fdSsyuu 	return (error);
6954a04f2fdSsyuu }
6964a04f2fdSsyuu 
6974a04f2fdSsyuu /* ---- send (output) */
6984a04f2fdSsyuu 
69934a927e5Smiod uint64_t
cnmac_send_makecmd_w0(uint64_t fau0,uint64_t fau1,size_t len,int segs,int ipoffp1)7001e2bb501Svisa cnmac_send_makecmd_w0(uint64_t fau0, uint64_t fau1, size_t len, int segs,
701db79c34eSvisa     int ipoffp1)
7024a04f2fdSsyuu {
7034a04f2fdSsyuu 	return cn30xxpko_cmd_word0(
7044a04f2fdSsyuu 		OCT_FAU_OP_SIZE_64,		/* sz1 */
7054a04f2fdSsyuu 		OCT_FAU_OP_SIZE_64,		/* sz0 */
7064a04f2fdSsyuu 		1, fau1, 1, fau0,		/* s1, reg1, s0, reg0 */
7074a04f2fdSsyuu 		0,				/* le */
7081e2bb501Svisa 		cnmac_param_pko_cmd_w0_n2,	/* n2 */
7094a04f2fdSsyuu 		1, 0,				/* q, r */
7104a04f2fdSsyuu 		(segs == 1) ? 0 : 1,		/* g */
711db79c34eSvisa 		ipoffp1, 0, 1,			/* ipoffp1, ii, df */
7124a04f2fdSsyuu 		segs, (int)len);		/* segs, totalbytes */
7134a04f2fdSsyuu }
7144a04f2fdSsyuu 
71534a927e5Smiod uint64_t
cnmac_send_makecmd_w1(int size,paddr_t addr)7161e2bb501Svisa cnmac_send_makecmd_w1(int size, paddr_t addr)
7174a04f2fdSsyuu {
7184a04f2fdSsyuu 	return cn30xxpko_cmd_word1(
7194a04f2fdSsyuu 		0, 0,				/* i, back */
720a736e532Svisa 		OCTEON_POOL_NO_SG,		/* pool */
7214a04f2fdSsyuu 		size, addr);			/* size, addr */
7224a04f2fdSsyuu }
7234a04f2fdSsyuu 
7241e2bb501Svisa #define KVTOPHYS(addr)	cnmac_kvtophys((vaddr_t)(addr))
7254a04f2fdSsyuu 
726c5f94f81Svisa static inline paddr_t
cnmac_kvtophys(vaddr_t kva)7271e2bb501Svisa cnmac_kvtophys(vaddr_t kva)
7284a04f2fdSsyuu {
729c5f94f81Svisa 	KASSERT(IS_XKPHYS(kva));
7304a04f2fdSsyuu 	return XKPHYS_TO_PHYS(kva);
7314a04f2fdSsyuu }
7324a04f2fdSsyuu 
73334a927e5Smiod int
cnmac_send_makecmd_gbuf(struct cnmac_softc * sc,struct mbuf * m0,uint64_t * gbuf,int * rsegs)7341e2bb501Svisa cnmac_send_makecmd_gbuf(struct cnmac_softc *sc, struct mbuf *m0,
7354a04f2fdSsyuu     uint64_t *gbuf, int *rsegs)
7364a04f2fdSsyuu {
7374a04f2fdSsyuu 	struct mbuf *m;
7384a04f2fdSsyuu 	int segs = 0;
7394a04f2fdSsyuu 
7404a04f2fdSsyuu 	for (m = m0; m != NULL; m = m->m_next) {
7414a04f2fdSsyuu 		if (__predict_false(m->m_len == 0))
7424a04f2fdSsyuu 			continue;
7434a04f2fdSsyuu 
744ddc09c32Svisa 		if (segs >= OCTEON_POOL_SIZE_SG / sizeof(uint64_t))
745ff08e424Svisa 			goto defrag;
7461e2bb501Svisa 		gbuf[segs] = cnmac_send_makecmd_w1(m->m_len,
7474a04f2fdSsyuu 		    KVTOPHYS(m->m_data));
7484a04f2fdSsyuu 		segs++;
7494a04f2fdSsyuu 	}
7504a04f2fdSsyuu 
7514a04f2fdSsyuu 	*rsegs = segs;
7524a04f2fdSsyuu 
7534a04f2fdSsyuu 	return 0;
754ff08e424Svisa 
755ff08e424Svisa defrag:
756ff08e424Svisa 	if (m_defrag(m0, M_DONTWAIT) != 0)
757ff08e424Svisa 		return 1;
7581e2bb501Svisa 	gbuf[0] = cnmac_send_makecmd_w1(m0->m_len, KVTOPHYS(m0->m_data));
759ff08e424Svisa 	*rsegs = 1;
760ff08e424Svisa 	return 0;
7614a04f2fdSsyuu }
7624a04f2fdSsyuu 
76334a927e5Smiod int
cnmac_send_makecmd(struct cnmac_softc * sc,struct mbuf * m,uint64_t * gbuf,uint64_t * rpko_cmd_w0,uint64_t * rpko_cmd_w1)7641e2bb501Svisa cnmac_send_makecmd(struct cnmac_softc *sc, struct mbuf *m,
7654a04f2fdSsyuu     uint64_t *gbuf, uint64_t *rpko_cmd_w0, uint64_t *rpko_cmd_w1)
7664a04f2fdSsyuu {
7674a04f2fdSsyuu 	uint64_t pko_cmd_w0, pko_cmd_w1;
768db79c34eSvisa 	int ipoffp1;
7694a04f2fdSsyuu 	int segs;
7704a04f2fdSsyuu 	int result = 0;
7714a04f2fdSsyuu 
7721e2bb501Svisa 	if (cnmac_send_makecmd_gbuf(sc, m, gbuf, &segs)) {
7730392a0a2Sbcallah 		log(LOG_WARNING, "%s: large number of transmission"
7740392a0a2Sbcallah 		    " data segments", sc->sc_dev.dv_xname);
7754a04f2fdSsyuu 		result = 1;
7764a04f2fdSsyuu 		goto done;
7774a04f2fdSsyuu 	}
7784a04f2fdSsyuu 
779db79c34eSvisa 	/* Get the IP packet offset for TCP/UDP checksum offloading. */
780db79c34eSvisa 	ipoffp1 = (m->m_pkthdr.csum_flags & (M_TCP_CSUM_OUT | M_UDP_CSUM_OUT))
781db79c34eSvisa 	    ? (ETHER_HDR_LEN + 1) : 0;
782db79c34eSvisa 
7834a04f2fdSsyuu 	/*
7844a04f2fdSsyuu 	 * segs == 1	-> link mode (single continuous buffer)
7854a04f2fdSsyuu 	 *		   WORD1[size] is number of bytes pointed by segment
7864a04f2fdSsyuu 	 *
7874a04f2fdSsyuu 	 * segs > 1	-> gather mode (scatter-gather buffer)
7884a04f2fdSsyuu 	 *		   WORD1[size] is number of segments
7894a04f2fdSsyuu 	 */
7901e2bb501Svisa 	pko_cmd_w0 = cnmac_send_makecmd_w0(sc->sc_fau_done.fd_regno,
791db79c34eSvisa 	    0, m->m_pkthdr.len, segs, ipoffp1);
7921e2bb501Svisa 	pko_cmd_w1 = cnmac_send_makecmd_w1(
7934a04f2fdSsyuu 	    (segs == 1) ? m->m_pkthdr.len : segs,
7944a04f2fdSsyuu 	    (segs == 1) ?
7954a04f2fdSsyuu 		KVTOPHYS(m->m_data) :
796706011a1Smiod 		XKPHYS_TO_PHYS(gbuf));
7974a04f2fdSsyuu 
7984a04f2fdSsyuu 	*rpko_cmd_w0 = pko_cmd_w0;
7994a04f2fdSsyuu 	*rpko_cmd_w1 = pko_cmd_w1;
8004a04f2fdSsyuu 
8014a04f2fdSsyuu done:
8024a04f2fdSsyuu 	return result;
8034a04f2fdSsyuu }
8044a04f2fdSsyuu 
80534a927e5Smiod int
cnmac_send_cmd(struct cnmac_softc * sc,uint64_t pko_cmd_w0,uint64_t pko_cmd_w1)8061e2bb501Svisa cnmac_send_cmd(struct cnmac_softc *sc, uint64_t pko_cmd_w0,
8074a04f2fdSsyuu     uint64_t pko_cmd_w1)
8084a04f2fdSsyuu {
8094a04f2fdSsyuu 	uint64_t *cmdptr;
8104a04f2fdSsyuu 	int result = 0;
8114a04f2fdSsyuu 
812706011a1Smiod 	cmdptr = (uint64_t *)PHYS_TO_XKPHYS(sc->sc_cmdptr.cmdptr, CCA_CACHED);
8134a04f2fdSsyuu 	cmdptr += sc->sc_cmdptr.cmdptr_idx;
8144a04f2fdSsyuu 
8154a04f2fdSsyuu 	OCTEON_ETH_KASSERT(cmdptr != NULL);
8164a04f2fdSsyuu 
8174a04f2fdSsyuu 	*cmdptr++ = pko_cmd_w0;
8184a04f2fdSsyuu 	*cmdptr++ = pko_cmd_w1;
8194a04f2fdSsyuu 
8204a04f2fdSsyuu 	OCTEON_ETH_KASSERT(sc->sc_cmdptr.cmdptr_idx + 2 <= FPA_COMMAND_BUFFER_POOL_NWORDS - 1);
8214a04f2fdSsyuu 
8224a04f2fdSsyuu 	if (sc->sc_cmdptr.cmdptr_idx + 2 == FPA_COMMAND_BUFFER_POOL_NWORDS - 1) {
8234a04f2fdSsyuu 		paddr_t buf;
8244a04f2fdSsyuu 
8251e2bb501Svisa 		buf = cn30xxfpa_buf_get_paddr(cnmac_fb_cmd);
8264a04f2fdSsyuu 		if (buf == 0) {
8274a04f2fdSsyuu 			log(LOG_WARNING,
8284a04f2fdSsyuu 			    "%s: cannot allocate command buffer from free pool allocator\n",
8294a04f2fdSsyuu 			    sc->sc_dev.dv_xname);
8304a04f2fdSsyuu 			result = 1;
8314a04f2fdSsyuu 			goto done;
8324a04f2fdSsyuu 		}
8334a04f2fdSsyuu 		*cmdptr++ = buf;
8344a04f2fdSsyuu 		sc->sc_cmdptr.cmdptr = (uint64_t)buf;
8354a04f2fdSsyuu 		sc->sc_cmdptr.cmdptr_idx = 0;
8364a04f2fdSsyuu 	} else {
8374a04f2fdSsyuu 		sc->sc_cmdptr.cmdptr_idx += 2;
8384a04f2fdSsyuu 	}
8394a04f2fdSsyuu 
8404a04f2fdSsyuu 	cn30xxpko_op_doorbell_write(sc->sc_port, sc->sc_port, 2);
8414a04f2fdSsyuu 
8424a04f2fdSsyuu done:
8434a04f2fdSsyuu 	return result;
8444a04f2fdSsyuu }
8454a04f2fdSsyuu 
84634a927e5Smiod int
cnmac_send_buf(struct cnmac_softc * sc,struct mbuf * m,uint64_t * gbuf)8471e2bb501Svisa cnmac_send_buf(struct cnmac_softc *sc, struct mbuf *m, uint64_t *gbuf)
8484a04f2fdSsyuu {
8494a04f2fdSsyuu 	int result = 0, error;
8504a04f2fdSsyuu 	uint64_t pko_cmd_w0, pko_cmd_w1;
8514a04f2fdSsyuu 
8521e2bb501Svisa 	error = cnmac_send_makecmd(sc, m, gbuf, &pko_cmd_w0, &pko_cmd_w1);
8534a04f2fdSsyuu 	if (error != 0) {
8544a04f2fdSsyuu 		/* already logging */
8554a04f2fdSsyuu 		result = error;
8564a04f2fdSsyuu 		goto done;
8574a04f2fdSsyuu 	}
8584a04f2fdSsyuu 
8591e2bb501Svisa 	error = cnmac_send_cmd(sc, pko_cmd_w0, pko_cmd_w1);
8604a04f2fdSsyuu 	if (error != 0) {
8614a04f2fdSsyuu 		/* already logging */
8624a04f2fdSsyuu 		result = error;
8634a04f2fdSsyuu 	}
8644a04f2fdSsyuu 
8654a04f2fdSsyuu done:
8664a04f2fdSsyuu 	return result;
8674a04f2fdSsyuu }
8684a04f2fdSsyuu 
86934a927e5Smiod int
cnmac_send(struct cnmac_softc * sc,struct mbuf * m)8701e2bb501Svisa cnmac_send(struct cnmac_softc *sc, struct mbuf *m)
8714a04f2fdSsyuu {
8724a04f2fdSsyuu 	paddr_t gaddr = 0;
8734a04f2fdSsyuu 	uint64_t *gbuf = NULL;
8744a04f2fdSsyuu 	int result = 0, error;
8754a04f2fdSsyuu 
8761e2bb501Svisa 	gaddr = cn30xxfpa_buf_get_paddr(cnmac_fb_sg);
8774a04f2fdSsyuu 	if (gaddr == 0) {
8784a04f2fdSsyuu 		log(LOG_WARNING,
8794a04f2fdSsyuu 		    "%s: cannot allocate gather buffer from free pool allocator\n",
8804a04f2fdSsyuu 		    sc->sc_dev.dv_xname);
8814a04f2fdSsyuu 		result = 1;
8824a04f2fdSsyuu 		goto done;
8834a04f2fdSsyuu 	}
8844a04f2fdSsyuu 
885706011a1Smiod 	gbuf = (uint64_t *)(uintptr_t)PHYS_TO_XKPHYS(gaddr, CCA_CACHED);
8864a04f2fdSsyuu 
8871e2bb501Svisa 	error = cnmac_send_buf(sc, m, gbuf);
8884a04f2fdSsyuu 	if (error != 0) {
8894a04f2fdSsyuu 		/* already logging */
8901e2bb501Svisa 		cn30xxfpa_buf_put_paddr(cnmac_fb_sg, gaddr);
8914a04f2fdSsyuu 		result = error;
8924a04f2fdSsyuu 		goto done;
8934a04f2fdSsyuu 	}
8944a04f2fdSsyuu 
8951e2bb501Svisa 	cnmac_send_queue_add(sc, m, gbuf);
8964a04f2fdSsyuu 
8974a04f2fdSsyuu done:
8984a04f2fdSsyuu 	return result;
8994a04f2fdSsyuu }
9004a04f2fdSsyuu 
90134a927e5Smiod void
cnmac_start(struct ifqueue * ifq)9021e2bb501Svisa cnmac_start(struct ifqueue *ifq)
9034a04f2fdSsyuu {
904722eb25eSdlg 	struct ifnet *ifp = ifq->ifq_if;
9051e2bb501Svisa 	struct cnmac_softc *sc = ifp->if_softc;
9064a04f2fdSsyuu 	struct mbuf *m;
9074a04f2fdSsyuu 
90840af4038Svisa 	if (__predict_false(!cn30xxgmx_link_status(sc->sc_gmx_port))) {
909722eb25eSdlg 		ifq_purge(ifq);
91040af4038Svisa 		return;
91140af4038Svisa 	}
91240af4038Svisa 
9134a04f2fdSsyuu 	/*
9144a04f2fdSsyuu 	 * performance tuning
9154a04f2fdSsyuu 	 * presend iobdma request
9164a04f2fdSsyuu 	 */
9171e2bb501Svisa 	cnmac_send_queue_flush_prefetch(sc);
9184a04f2fdSsyuu 
9194a04f2fdSsyuu 	for (;;) {
9201e2bb501Svisa 		cnmac_send_queue_flush_fetch(sc); /* XXX */
9214a04f2fdSsyuu 
9224a04f2fdSsyuu 		/*
9234a04f2fdSsyuu 		 * XXXSEIL
9244a04f2fdSsyuu 		 * If no free send buffer is available, free all the sent buffer
9254a04f2fdSsyuu 		 * and bail out.
9264a04f2fdSsyuu 		 */
9271e2bb501Svisa 		if (cnmac_send_queue_is_full(sc)) {
928722eb25eSdlg 			ifq_set_oactive(ifq);
92940af4038Svisa 			timeout_add(&sc->sc_tick_free_ch, 1);
9304a04f2fdSsyuu 			return;
9314a04f2fdSsyuu 		}
9324a04f2fdSsyuu 
933722eb25eSdlg 		m = ifq_dequeue(ifq);
934817239d7Svisa 		if (m == NULL)
935817239d7Svisa 			return;
9364a04f2fdSsyuu 
937c6c148e3Svisa #if NBPFILTER > 0
938c6c148e3Svisa 		if (ifp->if_bpf != NULL)
939c6c148e3Svisa 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
940c6c148e3Svisa #endif
9414a04f2fdSsyuu 
9424a04f2fdSsyuu 		/* XXX */
943e0081d0cSvisa 		if (ml_len(&sc->sc_sendq) > sc->sc_soft_req_thresh)
9441e2bb501Svisa 			cnmac_send_queue_flush(sc);
9451e2bb501Svisa 		if (cnmac_send(sc, m)) {
9464a04f2fdSsyuu 			ifp->if_oerrors++;
9474a04f2fdSsyuu 			m_freem(m);
9484a04f2fdSsyuu 			log(LOG_WARNING,
9490392a0a2Sbcallah 		  	  "%s: failed to transmit packet\n",
9504a04f2fdSsyuu 		    	  sc->sc_dev.dv_xname);
9514a04f2fdSsyuu 		}
9524a04f2fdSsyuu 		/* XXX */
9534a04f2fdSsyuu 
9544a04f2fdSsyuu 		/*
9554a04f2fdSsyuu 		 * send next iobdma request
9564a04f2fdSsyuu 		 */
9571e2bb501Svisa 		cnmac_send_queue_flush_prefetch(sc);
9584a04f2fdSsyuu 	}
9594a04f2fdSsyuu 
9601e2bb501Svisa 	cnmac_send_queue_flush_fetch(sc);
9614a04f2fdSsyuu }
9624a04f2fdSsyuu 
96334a927e5Smiod void
cnmac_watchdog(struct ifnet * ifp)9641e2bb501Svisa cnmac_watchdog(struct ifnet *ifp)
9654a04f2fdSsyuu {
9661e2bb501Svisa 	struct cnmac_softc *sc = ifp->if_softc;
9674a04f2fdSsyuu 
9684a04f2fdSsyuu 	printf("%s: device timeout\n", sc->sc_dev.dv_xname);
9694a04f2fdSsyuu 
9701e2bb501Svisa 	cnmac_stop(ifp, 0);
97140af4038Svisa 
9721e2bb501Svisa 	cnmac_configure(sc);
9734a04f2fdSsyuu 
9744a04f2fdSsyuu 	SET(ifp->if_flags, IFF_RUNNING);
9754a04f2fdSsyuu 	ifp->if_timer = 0;
9764a04f2fdSsyuu 
97740af4038Svisa 	ifq_restart(&ifp->if_snd);
9784a04f2fdSsyuu }
9794a04f2fdSsyuu 
98034a927e5Smiod int
cnmac_init(struct ifnet * ifp)9811e2bb501Svisa cnmac_init(struct ifnet *ifp)
9824a04f2fdSsyuu {
9831e2bb501Svisa 	struct cnmac_softc *sc = ifp->if_softc;
9844a04f2fdSsyuu 
9854a04f2fdSsyuu 	/* XXX don't disable commonly used parts!!! XXX */
9864a04f2fdSsyuu 	if (sc->sc_init_flag == 0) {
9874a04f2fdSsyuu 		/* Cancel any pending I/O. */
9881e2bb501Svisa 		cnmac_stop(ifp, 0);
9894a04f2fdSsyuu 
9904a04f2fdSsyuu 		/* Initialize the device */
9911e2bb501Svisa 		cnmac_configure(sc);
9924a04f2fdSsyuu 
9934a04f2fdSsyuu 		cn30xxpko_enable(sc->sc_pko);
9944a04f2fdSsyuu 		cn30xxipd_enable(sc->sc_ipd);
9954a04f2fdSsyuu 
9964a04f2fdSsyuu 		sc->sc_init_flag = 1;
9974a04f2fdSsyuu 	} else {
9984a04f2fdSsyuu 		cn30xxgmx_port_enable(sc->sc_gmx_port, 1);
9994a04f2fdSsyuu 	}
10001e2bb501Svisa 	cnmac_mediachange(ifp);
10014a04f2fdSsyuu 
10026ece48ccSvisa 	cn30xxpip_stats_init(sc->sc_pip);
10036ece48ccSvisa 	cn30xxgmx_stats_init(sc->sc_gmx_port);
10044a04f2fdSsyuu 	cn30xxgmx_set_filter(sc->sc_gmx_port);
10054a04f2fdSsyuu 
10064a04f2fdSsyuu 	timeout_add_sec(&sc->sc_tick_misc_ch, 1);
10074a04f2fdSsyuu 	timeout_add_sec(&sc->sc_tick_free_ch, 1);
10084a04f2fdSsyuu 
10094a04f2fdSsyuu 	SET(ifp->if_flags, IFF_RUNNING);
1010de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
10114a04f2fdSsyuu 
10124a04f2fdSsyuu 	return 0;
10134a04f2fdSsyuu }
10144a04f2fdSsyuu 
101534a927e5Smiod int
cnmac_stop(struct ifnet * ifp,int disable)10161e2bb501Svisa cnmac_stop(struct ifnet *ifp, int disable)
10174a04f2fdSsyuu {
10181e2bb501Svisa 	struct cnmac_softc *sc = ifp->if_softc;
10194a04f2fdSsyuu 
102040af4038Svisa 	CLR(ifp->if_flags, IFF_RUNNING);
102140af4038Svisa 
10224a04f2fdSsyuu 	timeout_del(&sc->sc_tick_misc_ch);
10234a04f2fdSsyuu 	timeout_del(&sc->sc_tick_free_ch);
10244a04f2fdSsyuu 
10254a04f2fdSsyuu 	mii_down(&sc->sc_mii);
10264a04f2fdSsyuu 
10274a04f2fdSsyuu 	cn30xxgmx_port_enable(sc->sc_gmx_port, 0);
10284a04f2fdSsyuu 
1029e842824bSvisa 	intr_barrier(sc->sc_ih);
103040af4038Svisa 	ifq_barrier(&ifp->if_snd);
103140af4038Svisa 
1032de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
10334a04f2fdSsyuu 	ifp->if_timer = 0;
10344a04f2fdSsyuu 
10354a04f2fdSsyuu 	return 0;
10364a04f2fdSsyuu }
10374a04f2fdSsyuu 
10384a04f2fdSsyuu /* ---- misc */
10394a04f2fdSsyuu 
10404a04f2fdSsyuu #define PKO_INDEX_MASK	((1ULL << 12/* XXX */) - 1)
10414a04f2fdSsyuu 
104234a927e5Smiod int
cnmac_reset(struct cnmac_softc * sc)10431e2bb501Svisa cnmac_reset(struct cnmac_softc *sc)
10444a04f2fdSsyuu {
10454a04f2fdSsyuu 	cn30xxgmx_reset_speed(sc->sc_gmx_port);
10464a04f2fdSsyuu 	cn30xxgmx_reset_flowctl(sc->sc_gmx_port);
10474a04f2fdSsyuu 	cn30xxgmx_reset_timing(sc->sc_gmx_port);
10484a04f2fdSsyuu 
10494a04f2fdSsyuu 	return 0;
10504a04f2fdSsyuu }
10514a04f2fdSsyuu 
105234a927e5Smiod int
cnmac_configure(struct cnmac_softc * sc)10531e2bb501Svisa cnmac_configure(struct cnmac_softc *sc)
10544a04f2fdSsyuu {
10554a04f2fdSsyuu 	cn30xxgmx_port_enable(sc->sc_gmx_port, 0);
10564a04f2fdSsyuu 
10571e2bb501Svisa 	cnmac_reset(sc);
10584a04f2fdSsyuu 
10594a04f2fdSsyuu 	cn30xxpko_port_config(sc->sc_pko);
10604a04f2fdSsyuu 	cn30xxpko_port_enable(sc->sc_pko, 1);
1061e842824bSvisa 	cn30xxpow_config(sc->sc_pow, sc->sc_powgroup);
10624a04f2fdSsyuu 
10634a04f2fdSsyuu 	cn30xxgmx_port_enable(sc->sc_gmx_port, 1);
10644a04f2fdSsyuu 
10654a04f2fdSsyuu 	return 0;
10664a04f2fdSsyuu }
10674a04f2fdSsyuu 
106834a927e5Smiod int
cnmac_configure_common(struct cnmac_softc * sc)10691e2bb501Svisa cnmac_configure_common(struct cnmac_softc *sc)
10704a04f2fdSsyuu {
10714a04f2fdSsyuu 	static int once;
10724a04f2fdSsyuu 
107337c8ef21Svisa 	uint64_t reg;
107437c8ef21Svisa 
10754a04f2fdSsyuu 	if (once == 1)
10764a04f2fdSsyuu 		return 0;
10774a04f2fdSsyuu 	once = 1;
10784a04f2fdSsyuu 
10794a04f2fdSsyuu 	cn30xxipd_config(sc->sc_ipd);
10804a04f2fdSsyuu 	cn30xxpko_config(sc->sc_pko);
10814a04f2fdSsyuu 
108237c8ef21Svisa 	/* Set padding for packets that Octeon does not recognize as IP. */
108337c8ef21Svisa 	reg = octeon_xkphys_read_8(PIP_GBL_CFG);
108437c8ef21Svisa 	reg &= ~PIP_GBL_CFG_NIP_SHF_MASK;
108537c8ef21Svisa 	reg |= ETHER_ALIGN << PIP_GBL_CFG_NIP_SHF_SHIFT;
108637c8ef21Svisa 	octeon_xkphys_write_8(PIP_GBL_CFG, reg);
108737c8ef21Svisa 
10884a04f2fdSsyuu 	return 0;
10894a04f2fdSsyuu }
10904a04f2fdSsyuu 
109134a927e5Smiod int
cnmac_mbuf_alloc(int n)10921e2bb501Svisa cnmac_mbuf_alloc(int n)
10934a04f2fdSsyuu {
10944a04f2fdSsyuu 	struct mbuf *m;
10958d999ab0Svisa 	paddr_t pktbuf;
10968d999ab0Svisa 
10978d999ab0Svisa 	while (n > 0) {
1098471f2571Sjan 		m = MCLGETL(NULL, M_NOWAIT,
1099332a1eb0Svisa 		    OCTEON_POOL_SIZE_PKT + CACHELINESIZE);
11008d999ab0Svisa 		if (m == NULL || !ISSET(m->m_flags, M_EXT)) {
11018d999ab0Svisa 			m_freem(m);
11028d999ab0Svisa 			break;
11038d999ab0Svisa 		}
11048d999ab0Svisa 
1105332a1eb0Svisa 		m->m_data = (void *)(((vaddr_t)m->m_data + CACHELINESIZE) &
1106332a1eb0Svisa 		    ~(CACHELINESIZE - 1));
11078d999ab0Svisa 		((struct mbuf **)m->m_data)[-1] = m;
11088d999ab0Svisa 
11098d999ab0Svisa 		pktbuf = KVTOPHYS(m->m_data);
11108d999ab0Svisa 		m->m_pkthdr.ph_cookie = (void *)pktbuf;
11118d999ab0Svisa 		cn30xxfpa_store(pktbuf, OCTEON_POOL_NO_PKT,
1112332a1eb0Svisa 		    OCTEON_POOL_SIZE_PKT / CACHELINESIZE);
11138d999ab0Svisa 
11148d999ab0Svisa 		n--;
11158d999ab0Svisa 	}
11168d999ab0Svisa 	return n;
11178d999ab0Svisa }
11188d999ab0Svisa 
11198d999ab0Svisa int
cnmac_recv_mbuf(struct cnmac_softc * sc,uint64_t * work,struct mbuf ** rm,int * nmbuf)11201e2bb501Svisa cnmac_recv_mbuf(struct cnmac_softc *sc, uint64_t *work,
11218d999ab0Svisa     struct mbuf **rm, int *nmbuf)
11228d999ab0Svisa {
11237e5cda19Svisa 	struct mbuf *m, *m0, *mprev, **pm;
11248d999ab0Svisa 	paddr_t addr, pktbuf;
11254a04f2fdSsyuu 	uint64_t word1 = work[1];
11264a04f2fdSsyuu 	uint64_t word2 = work[2];
11274a04f2fdSsyuu 	uint64_t word3 = work[3];
11287e5cda19Svisa 	unsigned int back, i, nbufs;
11297e5cda19Svisa 	unsigned int left, total, size;
11304a04f2fdSsyuu 
11311e2bb501Svisa 	cn30xxfpa_buf_put_paddr(cnmac_fb_wqe, XKPHYS_TO_PHYS(work));
1132b24e8039Svisa 
11337e5cda19Svisa 	nbufs = (word2 & PIP_WQE_WORD2_IP_BUFS) >> PIP_WQE_WORD2_IP_BUFS_SHIFT;
11347e5cda19Svisa 	if (nbufs == 0)
11357e5cda19Svisa 		panic("%s: dynamic short packet", __func__);
11364a04f2fdSsyuu 
11377e5cda19Svisa 	m0 = mprev = NULL;
11387e5cda19Svisa 	total = left = (word1 & PIP_WQE_WORD1_LEN) >> 48;
11397e5cda19Svisa 	for (i = 0; i < nbufs; i++) {
11408d999ab0Svisa 		addr = word3 & PIP_WQE_WORD3_ADDR;
11418d999ab0Svisa 		back = (word3 & PIP_WQE_WORD3_BACK) >> PIP_WQE_WORD3_BACK_SHIFT;
1142332a1eb0Svisa 		pktbuf = (addr & ~(CACHELINESIZE - 1)) - back * CACHELINESIZE;
11438d999ab0Svisa 		pm = (struct mbuf **)PHYS_TO_XKPHYS(pktbuf, CCA_CACHED) - 1;
11448d999ab0Svisa 		m = *pm;
11458d999ab0Svisa 		*pm = NULL;
11468d999ab0Svisa 		if ((paddr_t)m->m_pkthdr.ph_cookie != pktbuf)
11478d999ab0Svisa 			panic("%s: packet pool is corrupted, mbuf cookie %p != "
11488d999ab0Svisa 			    "pktbuf %p", __func__, m->m_pkthdr.ph_cookie,
11498d999ab0Svisa 			    (void *)pktbuf);
11504a04f2fdSsyuu 
11517e5cda19Svisa 		/*
11527e5cda19Svisa 		 * Because of a hardware bug in some Octeon models the size
11531ffbd945Svisa 		 * field of word3 can be wrong (erratum PKI-100).
11541ffbd945Svisa 		 * However, the hardware uses all space in a buffer before
11551ffbd945Svisa 		 * moving to the next one so it is possible to derive
11561ffbd945Svisa 		 * the size of this data segment from the size
11571ffbd945Svisa 		 * of packet data buffers.
11587e5cda19Svisa 		 */
11597e5cda19Svisa 		size = OCTEON_POOL_SIZE_PKT - (addr - pktbuf);
11607e5cda19Svisa 		if (size > left)
11617e5cda19Svisa 			size = left;
11627e5cda19Svisa 
11638d999ab0Svisa 		m->m_pkthdr.ph_cookie = NULL;
11648d999ab0Svisa 		m->m_data += addr - pktbuf;
11657e5cda19Svisa 		m->m_len = size;
11667e5cda19Svisa 		left -= size;
11674a04f2fdSsyuu 
11687e5cda19Svisa 		if (m0 == NULL)
11697e5cda19Svisa 			m0 = m;
11707e5cda19Svisa 		else {
11717e5cda19Svisa 			m->m_flags &= ~M_PKTHDR;
11727e5cda19Svisa 			mprev->m_next = m;
11737e5cda19Svisa 		}
11747e5cda19Svisa 		mprev = m;
11757e5cda19Svisa 
11767e5cda19Svisa 		if (i + 1 < nbufs)
11777e5cda19Svisa 			memcpy(&word3, (void *)PHYS_TO_XKPHYS(addr -
11787e5cda19Svisa 			    sizeof(word3), CCA_CACHED), sizeof(word3));
11797e5cda19Svisa 	}
11807e5cda19Svisa 
11817e5cda19Svisa 	m0->m_pkthdr.len = total;
11827e5cda19Svisa 	*rm = m0;
11837e5cda19Svisa 	*nmbuf = nbufs;
11844a04f2fdSsyuu 
11854a04f2fdSsyuu 	return 0;
11864a04f2fdSsyuu }
11874a04f2fdSsyuu 
118834a927e5Smiod int
cnmac_recv_check(struct cnmac_softc * sc,uint64_t word2)11891e2bb501Svisa cnmac_recv_check(struct cnmac_softc *sc, uint64_t word2)
11904a04f2fdSsyuu {
11919253bf3fSvisa 	static struct timeval rxerr_log_interval = { 0, 250000 };
11929253bf3fSvisa 	uint64_t opecode;
11934a04f2fdSsyuu 
11944a04f2fdSsyuu 	if (__predict_true(!ISSET(word2, PIP_WQE_WORD2_NOIP_RE)))
11954a04f2fdSsyuu 		return 0;
11964a04f2fdSsyuu 
11979253bf3fSvisa 	opecode = word2 & PIP_WQE_WORD2_NOIP_OPECODE;
11989253bf3fSvisa 	if ((sc->sc_arpcom.ac_if.if_flags & IFF_DEBUG) &&
11999253bf3fSvisa 	    ratecheck(&sc->sc_rxerr_log_last, &rxerr_log_interval))
12009253bf3fSvisa 		log(LOG_DEBUG, "%s: rx error (%lld)\n", sc->sc_dev.dv_xname,
12019253bf3fSvisa 		    opecode);
12029253bf3fSvisa 
12039253bf3fSvisa 	/* XXX harmless error? */
12049253bf3fSvisa 	if (opecode == PIP_WQE_WORD2_RE_OPCODE_OVRRUN)
12054a04f2fdSsyuu 		return 0;
12064a04f2fdSsyuu 
12074a04f2fdSsyuu 	return 1;
12084a04f2fdSsyuu }
12094a04f2fdSsyuu 
121034a927e5Smiod int
cnmac_recv(struct cnmac_softc * sc,uint64_t * work,struct mbuf_list * ml)12117e29bba0Svisa cnmac_recv(struct cnmac_softc *sc, uint64_t *work, struct mbuf_list *ml)
12124a04f2fdSsyuu {
12137e29bba0Svisa 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
12144a04f2fdSsyuu 	struct mbuf *m;
12154a04f2fdSsyuu 	uint64_t word2;
12167e29bba0Svisa 	int nmbuf = 0;
12174a04f2fdSsyuu 
12184a04f2fdSsyuu 	word2 = work[2];
12194a04f2fdSsyuu 
12202588bfb3Svisa 	if (!(ifp->if_flags & IFF_RUNNING))
12212588bfb3Svisa 		goto drop;
12222588bfb3Svisa 
12231e2bb501Svisa 	if (__predict_false(cnmac_recv_check(sc, word2) != 0)) {
12244a04f2fdSsyuu 		ifp->if_ierrors++;
12254a04f2fdSsyuu 		goto drop;
12264a04f2fdSsyuu 	}
12274a04f2fdSsyuu 
12287e29bba0Svisa 	/* On success, this releases the work queue entry. */
12291e2bb501Svisa 	if (__predict_false(cnmac_recv_mbuf(sc, work, &m, &nmbuf) != 0)) {
12304a04f2fdSsyuu 		ifp->if_ierrors++;
12314a04f2fdSsyuu 		goto drop;
12324a04f2fdSsyuu 	}
12334a04f2fdSsyuu 
1234b9271645Svisa 	m->m_pkthdr.csum_flags = 0;
1235b9271645Svisa 	if (__predict_true(!ISSET(word2, PIP_WQE_WORD2_IP_NI))) {
1236b9271645Svisa 		/* Check IP checksum status. */
1237b9271645Svisa 		if (!ISSET(word2, PIP_WQE_WORD2_IP_V6) &&
1238b9271645Svisa 		    !ISSET(word2, PIP_WQE_WORD2_IP_IE))
1239b9271645Svisa 			m->m_pkthdr.csum_flags |= M_IPV4_CSUM_IN_OK;
1240b9271645Svisa 
1241b9271645Svisa 		/* Check TCP/UDP checksum status. */
1242b9271645Svisa 		if (ISSET(word2, PIP_WQE_WORD2_IP_TU) &&
1243b9271645Svisa 		    !ISSET(word2, PIP_WQE_WORD2_IP_FR) &&
1244b9271645Svisa 		    !ISSET(word2, PIP_WQE_WORD2_IP_LE))
1245b9271645Svisa 			m->m_pkthdr.csum_flags |=
1246b9271645Svisa 			    M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK;
1247b9271645Svisa 	}
12484a04f2fdSsyuu 
12497e29bba0Svisa 	ml_enqueue(ml, m);
12504a04f2fdSsyuu 
12517e29bba0Svisa 	return nmbuf;
12524a04f2fdSsyuu 
12534a04f2fdSsyuu drop:
12541e2bb501Svisa 	cnmac_buf_free_work(sc, work);
12557e29bba0Svisa 	return 0;
12564a04f2fdSsyuu }
12574a04f2fdSsyuu 
12584e19228dSvisa int
cnmac_intr(void * arg)12594e19228dSvisa cnmac_intr(void *arg)
12604a04f2fdSsyuu {
12617e29bba0Svisa 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
12624e19228dSvisa 	struct cnmac_softc *sc = arg;
12637e29bba0Svisa 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
12644e19228dSvisa 	uint64_t *work;
12654e19228dSvisa 	uint64_t wqmask = 1ull << sc->sc_powgroup;
12664e19228dSvisa 	uint32_t coreid = octeon_get_coreid();
1267c1130c1fSvisa 	uint32_t port;
12687e29bba0Svisa 	int nmbuf = 0;
12694a04f2fdSsyuu 
12704e19228dSvisa 	_POW_WR8(sc->sc_pow, POW_PP_GRP_MSK_OFFSET(coreid), wqmask);
12714e19228dSvisa 
12724e19228dSvisa 	cn30xxpow_tag_sw_wait();
12734e19228dSvisa 	cn30xxpow_work_request_async(OCTEON_CVMSEG_OFFSET(csm_pow_intr),
12744e19228dSvisa 	    POW_NO_WAIT);
12754e19228dSvisa 
12764e19228dSvisa 	for (;;) {
12774e19228dSvisa 		work = (uint64_t *)cn30xxpow_work_response_async(
12784e19228dSvisa 		    OCTEON_CVMSEG_OFFSET(csm_pow_intr));
12794e19228dSvisa 		if (work == NULL)
12804e19228dSvisa 			break;
12814e19228dSvisa 
12824e19228dSvisa 		cn30xxpow_tag_sw_wait();
12834e19228dSvisa 		cn30xxpow_work_request_async(
12844e19228dSvisa 		    OCTEON_CVMSEG_OFFSET(csm_pow_intr), POW_NO_WAIT);
12854e19228dSvisa 
12864a04f2fdSsyuu 		port = (work[1] & PIP_WQE_WORD1_IPRT) >> 42;
1287c1130c1fSvisa 		if (port != sc->sc_port) {
1288c1130c1fSvisa 			printf("%s: unexpected wqe port %u, should be %u\n",
1289c1130c1fSvisa 			    sc->sc_dev.dv_xname, port, sc->sc_port);
1290c1130c1fSvisa 			goto wqe_error;
1291c1130c1fSvisa 		}
12924a04f2fdSsyuu 
12937e29bba0Svisa 		nmbuf += cnmac_recv(sc, work, &ml);
12944e19228dSvisa 	}
1295c1130c1fSvisa 
12964e19228dSvisa 	_POW_WR8(sc->sc_pow, POW_WQ_INT_OFFSET, wqmask);
12974e19228dSvisa 
12987e29bba0Svisa 	if_input(ifp, &ml);
12997e29bba0Svisa 
13007e29bba0Svisa 	nmbuf = cnmac_mbuf_alloc(nmbuf);
13017e29bba0Svisa 	if (nmbuf != 0)
13027e29bba0Svisa 		atomic_add_int(&cnmac_mbufs_to_alloc, nmbuf);
13037e29bba0Svisa 
13044e19228dSvisa 	return 1;
1305c1130c1fSvisa 
1306c1130c1fSvisa wqe_error:
1307c1130c1fSvisa 	printf("word0: 0x%016llx\n", work[0]);
1308c1130c1fSvisa 	printf("word1: 0x%016llx\n", work[1]);
130990bc08a7Svisa 	printf("word2: 0x%016llx\n", work[2]);
131090bc08a7Svisa 	printf("word3: 0x%016llx\n", work[3]);
1311c1130c1fSvisa 	panic("wqe error");
13124a04f2fdSsyuu }
13134a04f2fdSsyuu 
13144a04f2fdSsyuu /* ---- tick */
13154a04f2fdSsyuu 
131640af4038Svisa void
cnmac_free_task(void * arg)13171e2bb501Svisa cnmac_free_task(void *arg)
131840af4038Svisa {
13191e2bb501Svisa 	struct cnmac_softc *sc = arg;
132040af4038Svisa 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
1321722eb25eSdlg 	struct ifqueue *ifq = &ifp->if_snd;
132240af4038Svisa 	int resched = 1;
132340af4038Svisa 	int timeout;
132440af4038Svisa 
132540af4038Svisa 	if (ml_len(&sc->sc_sendq) > 0) {
13261e2bb501Svisa 		cnmac_send_queue_flush_prefetch(sc);
13271e2bb501Svisa 		cnmac_send_queue_flush_fetch(sc);
13281e2bb501Svisa 		cnmac_send_queue_flush(sc);
132940af4038Svisa 	}
133040af4038Svisa 
1331722eb25eSdlg 	if (ifq_is_oactive(ifq)) {
1332722eb25eSdlg 		ifq_clr_oactive(ifq);
13331e2bb501Svisa 		cnmac_start(ifq);
133440af4038Svisa 
1335722eb25eSdlg 		if (ifq_is_oactive(ifq)) {
133640af4038Svisa 			/* The start routine did rescheduling already. */
133740af4038Svisa 			resched = 0;
133840af4038Svisa 		}
1339722eb25eSdlg 	}
134040af4038Svisa 
134140af4038Svisa 	if (resched) {
134240af4038Svisa 		timeout = (sc->sc_ext_callback_cnt > 0) ? 1 : hz;
134340af4038Svisa 		timeout_add(&sc->sc_tick_free_ch, timeout);
134440af4038Svisa 	}
134540af4038Svisa }
134640af4038Svisa 
13474a04f2fdSsyuu /*
13481e2bb501Svisa  * cnmac_tick_free
13494a04f2fdSsyuu  *
13504a04f2fdSsyuu  * => garbage collect send gather buffer / mbuf
13514a04f2fdSsyuu  * => called at softclock
13524a04f2fdSsyuu  */
135334a927e5Smiod void
cnmac_tick_free(void * arg)13541e2bb501Svisa cnmac_tick_free(void *arg)
13554a04f2fdSsyuu {
13561e2bb501Svisa 	struct cnmac_softc *sc = arg;
135740af4038Svisa 	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
13588d999ab0Svisa 	int to_alloc;
13594a04f2fdSsyuu 
136040af4038Svisa 	ifq_serialize(&ifp->if_snd, &sc->sc_free_task);
13618d999ab0Svisa 
13621e2bb501Svisa 	if (cnmac_mbufs_to_alloc != 0) {
13631e2bb501Svisa 		to_alloc = atomic_swap_uint(&cnmac_mbufs_to_alloc, 0);
13641e2bb501Svisa 		to_alloc = cnmac_mbuf_alloc(to_alloc);
13658d999ab0Svisa 		if (to_alloc != 0)
13661e2bb501Svisa 			atomic_add_int(&cnmac_mbufs_to_alloc, to_alloc);
13678d999ab0Svisa 	}
13684a04f2fdSsyuu }
13694a04f2fdSsyuu 
13704a04f2fdSsyuu /*
13711e2bb501Svisa  * cnmac_tick_misc
13724a04f2fdSsyuu  *
13734a04f2fdSsyuu  * => collect statistics
13744a04f2fdSsyuu  * => check link status
13754a04f2fdSsyuu  * => called at softclock
13764a04f2fdSsyuu  */
137734a927e5Smiod void
cnmac_tick_misc(void * arg)13781e2bb501Svisa cnmac_tick_misc(void *arg)
13794a04f2fdSsyuu {
13801e2bb501Svisa 	struct cnmac_softc *sc = arg;
13814a04f2fdSsyuu 	int s;
13824a04f2fdSsyuu 
13834a04f2fdSsyuu 	s = splnet();
13844a04f2fdSsyuu 	mii_tick(&sc->sc_mii);
13854a04f2fdSsyuu 	splx(s);
13864a04f2fdSsyuu 
13876ece48ccSvisa #if NKSTAT > 0
13886ece48ccSvisa 	cnmac_kstat_tick(sc);
13896ece48ccSvisa #endif
13906ece48ccSvisa 
13914a04f2fdSsyuu 	timeout_add_sec(&sc->sc_tick_misc_ch, 1);
13924a04f2fdSsyuu }
13936ece48ccSvisa 
13946ece48ccSvisa #if NKSTAT > 0
13956ece48ccSvisa #define KVE(n, t) \
13966ece48ccSvisa 	KSTAT_KV_UNIT_INITIALIZER((n), KSTAT_KV_T_COUNTER64, (t))
13976ece48ccSvisa 
13986ece48ccSvisa static const struct kstat_kv cnmac_kstat_tpl[cnmac_stat_count] = {
13996ece48ccSvisa 	[cnmac_stat_rx_toto_gmx]= KVE("rx total gmx",	KSTAT_KV_U_BYTES),
14006ece48ccSvisa 	[cnmac_stat_rx_totp_gmx]= KVE("rx total gmx",	KSTAT_KV_U_PACKETS),
14016ece48ccSvisa 	[cnmac_stat_rx_toto_pip]= KVE("rx total pip",	KSTAT_KV_U_BYTES),
14026ece48ccSvisa 	[cnmac_stat_rx_totp_pip]= KVE("rx total pip",	KSTAT_KV_U_PACKETS),
14036ece48ccSvisa 	[cnmac_stat_rx_h64]	= KVE("rx 64B",		KSTAT_KV_U_PACKETS),
14046ece48ccSvisa 	[cnmac_stat_rx_h127]	= KVE("rx 65-127B",	KSTAT_KV_U_PACKETS),
14056ece48ccSvisa 	[cnmac_stat_rx_h255]	= KVE("rx 128-255B",	KSTAT_KV_U_PACKETS),
14066ece48ccSvisa 	[cnmac_stat_rx_h511]	= KVE("rx 256-511B",	KSTAT_KV_U_PACKETS),
14076ece48ccSvisa 	[cnmac_stat_rx_h1023]	= KVE("rx 512-1023B",	KSTAT_KV_U_PACKETS),
14086ece48ccSvisa 	[cnmac_stat_rx_h1518]	= KVE("rx 1024-1518B",	KSTAT_KV_U_PACKETS),
14096ece48ccSvisa 	[cnmac_stat_rx_hmax]	= KVE("rx 1519-maxB",	KSTAT_KV_U_PACKETS),
14106ece48ccSvisa 	[cnmac_stat_rx_bcast]	= KVE("rx bcast",	KSTAT_KV_U_PACKETS),
14116ece48ccSvisa 	[cnmac_stat_rx_mcast]	= KVE("rx mcast",	KSTAT_KV_U_PACKETS),
14126ece48ccSvisa 	[cnmac_stat_rx_qdpo]	= KVE("rx qos drop",	KSTAT_KV_U_BYTES),
14136ece48ccSvisa 	[cnmac_stat_rx_qdpp]	= KVE("rx qos drop",	KSTAT_KV_U_PACKETS),
14146ece48ccSvisa 	[cnmac_stat_rx_fcs]	= KVE("rx fcs err",	KSTAT_KV_U_PACKETS),
14156ece48ccSvisa 	[cnmac_stat_rx_frag]	= KVE("rx fcs undersize",KSTAT_KV_U_PACKETS),
14166ece48ccSvisa 	[cnmac_stat_rx_undersz]	= KVE("rx undersize",	KSTAT_KV_U_PACKETS),
14176ece48ccSvisa 	[cnmac_stat_rx_jabber]	= KVE("rx jabber",	KSTAT_KV_U_PACKETS),
14186ece48ccSvisa 	[cnmac_stat_rx_oversz]	= KVE("rx oversize",	KSTAT_KV_U_PACKETS),
14196ece48ccSvisa 	[cnmac_stat_rx_raw]	= KVE("rx raw",		KSTAT_KV_U_PACKETS),
14206ece48ccSvisa 	[cnmac_stat_rx_bad]	= KVE("rx bad",		KSTAT_KV_U_PACKETS),
14216ece48ccSvisa 	[cnmac_stat_rx_drop]	= KVE("rx drop",	KSTAT_KV_U_PACKETS),
14226ece48ccSvisa 	[cnmac_stat_rx_ctl]	= KVE("rx control",	KSTAT_KV_U_PACKETS),
14236ece48ccSvisa 	[cnmac_stat_rx_dmac]	= KVE("rx dmac",	KSTAT_KV_U_PACKETS),
14246ece48ccSvisa 	[cnmac_stat_tx_toto]	= KVE("tx total",	KSTAT_KV_U_BYTES),
14256ece48ccSvisa 	[cnmac_stat_tx_totp]	= KVE("tx total",	KSTAT_KV_U_PACKETS),
14266ece48ccSvisa 	[cnmac_stat_tx_hmin]	= KVE("tx min-63B",	KSTAT_KV_U_PACKETS),
14276ece48ccSvisa 	[cnmac_stat_tx_h64]	= KVE("tx 64B",		KSTAT_KV_U_PACKETS),
14286ece48ccSvisa 	[cnmac_stat_tx_h127]	= KVE("tx 65-127B",	KSTAT_KV_U_PACKETS),
14296ece48ccSvisa 	[cnmac_stat_tx_h255]	= KVE("tx 128-255B",	KSTAT_KV_U_PACKETS),
14306ece48ccSvisa 	[cnmac_stat_tx_h511]	= KVE("tx 256-511B",	KSTAT_KV_U_PACKETS),
14316ece48ccSvisa 	[cnmac_stat_tx_h1023]	= KVE("tx 512-1023B",	KSTAT_KV_U_PACKETS),
14326ece48ccSvisa 	[cnmac_stat_tx_h1518]	= KVE("tx 1024-1518B",	KSTAT_KV_U_PACKETS),
14336ece48ccSvisa 	[cnmac_stat_tx_hmax]	= KVE("tx 1519-maxB",	KSTAT_KV_U_PACKETS),
14346ece48ccSvisa 	[cnmac_stat_tx_bcast]	= KVE("tx bcast",	KSTAT_KV_U_PACKETS),
14356ece48ccSvisa 	[cnmac_stat_tx_mcast]	= KVE("tx mcast",	KSTAT_KV_U_PACKETS),
14366ece48ccSvisa 	[cnmac_stat_tx_coll]	= KVE("tx coll",	KSTAT_KV_U_PACKETS),
14376ece48ccSvisa 	[cnmac_stat_tx_defer]	= KVE("tx defer",	KSTAT_KV_U_PACKETS),
14386ece48ccSvisa 	[cnmac_stat_tx_scol]	= KVE("tx scoll",	KSTAT_KV_U_PACKETS),
14396ece48ccSvisa 	[cnmac_stat_tx_mcol]	= KVE("tx mcoll",	KSTAT_KV_U_PACKETS),
14406ece48ccSvisa 	[cnmac_stat_tx_ctl]	= KVE("tx control",	KSTAT_KV_U_PACKETS),
14416ece48ccSvisa 	[cnmac_stat_tx_uflow]	= KVE("tx underflow",	KSTAT_KV_U_PACKETS),
14426ece48ccSvisa };
14436ece48ccSvisa 
14446ece48ccSvisa void
cnmac_kstat_attach(struct cnmac_softc * sc)14456ece48ccSvisa cnmac_kstat_attach(struct cnmac_softc *sc)
14466ece48ccSvisa {
14476ece48ccSvisa 	struct kstat *ks;
14486ece48ccSvisa 	struct kstat_kv *kvs;
14496ece48ccSvisa 
14506ece48ccSvisa 	mtx_init(&sc->sc_kstat_mtx, IPL_SOFTCLOCK);
14516ece48ccSvisa 
14526ece48ccSvisa 	ks = kstat_create(sc->sc_dev.dv_xname, 0, "cnmac-stats", 0,
14536ece48ccSvisa 	    KSTAT_T_KV, 0);
14546ece48ccSvisa 	if (ks == NULL)
14556ece48ccSvisa 		return;
14566ece48ccSvisa 
14576ece48ccSvisa 	kvs = malloc(sizeof(cnmac_kstat_tpl), M_DEVBUF, M_WAITOK | M_ZERO);
14586ece48ccSvisa 	memcpy(kvs, cnmac_kstat_tpl, sizeof(cnmac_kstat_tpl));
14596ece48ccSvisa 
14606ece48ccSvisa 	kstat_set_mutex(ks, &sc->sc_kstat_mtx);
14616ece48ccSvisa 	ks->ks_softc = sc;
14626ece48ccSvisa 	ks->ks_data = kvs;
14636ece48ccSvisa 	ks->ks_datalen = sizeof(cnmac_kstat_tpl);
14646ece48ccSvisa 	ks->ks_read = cnmac_kstat_read;
14656ece48ccSvisa 
14666ece48ccSvisa 	sc->sc_kstat = ks;
14676ece48ccSvisa 	kstat_install(ks);
14686ece48ccSvisa }
14696ece48ccSvisa 
14706ece48ccSvisa int
cnmac_kstat_read(struct kstat * ks)14716ece48ccSvisa cnmac_kstat_read(struct kstat *ks)
14726ece48ccSvisa {
14736ece48ccSvisa 	struct cnmac_softc *sc = ks->ks_softc;
14746ece48ccSvisa 	struct kstat_kv *kvs = ks->ks_data;
14756ece48ccSvisa 
14766ece48ccSvisa 	cn30xxpip_kstat_read(sc->sc_pip, kvs);
14776ece48ccSvisa 	cn30xxgmx_kstat_read(sc->sc_gmx_port, kvs);
14786ece48ccSvisa 
14796ece48ccSvisa 	getnanouptime(&ks->ks_updated);
14806ece48ccSvisa 
14816ece48ccSvisa 	return 0;
14826ece48ccSvisa }
14836ece48ccSvisa 
14846ece48ccSvisa void
cnmac_kstat_tick(struct cnmac_softc * sc)14856ece48ccSvisa cnmac_kstat_tick(struct cnmac_softc *sc)
14866ece48ccSvisa {
14876ece48ccSvisa 	if (sc->sc_kstat == NULL)
14886ece48ccSvisa 		return;
14896ece48ccSvisa 	if (!mtx_enter_try(&sc->sc_kstat_mtx))
14906ece48ccSvisa 		return;
14916ece48ccSvisa 	cnmac_kstat_read(sc->sc_kstat);
14926ece48ccSvisa 	mtx_leave(&sc->sc_kstat_mtx);
14936ece48ccSvisa }
14946ece48ccSvisa #endif
1495