xref: /openbsd-src/sys/arch/octeon/dev/if_ogx.c (revision a8b581972213782bec450017fe45a05ea431addf)
1*a8b58197Sjsg /*	$OpenBSD: if_ogx.c,v 1.7 2024/05/20 23:13:33 jsg Exp $	*/
23ec638c9Svisa 
33ec638c9Svisa /*
43a44e889Svisa  * Copyright (c) 2019-2020 Visa Hankala
53ec638c9Svisa  *
63ec638c9Svisa  * Permission to use, copy, modify, and/or distribute this software for any
73ec638c9Svisa  * purpose with or without fee is hereby granted, provided that the above
83ec638c9Svisa  * copyright notice and this permission notice appear in all copies.
93ec638c9Svisa  *
103ec638c9Svisa  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113ec638c9Svisa  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123ec638c9Svisa  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133ec638c9Svisa  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143ec638c9Svisa  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153ec638c9Svisa  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163ec638c9Svisa  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173ec638c9Svisa  */
183ec638c9Svisa 
193ec638c9Svisa /*
203ec638c9Svisa  * Driver for OCTEON III network processor.
213ec638c9Svisa  */
223ec638c9Svisa 
23816637a4Svisa #include "bpfilter.h"
247284f6b7Svisa #include "kstat.h"
25816637a4Svisa 
263ec638c9Svisa #include <sys/param.h>
273ec638c9Svisa #include <sys/systm.h>
283ec638c9Svisa #include <sys/atomic.h>
297284f6b7Svisa #include <sys/mutex.h>
307284f6b7Svisa #include <sys/rwlock.h>
313ec638c9Svisa #include <sys/device.h>
323ec638c9Svisa #include <sys/ioctl.h>
337284f6b7Svisa #include <sys/kstat.h>
343ec638c9Svisa #include <sys/socket.h>
353ec638c9Svisa #include <sys/stdint.h>
363ec638c9Svisa 
373ec638c9Svisa #include <net/if.h>
383ec638c9Svisa #include <net/if_media.h>
393ec638c9Svisa #include <netinet/in.h>
403a44e889Svisa #include <netinet/ip.h>
413ec638c9Svisa #include <netinet/if_ether.h>
423ec638c9Svisa 
43816637a4Svisa #if NBPFILTER > 0
44816637a4Svisa #include <net/bpf.h>
45816637a4Svisa #endif
46816637a4Svisa 
473a44e889Svisa #ifdef INET6
483a44e889Svisa #include <netinet/ip6.h>
493a44e889Svisa #endif
503a44e889Svisa 
513ec638c9Svisa #include <dev/mii/mii.h>
523ec638c9Svisa #include <dev/mii/miivar.h>
533ec638c9Svisa 
543ec638c9Svisa #include <dev/ofw/fdt.h>
553ec638c9Svisa #include <dev/ofw/openfirm.h>
563ec638c9Svisa 
573ec638c9Svisa #include <machine/bus.h>
583ec638c9Svisa #include <machine/fdt.h>
593ec638c9Svisa #include <machine/octeonvar.h>
603ec638c9Svisa #include <machine/octeon_model.h>
613ec638c9Svisa 
623ec638c9Svisa #include <octeon/dev/cn30xxsmivar.h>
633ec638c9Svisa #include <octeon/dev/ogxreg.h>
643ec638c9Svisa #include <octeon/dev/ogxvar.h>
653ec638c9Svisa 
663ec638c9Svisa struct ogx_link_ops;
673ec638c9Svisa 
683ec638c9Svisa struct ogx_softc {
693ec638c9Svisa 	struct device		 sc_dev;
703ec638c9Svisa 	struct arpcom		 sc_ac;
713ec638c9Svisa 	unsigned int		 sc_bgxid;
723ec638c9Svisa 	unsigned int		 sc_lmacid;
733ec638c9Svisa 	unsigned int		 sc_ipdport;
743ec638c9Svisa 	unsigned int		 sc_pkomac;
753ec638c9Svisa 	unsigned int		 sc_rxused;
763ec638c9Svisa 	unsigned int		 sc_txfree;
773ec638c9Svisa 
783ec638c9Svisa 	struct ogx_node		*sc_node;
793ec638c9Svisa 	unsigned int		 sc_unit;	/* logical unit within node */
803ec638c9Svisa 
813ec638c9Svisa 	struct mii_data		 sc_mii;
823ec638c9Svisa #define sc_media	sc_mii.mii_media
833ec638c9Svisa 	struct timeout		 sc_tick;
843ec638c9Svisa 	struct cn30xxsmi_softc	*sc_smi;
853ec638c9Svisa 
863ec638c9Svisa 	struct timeout		 sc_rxrefill;
873ec638c9Svisa 	void			*sc_rx_ih;
883ec638c9Svisa 	void			*sc_tx_ih;
893ec638c9Svisa 
903ec638c9Svisa 	bus_space_tag_t		 sc_iot;
913ec638c9Svisa 	bus_space_handle_t	 sc_port_ioh;
923ec638c9Svisa 	bus_space_handle_t	 sc_nexus_ioh;
933ec638c9Svisa 
943ec638c9Svisa 	struct fpa3aura		 sc_pkt_aura;
953ec638c9Svisa 	const struct ogx_link_ops *sc_link_ops;
963ec638c9Svisa 	uint8_t			 sc_link_duplex;
977284f6b7Svisa 
987284f6b7Svisa 	struct mutex		 sc_kstat_mtx;
997284f6b7Svisa 	struct timeout		 sc_kstat_tmo;
1007284f6b7Svisa 	struct kstat		*sc_kstat;
1017284f6b7Svisa 	uint64_t		*sc_counter_vals;
1027284f6b7Svisa 	bus_space_handle_t	 sc_pki_stat_ioh;
1033ec638c9Svisa };
1043ec638c9Svisa 
1053ec638c9Svisa #define DEVNAME(sc)		((sc)->sc_dev.dv_xname)
1063ec638c9Svisa 
1073ec638c9Svisa #define L1_QUEUE(sc)		((sc)->sc_unit)
1083ec638c9Svisa #define L2_QUEUE(sc)		((sc)->sc_unit)
1093ec638c9Svisa #define L3_QUEUE(sc)		((sc)->sc_unit)
1103ec638c9Svisa #define L4_QUEUE(sc)		((sc)->sc_unit)
1113ec638c9Svisa #define L5_QUEUE(sc)		((sc)->sc_unit)
1123ec638c9Svisa #define DESC_QUEUE(sc)		((sc)->sc_unit)
1133ec638c9Svisa 
1143ec638c9Svisa #define PORT_FIFO(sc)		((sc)->sc_unit)		/* PKO FIFO */
1153ec638c9Svisa #define PORT_GROUP_RX(sc)	((sc)->sc_unit * 2)	/* SSO group for Rx */
1163ec638c9Svisa #define PORT_GROUP_TX(sc)	((sc)->sc_unit * 2 + 1)	/* SSO group for Tx */
1173ec638c9Svisa #define PORT_MAC(sc)		((sc)->sc_pkomac)
1183ec638c9Svisa #define PORT_PKIND(sc)		((sc)->sc_unit)
1193ec638c9Svisa #define PORT_QPG(sc)		((sc)->sc_unit)
1203ec638c9Svisa #define PORT_STYLE(sc)		((sc)->sc_unit)
1213ec638c9Svisa 
1223ec638c9Svisa struct ogx_link_ops {
1233ec638c9Svisa 	const char	*link_type;
1243ec638c9Svisa 	unsigned int	 link_fifo_speed;	/* in Mbps */
1253ec638c9Svisa 	/* Initialize link. */
1263ec638c9Svisa 	int		(*link_init)(struct ogx_softc *);
1273ec638c9Svisa 	/* Deinitialize link. */
1283ec638c9Svisa 	void		(*link_down)(struct ogx_softc *);
1293ec638c9Svisa 	/* Change link parameters. */
1303ec638c9Svisa 	void		(*link_change)(struct ogx_softc *);
1313ec638c9Svisa 	/* Query link status. Returns non-zero if status has changed. */
1323ec638c9Svisa 	int		(*link_status)(struct ogx_softc *);
1333ec638c9Svisa };
1343ec638c9Svisa 
1353ec638c9Svisa struct ogx_fifo_group {
1363ec638c9Svisa 	unsigned int		fg_inited;
1373ec638c9Svisa 	unsigned int		fg_speed;
1383ec638c9Svisa };
1393ec638c9Svisa 
1403ec638c9Svisa struct ogx_config {
1413ec638c9Svisa 	unsigned int		cfg_nclusters;	/* number of parsing clusters */
1423ec638c9Svisa 	unsigned int		cfg_nfifogrps;	/* number of FIFO groups */
1433ec638c9Svisa 	unsigned int		cfg_nmacs;	/* number of MACs */
1443ec638c9Svisa 	unsigned int		cfg_npqs;	/* number of port queues */
1453ec638c9Svisa 	unsigned int		cfg_npkolvl;	/* number of PKO Lx levels */
1463ec638c9Svisa 	unsigned int		cfg_nullmac;	/* index of NULL MAC */
1473ec638c9Svisa };
1483ec638c9Svisa 
1493ec638c9Svisa struct ogx_node {
1503ec638c9Svisa 	bus_dma_tag_t		 node_dmat;
1513ec638c9Svisa 	bus_space_tag_t		 node_iot;
1523ec638c9Svisa 	bus_space_handle_t	 node_fpa3;
1533ec638c9Svisa 	bus_space_handle_t	 node_pki;
1543ec638c9Svisa 	bus_space_handle_t	 node_pko3;
1553ec638c9Svisa 	bus_space_handle_t	 node_sso;
1563ec638c9Svisa 
1573ec638c9Svisa 	struct fpa3pool		 node_pko_pool;
1583ec638c9Svisa 	struct fpa3pool		 node_pkt_pool;
1593ec638c9Svisa 	struct fpa3pool		 node_sso_pool;
1603ec638c9Svisa 	struct fpa3aura		 node_pko_aura;
1613ec638c9Svisa 	struct fpa3aura		 node_sso_aura;
1623ec638c9Svisa 
1633ec638c9Svisa 	uint64_t		 node_id;
1643ec638c9Svisa 	unsigned int		 node_nclusters;
1653ec638c9Svisa 	unsigned int		 node_nunits;
1663ec638c9Svisa 	struct ogx_fifo_group	 node_fifogrp[8];
1673ec638c9Svisa 	const struct ogx_config	*node_cfg;
1683ec638c9Svisa 
1693ec638c9Svisa 	struct rwlock		 node_lock;
1703ec638c9Svisa 	unsigned int		 node_flags;
1713ec638c9Svisa #define NODE_INITED			0x01	/* node initialized */
1723ec638c9Svisa #define NODE_FWREADY			0x02	/* node firmware ready */
1733ec638c9Svisa };
1743ec638c9Svisa 
1753ec638c9Svisa struct ogx_fwhdr {
1763ec638c9Svisa 	char		fw_version[8];
1773ec638c9Svisa 	uint64_t	fw_size;
1783ec638c9Svisa };
1793ec638c9Svisa 
1803ec638c9Svisa #define BGX_PORT_SIZE	0x100000
1813ec638c9Svisa 
1823ec638c9Svisa #define PORT_RD_8(sc, reg) \
1833ec638c9Svisa 	bus_space_read_8((sc)->sc_iot, (sc)->sc_port_ioh, (reg))
1843ec638c9Svisa #define PORT_WR_8(sc, reg, val) \
1853ec638c9Svisa 	bus_space_write_8((sc)->sc_iot, (sc)->sc_port_ioh, (reg), (val))
1863ec638c9Svisa 
1873ec638c9Svisa #define NEXUS_RD_8(sc, reg) \
1883ec638c9Svisa 	bus_space_read_8((sc)->sc_iot, (sc)->sc_nexus_ioh, (reg))
1893ec638c9Svisa #define NEXUS_WR_8(sc, reg, val) \
1903ec638c9Svisa 	bus_space_write_8((sc)->sc_iot, (sc)->sc_nexus_ioh, (reg), (val))
1913ec638c9Svisa 
1923ec638c9Svisa #define FPA3_RD_8(node, reg) \
1933ec638c9Svisa 	bus_space_read_8((node)->node_iot, (node)->node_fpa3, (reg))
1943ec638c9Svisa #define FPA3_WR_8(node, reg, val) \
1953ec638c9Svisa 	bus_space_write_8((node)->node_iot, (node)->node_fpa3, (reg), (val))
1963ec638c9Svisa #define PKI_RD_8(node, reg) \
1973ec638c9Svisa 	bus_space_read_8((node)->node_iot, (node)->node_pki, (reg))
1983ec638c9Svisa #define PKI_WR_8(node, reg, val) \
1993ec638c9Svisa 	bus_space_write_8((node)->node_iot, (node)->node_pki, (reg), (val))
2003ec638c9Svisa #define PKO3_RD_8(node, reg) \
2013ec638c9Svisa 	bus_space_read_8((node)->node_iot, (node)->node_pko3, (reg))
2023ec638c9Svisa #define PKO3_WR_8(node, reg, val) \
2033ec638c9Svisa 	bus_space_write_8((node)->node_iot, (node)->node_pko3, (reg), (val))
2043ec638c9Svisa #define SSO_RD_8(node, reg) \
2053ec638c9Svisa 	bus_space_read_8((node)->node_iot, (node)->node_sso, (reg))
2063ec638c9Svisa #define SSO_WR_8(node, reg, val) \
2073ec638c9Svisa 	bus_space_write_8((node)->node_iot, (node)->node_sso, (reg), (val))
2083ec638c9Svisa 
2093ec638c9Svisa int	ogx_match(struct device *, void *, void *);
2103ec638c9Svisa void	ogx_attach(struct device *, struct device *, void *);
2113ec638c9Svisa void	ogx_defer(struct device *);
2123ec638c9Svisa 
2133ec638c9Svisa int	ogx_ioctl(struct ifnet *, u_long, caddr_t);
2143ec638c9Svisa void	ogx_start(struct ifqueue *);
2153ec638c9Svisa int	ogx_send_mbuf(struct ogx_softc *, struct mbuf *);
2163ec638c9Svisa u_int	ogx_load_mbufs(struct ogx_softc *, unsigned int);
2173ec638c9Svisa u_int	ogx_unload_mbufs(struct ogx_softc *);
2183ec638c9Svisa 
2193ec638c9Svisa void	ogx_media_status(struct ifnet *, struct ifmediareq *);
2203ec638c9Svisa int	ogx_media_change(struct ifnet *);
2213ec638c9Svisa int	ogx_mii_readreg(struct device *, int, int);
2223ec638c9Svisa void	ogx_mii_writereg(struct device *, int, int, int);
2233ec638c9Svisa void	ogx_mii_statchg(struct device *);
2243ec638c9Svisa 
2253ec638c9Svisa int	ogx_init(struct ogx_softc *);
2263ec638c9Svisa void	ogx_down(struct ogx_softc *);
2273ec638c9Svisa void	ogx_iff(struct ogx_softc *);
2283ec638c9Svisa void	ogx_rxrefill(void *);
2293ec638c9Svisa int	ogx_rxintr(void *);
2303ec638c9Svisa int	ogx_txintr(void *);
2313ec638c9Svisa void	ogx_tick(void *);
2323ec638c9Svisa 
2337284f6b7Svisa #if NKSTAT > 0
2347284f6b7Svisa #define OGX_KSTAT_TICK_SECS	600
2357284f6b7Svisa void	ogx_kstat_attach(struct ogx_softc *);
2367284f6b7Svisa int	ogx_kstat_read(struct kstat *);
2377284f6b7Svisa void	ogx_kstat_start(struct ogx_softc *);
2387284f6b7Svisa void	ogx_kstat_stop(struct ogx_softc *);
2397284f6b7Svisa void	ogx_kstat_tick(void *);
2407284f6b7Svisa #endif
2417284f6b7Svisa 
2423ec638c9Svisa int	ogx_node_init(struct ogx_node **, bus_dma_tag_t, bus_space_tag_t);
2433ec638c9Svisa int	ogx_node_load_firmware(struct ogx_node *);
2443ec638c9Svisa void	ogx_fpa3_aura_init(struct ogx_node *, struct fpa3aura *, uint32_t,
2453ec638c9Svisa 	    struct fpa3pool *);
2463ec638c9Svisa void	ogx_fpa3_aura_load(struct ogx_node *, struct fpa3aura *, size_t,
2473ec638c9Svisa 	    size_t);
2483ec638c9Svisa paddr_t	ogx_fpa3_alloc(struct fpa3aura *);
2493ec638c9Svisa void	ogx_fpa3_free(struct fpa3aura *, paddr_t);
2503ec638c9Svisa void	ogx_fpa3_pool_init(struct ogx_node *, struct fpa3pool *, uint32_t,
2513ec638c9Svisa 	    uint32_t);
2523ec638c9Svisa 
2533ec638c9Svisa int	ogx_sgmii_link_init(struct ogx_softc *);
2543ec638c9Svisa void	ogx_sgmii_link_down(struct ogx_softc *);
2553ec638c9Svisa void	ogx_sgmii_link_change(struct ogx_softc *);
2563ec638c9Svisa 
2573ec638c9Svisa static inline paddr_t
ogx_kvtophys(vaddr_t kva)2583ec638c9Svisa ogx_kvtophys(vaddr_t kva)
2593ec638c9Svisa {
2603ec638c9Svisa 	KASSERT(IS_XKPHYS(kva));
2613ec638c9Svisa 	return XKPHYS_TO_PHYS(kva);
2623ec638c9Svisa }
2633ec638c9Svisa #define KVTOPHYS(addr)	ogx_kvtophys((vaddr_t)(addr))
2643ec638c9Svisa 
2653ec638c9Svisa const struct cfattach ogx_ca = {
2663ec638c9Svisa 	sizeof(struct ogx_softc), ogx_match, ogx_attach
2673ec638c9Svisa };
2683ec638c9Svisa 
2693ec638c9Svisa struct cfdriver ogx_cd = {
2706c6a9dbfSvisa 	NULL, "ogx", DV_IFNET
2713ec638c9Svisa };
2723ec638c9Svisa 
2733ec638c9Svisa const struct ogx_config ogx_cn73xx_config = {
2743ec638c9Svisa 	.cfg_nclusters		= 2,
2753ec638c9Svisa 	.cfg_nfifogrps		= 4,
2763ec638c9Svisa 	.cfg_nmacs		= 14,
2773ec638c9Svisa 	.cfg_npqs		= 16,
2783ec638c9Svisa 	.cfg_npkolvl		= 3,
2793ec638c9Svisa 	.cfg_nullmac		= 15,
2803ec638c9Svisa };
2813ec638c9Svisa 
2823ec638c9Svisa const struct ogx_config ogx_cn78xx_config = {
2833ec638c9Svisa 	.cfg_nclusters		= 4,
2843ec638c9Svisa 	.cfg_nfifogrps		= 8,
2853ec638c9Svisa 	.cfg_nmacs		= 28,
2863ec638c9Svisa 	.cfg_npqs		= 32,
2873ec638c9Svisa 	.cfg_npkolvl		= 5,
2883ec638c9Svisa 	.cfg_nullmac		= 28,
2893ec638c9Svisa };
2903ec638c9Svisa 
2913ec638c9Svisa const struct ogx_link_ops ogx_sgmii_link_ops = {
2923ec638c9Svisa 	.link_type		= "SGMII",
2933ec638c9Svisa 	.link_fifo_speed	= 1000,
2943ec638c9Svisa 	.link_init		= ogx_sgmii_link_init,
2953ec638c9Svisa 	.link_down		= ogx_sgmii_link_down,
2963ec638c9Svisa 	.link_change		= ogx_sgmii_link_change,
2973ec638c9Svisa };
2983ec638c9Svisa 
2993ec638c9Svisa const struct ogx_link_ops ogx_xfi_link_ops = {
3003ec638c9Svisa 	.link_type		= "XFI",
3013ec638c9Svisa 	.link_fifo_speed	= 10000,
3023ec638c9Svisa };
3033ec638c9Svisa 
3043ec638c9Svisa #define BELTYPE_NONE	0x00
3053ec638c9Svisa #define BELTYPE_MISC	0x01
3063ec638c9Svisa #define BELTYPE_IPv4	0x02
3073ec638c9Svisa #define BELTYPE_IPv6	0x03
3083ec638c9Svisa #define BELTYPE_TCP	0x04
3093ec638c9Svisa #define BELTYPE_UDP	0x05
3103ec638c9Svisa 
3113ec638c9Svisa static const unsigned int ogx_ltypes[] = {
3123ec638c9Svisa 	BELTYPE_NONE,	/* 0x00 */
3133ec638c9Svisa 	BELTYPE_MISC,	/* 0x01 Ethernet */
3143ec638c9Svisa 	BELTYPE_MISC,	/* 0x02 VLAN */
3153ec638c9Svisa 	BELTYPE_NONE,	/* 0x03 */
3163ec638c9Svisa 	BELTYPE_NONE,	/* 0x04 */
3173ec638c9Svisa 	BELTYPE_MISC,	/* 0x05 SNAP */
3183ec638c9Svisa 	BELTYPE_MISC,	/* 0x06 ARP */
3193ec638c9Svisa 	BELTYPE_MISC,	/* 0x07 RARP */
3203ec638c9Svisa 	BELTYPE_IPv4,	/* 0x08 IPv4 */
3213ec638c9Svisa 	BELTYPE_IPv4,	/* 0x09 IPv4 options */
3223ec638c9Svisa 	BELTYPE_IPv6,	/* 0x0a IPv6 */
3233ec638c9Svisa 	BELTYPE_IPv6,	/* 0x0b IPv6 options */
3243ec638c9Svisa 	BELTYPE_MISC,	/* 0x0c ESP */
3253ec638c9Svisa 	BELTYPE_MISC,	/* 0x0d IP fragment */
3263ec638c9Svisa 	BELTYPE_MISC,	/* 0x0e IPcomp */
3273ec638c9Svisa 	BELTYPE_NONE,	/* 0x0f */
3283ec638c9Svisa 	BELTYPE_TCP,	/* 0x10 TCP */
3293ec638c9Svisa 	BELTYPE_UDP,	/* 0x11 UDP */
3303ec638c9Svisa 	BELTYPE_MISC,	/* 0x12 SCTP */
3313ec638c9Svisa 	BELTYPE_UDP,	/* 0x13 UDP VXLAN */
3323ec638c9Svisa 	BELTYPE_MISC,	/* 0x14 GRE */
3333ec638c9Svisa 	BELTYPE_MISC,	/* 0x15 NVGRE */
3343ec638c9Svisa 	BELTYPE_MISC,	/* 0x16 GTP */
3353ec638c9Svisa 	BELTYPE_UDP,	/* 0x17 UDP Geneve */
3363ec638c9Svisa 	BELTYPE_NONE,	/* 0x18 */
3373ec638c9Svisa 	BELTYPE_NONE,	/* 0x19 */
3383ec638c9Svisa 	BELTYPE_NONE,	/* 0x1a */
3393ec638c9Svisa 	BELTYPE_NONE,	/* 0x1b */
3403ec638c9Svisa 	BELTYPE_MISC,	/* 0x1c software */
3413ec638c9Svisa 	BELTYPE_MISC,	/* 0x1d software */
3423ec638c9Svisa 	BELTYPE_MISC,	/* 0x1e software */
3433ec638c9Svisa 	BELTYPE_MISC	/* 0x1f software */
3443ec638c9Svisa };
3453ec638c9Svisa 
3463ec638c9Svisa #define OGX_POOL_SSO		0
3473ec638c9Svisa #define OGX_POOL_PKO		1
3483ec638c9Svisa #define OGX_POOL_PKT		2
3493ec638c9Svisa 
3503ec638c9Svisa #define OGX_AURA_SSO		0
3513ec638c9Svisa #define OGX_AURA_PKO		1
3523ec638c9Svisa #define OGX_AURA_PKT(sc)	((sc)->sc_unit + 2)
3533ec638c9Svisa 
3543ec638c9Svisa struct ogx_node	ogx_node;
3553ec638c9Svisa 
3563ec638c9Svisa int
ogx_match(struct device * parent,void * match,void * aux)3573ec638c9Svisa ogx_match(struct device *parent, void *match, void *aux)
3583ec638c9Svisa {
3593ec638c9Svisa 	return 1;
3603ec638c9Svisa }
3613ec638c9Svisa 
3623ec638c9Svisa void
ogx_attach(struct device * parent,struct device * self,void * aux)3633ec638c9Svisa ogx_attach(struct device *parent, struct device *self, void *aux)
3643ec638c9Svisa {
3653ec638c9Svisa 	const struct ogx_config *cfg;
3663ec638c9Svisa 	struct ogx_fifo_group *fifogrp;
3673ec638c9Svisa 	struct ogx_node *node;
3683ec638c9Svisa 	struct ogx_attach_args *oaa = aux;
3693ec638c9Svisa 	struct ogx_softc *sc = (struct ogx_softc *)self;
3703ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
3713ec638c9Svisa 	uint64_t lmac_type, lut_index, val;
3723ec638c9Svisa 	uint32_t lmac;
3733ec638c9Svisa 	int fgindex = PORT_FIFO(sc) >> 2;
3743ec638c9Svisa 	int cl, phy_addr, phy_handle;
3753ec638c9Svisa 
3763ec638c9Svisa 	if (ogx_node_init(&node, oaa->oaa_dmat, oaa->oaa_iot)) {
3773ec638c9Svisa 		printf(": node init failed\n");
3783ec638c9Svisa 		return;
3793ec638c9Svisa 	}
3803ec638c9Svisa 	cfg = node->node_cfg;
3813ec638c9Svisa 
3823ec638c9Svisa 	sc->sc_node = node;
3833ec638c9Svisa 	sc->sc_unit = node->node_nunits++;
3843ec638c9Svisa 
3853ec638c9Svisa 	phy_handle = OF_getpropint(oaa->oaa_node, "phy-handle", 0);
3863ec638c9Svisa 	if (phy_handle == 0) {
3873ec638c9Svisa 		printf(": no phy-handle\n");
3883ec638c9Svisa 		return;
3893ec638c9Svisa 	}
3903ec638c9Svisa 	if (cn30xxsmi_get_phy(phy_handle, 0, &sc->sc_smi, &phy_addr)) {
3913ec638c9Svisa 		printf(": no phy found\n");
3923ec638c9Svisa 		return;
3933ec638c9Svisa 	}
3943ec638c9Svisa 
3953ec638c9Svisa 	lmac = OF_getpropint(oaa->oaa_node, "reg", UINT32_MAX);
3963ec638c9Svisa 	if (lmac == UINT32_MAX) {
3973ec638c9Svisa 		printf(": no reg property\n");
3983ec638c9Svisa 		return;
3993ec638c9Svisa 	}
4003ec638c9Svisa 
4013ec638c9Svisa 	sc->sc_bgxid = oaa->oaa_bgxid;
4023ec638c9Svisa 	sc->sc_lmacid = lmac;
4033ec638c9Svisa 	sc->sc_ipdport = sc->sc_bgxid * 0x100 + lmac * 0x10 + 0x800;
4043ec638c9Svisa 	sc->sc_pkomac = sc->sc_bgxid * 4 + lmac + 2;
4053ec638c9Svisa 
4063ec638c9Svisa 	if (OF_getproplen(oaa->oaa_node, "local-mac-address") !=
4073ec638c9Svisa 	    ETHER_ADDR_LEN) {
4083ec638c9Svisa 		printf(": no MAC address\n");
4093ec638c9Svisa 		return;
4103ec638c9Svisa 	}
4113ec638c9Svisa 	OF_getprop(oaa->oaa_node, "local-mac-address", sc->sc_ac.ac_enaddr,
4123ec638c9Svisa 	    ETHER_ADDR_LEN);
4133ec638c9Svisa 
4143ec638c9Svisa 	sc->sc_iot = oaa->oaa_iot;
4153ec638c9Svisa 	sc->sc_nexus_ioh = oaa->oaa_ioh;
4163ec638c9Svisa 	if (bus_space_subregion(sc->sc_iot, oaa->oaa_ioh,
4173ec638c9Svisa 	    sc->sc_lmacid * BGX_PORT_SIZE, BGX_PORT_SIZE, &sc->sc_port_ioh)) {
4183ec638c9Svisa 		printf(": can't map IO subregion\n");
4193ec638c9Svisa 		return;
4203ec638c9Svisa 	}
4213ec638c9Svisa 
4223ec638c9Svisa 	val = PORT_RD_8(sc, BGX_CMR_RX_ID_MAP);
4233ec638c9Svisa 	val &= ~BGX_CMR_RX_ID_MAP_RID_M;
4243ec638c9Svisa 	val &= ~BGX_CMR_RX_ID_MAP_PKND_M;
4253ec638c9Svisa 	val |= (uint64_t)(sc->sc_bgxid * 4 + 2 + sc->sc_lmacid) <<
4263ec638c9Svisa 	    BGX_CMR_RX_ID_MAP_RID_S;
4273ec638c9Svisa 	val |= (uint64_t)PORT_PKIND(sc) << BGX_CMR_RX_ID_MAP_PKND_S;
4283ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_RX_ID_MAP, val);
4293ec638c9Svisa 
4303ec638c9Svisa 	val = PORT_RD_8(sc, BGX_CMR_CHAN_MSK_AND);
4313ec638c9Svisa 	val |= 0xffffULL << (sc->sc_lmacid * 16);
4323ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_CHAN_MSK_AND, val);
4333ec638c9Svisa 
4343ec638c9Svisa 	val = PORT_RD_8(sc, BGX_CMR_CHAN_MSK_OR);
4353ec638c9Svisa 	val |= 0xffffULL << (sc->sc_lmacid * 16);
4363ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_CHAN_MSK_OR, val);
4373ec638c9Svisa 
4383ec638c9Svisa 	sc->sc_rx_ih = octeon_intr_establish(0x61000 | PORT_GROUP_RX(sc),
4393ec638c9Svisa 	    IPL_NET | IPL_MPSAFE, ogx_rxintr, sc, DEVNAME(sc));
4403ec638c9Svisa 	if (sc->sc_rx_ih == NULL) {
4413ec638c9Svisa 		printf(": could not establish Rx interrupt\n");
4423ec638c9Svisa 		return;
4433ec638c9Svisa 	}
4443ec638c9Svisa 	sc->sc_tx_ih = octeon_intr_establish(0x61000 | PORT_GROUP_TX(sc),
4453ec638c9Svisa 	    IPL_NET | IPL_MPSAFE, ogx_txintr, sc, DEVNAME(sc));
4463ec638c9Svisa 	if (sc->sc_tx_ih == NULL) {
4473ec638c9Svisa 		printf(": could not establish Tx interrupt\n");
4483ec638c9Svisa 		return;
4493ec638c9Svisa 	}
4503ec638c9Svisa 
4513ec638c9Svisa 	val = PORT_RD_8(sc, BGX_CMR_CONFIG);
4523ec638c9Svisa 	lmac_type = (val & BGX_CMR_CONFIG_LMAC_TYPE_M) >>
4533ec638c9Svisa 	    BGX_CMR_CONFIG_LMAC_TYPE_S;
4543ec638c9Svisa 	switch (lmac_type) {
4553ec638c9Svisa 	case 0:
4563ec638c9Svisa 		sc->sc_link_ops = &ogx_sgmii_link_ops;
4573ec638c9Svisa 		break;
4583ec638c9Svisa 	default:
4593ec638c9Svisa 		printf(": unhandled LMAC type %llu\n", lmac_type);
4603ec638c9Svisa 		return;
4613ec638c9Svisa 	}
4623ec638c9Svisa 	printf(": %s", sc->sc_link_ops->link_type);
4633ec638c9Svisa 
4643ec638c9Svisa 	printf(", address %s", ether_sprintf(sc->sc_ac.ac_enaddr));
4653ec638c9Svisa 
4663ec638c9Svisa 	ogx_fpa3_aura_init(node, &sc->sc_pkt_aura, OGX_AURA_PKT(sc),
4673ec638c9Svisa 	    &node->node_pkt_pool);
4683ec638c9Svisa 
4693ec638c9Svisa 	sc->sc_rxused = 128;
4703ec638c9Svisa 	sc->sc_txfree = 128;
4713ec638c9Svisa 
4723ec638c9Svisa 	timeout_set(&sc->sc_rxrefill, ogx_rxrefill, sc);
4733ec638c9Svisa 	timeout_set(&sc->sc_tick, ogx_tick, sc);
4743ec638c9Svisa 
4753ec638c9Svisa 	printf("\n");
4763ec638c9Svisa 
4773ec638c9Svisa 	strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
4783ec638c9Svisa 	ifp->if_softc = sc;
4793ec638c9Svisa 	ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
4803ec638c9Svisa 	ifp->if_xflags |= IFXF_MPSAFE;
4813ec638c9Svisa 	ifp->if_ioctl = ogx_ioctl;
4823ec638c9Svisa 	ifp->if_qstart = ogx_start;
4833a44e889Svisa 	ifp->if_capabilities = IFCAP_CSUM_IPv4 |
4843a44e889Svisa 	    IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4 |
4853a44e889Svisa 	    IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6;
4863ec638c9Svisa 
4873ec638c9Svisa 	sc->sc_mii.mii_ifp = ifp;
4883ec638c9Svisa 	sc->sc_mii.mii_readreg = ogx_mii_readreg;
4893ec638c9Svisa 	sc->sc_mii.mii_writereg = ogx_mii_writereg;
4903ec638c9Svisa 	sc->sc_mii.mii_statchg = ogx_mii_statchg;
4913ec638c9Svisa 	ifmedia_init(&sc->sc_media, 0, ogx_media_change, ogx_media_status);
4923ec638c9Svisa 
4933ec638c9Svisa 	mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, phy_addr,
4943ec638c9Svisa 	    MII_OFFSET_ANY, MIIF_NOISOLATE);
4953ec638c9Svisa 	if (LIST_EMPTY(&sc->sc_mii.mii_phys)) {
4963ec638c9Svisa 		printf("%s: no PHY found\n", DEVNAME(sc));
4973ec638c9Svisa 		ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_MANUAL, 0, NULL);
4983ec638c9Svisa 		ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_MANUAL);
4993ec638c9Svisa 	} else {
5003ec638c9Svisa 		ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
5013ec638c9Svisa 
5023ec638c9Svisa 		timeout_add_sec(&sc->sc_tick, 1);
5033ec638c9Svisa 	}
5043ec638c9Svisa 
5053ec638c9Svisa 	/*
5063ec638c9Svisa 	 * Set up the PKI for this port.
5073ec638c9Svisa 	 */
5083ec638c9Svisa 
5093ec638c9Svisa 	val = (uint64_t)PORT_GROUP_RX(sc) << PKI_QPG_TBL_GRP_OK_S;
5103ec638c9Svisa 	val |= (uint64_t)PORT_GROUP_RX(sc) << PKI_QPG_TBL_GRP_BAD_S;
5113ec638c9Svisa 	val |= OGX_AURA_PKT(sc) << PKI_QPG_TBL_LAURA_S;
5123ec638c9Svisa 	PKI_WR_8(node, PKI_QPG_TBL(PORT_QPG(sc)), val);
5133ec638c9Svisa 
5143ec638c9Svisa 	for (cl = 0; cl < cfg->cfg_nclusters; cl++) {
5153ec638c9Svisa 		val = (uint64_t)PORT_QPG(sc) << PKI_CL_STYLE_CFG_QPG_BASE_S;
5163ec638c9Svisa 		PKI_WR_8(node, PKI_CL_STYLE_CFG(cl, PORT_STYLE(sc)), val);
5173ec638c9Svisa 		PKI_WR_8(node, PKI_CL_STYLE_CFG2(cl, PORT_STYLE(sc)), 0);
5183ec638c9Svisa 		PKI_WR_8(node, PKI_CL_STYLE_ALG(cl, PORT_STYLE(sc)), 1u << 31);
5193ec638c9Svisa 
5203ec638c9Svisa 		val = PKI_RD_8(node, PKI_CL_PKIND_STYLE(cl, PORT_PKIND(sc)));
5213ec638c9Svisa 		val &= ~PKI_CL_PKIND_STYLE_PM_M;
5223ec638c9Svisa 		val &= ~PKI_CL_PKIND_STYLE_STYLE_M;
5233ec638c9Svisa 		val |= PORT_STYLE(sc) << PKI_CL_PKIND_STYLE_STYLE_S;
5243ec638c9Svisa 		PKI_WR_8(node, PKI_CL_PKIND_STYLE(cl, PORT_PKIND(sc)), val);
5253ec638c9Svisa 	}
5263ec638c9Svisa 
5273ec638c9Svisa 	val = 5ULL << PKI_STYLE_BUF_FIRST_SKIP_S;
5283ec638c9Svisa 	val |= ((MCLBYTES - CACHELINESIZE) / sizeof(uint64_t)) <<
5293ec638c9Svisa 	    PKI_STYLE_BUF_MB_SIZE_S;
5303ec638c9Svisa 	PKI_WR_8(node, PKI_STYLE_BUF(PORT_STYLE(sc)), val);
5313ec638c9Svisa 
5323ec638c9Svisa 	/*
5333ec638c9Svisa 	 * Set up output queues from the descriptor queue to the port queue.
5343ec638c9Svisa 	 *
5353ec638c9Svisa 	 * The hardware implements a multilevel hierarchy of queues
5363ec638c9Svisa 	 * with configurable priorities.
5373ec638c9Svisa 	 * This driver uses a simple topology where there is one queue
5383ec638c9Svisa 	 * on each level.
5393ec638c9Svisa 	 *
5403ec638c9Svisa 	 * CN73xx: DQ ->             L3 -> L2 -> port
5413ec638c9Svisa 	 * CN78xx: DQ -> L5 -> L4 -> L3 -> L2 -> port
5423ec638c9Svisa 	 */
5433ec638c9Svisa 
5443ec638c9Svisa 	/* Map channel to queue L2. */
5453ec638c9Svisa 	val = PKO3_RD_8(node, PKO3_L3_L2_SQ_CHANNEL(L2_QUEUE(sc)));
5463ec638c9Svisa 	val &= ~PKO3_L3_L2_SQ_CHANNEL_CC_ENABLE;
5473ec638c9Svisa 	val &= ~PKO3_L3_L2_SQ_CHANNEL_M;
5483ec638c9Svisa 	val |= (uint64_t)sc->sc_ipdport << PKO3_L3_L2_SQ_CHANNEL_S;
5493ec638c9Svisa 	PKO3_WR_8(node, PKO3_L3_L2_SQ_CHANNEL(L2_QUEUE(sc)), val);
5503ec638c9Svisa 
5513ec638c9Svisa 	val = PKO3_RD_8(node, PKO3_MAC_CFG(PORT_MAC(sc)));
5523ec638c9Svisa 	val &= ~PKO3_MAC_CFG_MIN_PAD_ENA;
5533ec638c9Svisa 	val &= ~PKO3_MAC_CFG_FCS_ENA;
5543ec638c9Svisa 	val &= ~PKO3_MAC_CFG_FCS_SOP_OFF_M;
5553ec638c9Svisa 	val &= ~PKO3_MAC_CFG_FIFO_NUM_M;
5563ec638c9Svisa 	val |= PORT_FIFO(sc) << PKO3_MAC_CFG_FIFO_NUM_S;
5573ec638c9Svisa 	PKO3_WR_8(node, PKO3_MAC_CFG(PORT_MAC(sc)), val);
5583ec638c9Svisa 
5593ec638c9Svisa 	val = PKO3_RD_8(node, PKO3_MAC_CFG(PORT_MAC(sc)));
5603ec638c9Svisa 	val &= ~PKO3_MAC_CFG_SKID_MAX_CNT_M;
5613ec638c9Svisa 	PKO3_WR_8(node, PKO3_MAC_CFG(PORT_MAC(sc)), val);
5623ec638c9Svisa 
5633ec638c9Svisa 	PKO3_WR_8(node, PKO3_MCI0_MAX_CRED(PORT_MAC(sc)), 0);
5643ec638c9Svisa 	PKO3_WR_8(node, PKO3_MCI1_MAX_CRED(PORT_MAC(sc)), 2560 / 16);
5653ec638c9Svisa 
5663ec638c9Svisa 	/* Map the port queue to the MAC. */
5673ec638c9Svisa 
5683ec638c9Svisa 	val = (uint64_t)PORT_MAC(sc) << PKO3_L1_SQ_TOPOLOGY_LINK_S;
5693ec638c9Svisa 	PKO3_WR_8(node, PKO3_L1_SQ_TOPOLOGY(L1_QUEUE(sc)), val);
5703ec638c9Svisa 
5713ec638c9Svisa 	val = (uint64_t)PORT_MAC(sc) << PKO3_L1_SQ_SHAPE_LINK_S;
5723ec638c9Svisa 	PKO3_WR_8(node, PKO3_L1_SQ_SHAPE(L1_QUEUE(sc)), val);
5733ec638c9Svisa 
5743ec638c9Svisa 	val = (uint64_t)PORT_MAC(sc) << PKO3_L1_SQ_LINK_LINK_S;
5753ec638c9Svisa 	PKO3_WR_8(node, PKO3_L1_SQ_LINK(L1_QUEUE(sc)), val);
5763ec638c9Svisa 
5773ec638c9Svisa 	/* L1 / port queue */
5783ec638c9Svisa 
5793ec638c9Svisa 	val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
5803ec638c9Svisa 	PKO3_WR_8(node, PKO3_L1_SQ_SCHEDULE(L1_QUEUE(sc)), val);
5813ec638c9Svisa 
5823ec638c9Svisa 	val = PKO3_RD_8(node, PKO3_L1_SQ_TOPOLOGY(L1_QUEUE(sc)));
5833ec638c9Svisa 	val &= ~PKO3_L1_SQ_TOPOLOGY_PRIO_ANCHOR_M;
5843ec638c9Svisa 	val &= ~PKO3_L1_SQ_TOPOLOGY_RR_PRIO_M;
5853ec638c9Svisa 	val |= (uint64_t)L2_QUEUE(sc) << PKO3_L1_SQ_TOPOLOGY_PRIO_ANCHOR_S;
5863ec638c9Svisa 	val |= (uint64_t)0xf << PKO3_L1_SQ_TOPOLOGY_RR_PRIO_S;
5873ec638c9Svisa 	PKO3_WR_8(node, PKO3_L1_SQ_TOPOLOGY(L1_QUEUE(sc)), val);
5883ec638c9Svisa 
5893ec638c9Svisa 	/* L2 */
5903ec638c9Svisa 
5913ec638c9Svisa 	val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
5923ec638c9Svisa 	PKO3_WR_8(node, PKO3_L2_SQ_SCHEDULE(L2_QUEUE(sc)), val);
5933ec638c9Svisa 
5943ec638c9Svisa 	val = PKO3_RD_8(node, PKO3_L2_SQ_TOPOLOGY(L2_QUEUE(sc)));
5953ec638c9Svisa 	val &= ~PKO3_L2_SQ_TOPOLOGY_PRIO_ANCHOR_M;
5963ec638c9Svisa 	val &= ~PKO3_L2_SQ_TOPOLOGY_PARENT_M;
5973ec638c9Svisa 	val &= ~PKO3_L2_SQ_TOPOLOGY_RR_PRIO_M;
5983ec638c9Svisa 	val |= (uint64_t)L3_QUEUE(sc) << PKO3_L2_SQ_TOPOLOGY_PRIO_ANCHOR_S;
5993ec638c9Svisa 	val |= (uint64_t)L1_QUEUE(sc) << PKO3_L2_SQ_TOPOLOGY_PARENT_S;
6003ec638c9Svisa 	val |= (uint64_t)0xf << PKO3_L2_SQ_TOPOLOGY_RR_PRIO_S;
6013ec638c9Svisa 	PKO3_WR_8(node, PKO3_L2_SQ_TOPOLOGY(L2_QUEUE(sc)), val);
6023ec638c9Svisa 
6033ec638c9Svisa 	switch (cfg->cfg_npkolvl) {
6043ec638c9Svisa 	case 3:
6053ec638c9Svisa 		/* L3 */
6063ec638c9Svisa 
6073ec638c9Svisa 		val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
6083ec638c9Svisa 		PKO3_WR_8(node, PKO3_L3_SQ_SCHEDULE(L3_QUEUE(sc)), val);
6093ec638c9Svisa 
6103ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_L3_SQ_TOPOLOGY(L3_QUEUE(sc)));
6113ec638c9Svisa 		val &= ~PKO3_L3_SQ_TOPOLOGY_PRIO_ANCHOR_M;
6123ec638c9Svisa 		val &= ~PKO3_L3_SQ_TOPOLOGY_PARENT_M;
6133ec638c9Svisa 		val &= ~PKO3_L3_SQ_TOPOLOGY_RR_PRIO_M;
6143ec638c9Svisa 		val |= (uint64_t)DESC_QUEUE(sc) <<
6153ec638c9Svisa 		    PKO3_L3_SQ_TOPOLOGY_PRIO_ANCHOR_S;
6163ec638c9Svisa 		val |= (uint64_t)L2_QUEUE(sc) << PKO3_L3_SQ_TOPOLOGY_PARENT_S;
6173ec638c9Svisa 		val |= (uint64_t)0xf << PKO3_L3_SQ_TOPOLOGY_RR_PRIO_S;
6183ec638c9Svisa 		PKO3_WR_8(node, PKO3_L3_SQ_TOPOLOGY(L3_QUEUE(sc)), val);
6193ec638c9Svisa 
6203ec638c9Svisa 		/* Descriptor queue */
6213ec638c9Svisa 
6223ec638c9Svisa 		val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
6233ec638c9Svisa 		PKO3_WR_8(node, PKO3_DQ_SCHEDULE(DESC_QUEUE(sc)), val);
6243ec638c9Svisa 
6253ec638c9Svisa 		val = (uint64_t)L3_QUEUE(sc) << PKO3_DQ_TOPOLOGY_PARENT_S;
6263ec638c9Svisa 		PKO3_WR_8(node, PKO3_DQ_TOPOLOGY(DESC_QUEUE(sc)), val);
6273ec638c9Svisa 
6283ec638c9Svisa 		break;
6293ec638c9Svisa 
6303ec638c9Svisa 	case 5:
6313ec638c9Svisa 		/* L3 */
6323ec638c9Svisa 
6333ec638c9Svisa 		val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
6343ec638c9Svisa 		PKO3_WR_8(node, PKO3_L3_SQ_SCHEDULE(L3_QUEUE(sc)), val);
6353ec638c9Svisa 
6363ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_L3_SQ_TOPOLOGY(L3_QUEUE(sc)));
6373ec638c9Svisa 		val &= ~PKO3_L3_SQ_TOPOLOGY_PRIO_ANCHOR_M;
6383ec638c9Svisa 		val &= ~PKO3_L3_SQ_TOPOLOGY_PARENT_M;
6393ec638c9Svisa 		val &= ~PKO3_L3_SQ_TOPOLOGY_RR_PRIO_M;
6403ec638c9Svisa 		val |= (uint64_t)L4_QUEUE(sc) <<
6413ec638c9Svisa 		    PKO3_L3_SQ_TOPOLOGY_PRIO_ANCHOR_S;
6423ec638c9Svisa 		val |= (uint64_t)L2_QUEUE(sc) << PKO3_L3_SQ_TOPOLOGY_PARENT_S;
6433ec638c9Svisa 		val |= (uint64_t)0xf << PKO3_L3_SQ_TOPOLOGY_RR_PRIO_S;
6443ec638c9Svisa 		PKO3_WR_8(node, PKO3_L3_SQ_TOPOLOGY(L3_QUEUE(sc)), val);
6453ec638c9Svisa 
6463ec638c9Svisa 		/* L4 */
6473ec638c9Svisa 
6483ec638c9Svisa 		val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
6493ec638c9Svisa 		PKO3_WR_8(node, PKO3_L4_SQ_SCHEDULE(L4_QUEUE(sc)), val);
6503ec638c9Svisa 
6513ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_L4_SQ_TOPOLOGY(L4_QUEUE(sc)));
6523ec638c9Svisa 		val &= ~PKO3_L4_SQ_TOPOLOGY_PRIO_ANCHOR_M;
6533ec638c9Svisa 		val &= ~PKO3_L4_SQ_TOPOLOGY_PARENT_M;
6543ec638c9Svisa 		val &= ~PKO3_L4_SQ_TOPOLOGY_RR_PRIO_M;
6553ec638c9Svisa 		val |= (uint64_t)L5_QUEUE(sc) <<
6563ec638c9Svisa 		    PKO3_L4_SQ_TOPOLOGY_PRIO_ANCHOR_S;
6573ec638c9Svisa 		val |= (uint64_t)L3_QUEUE(sc) << PKO3_L4_SQ_TOPOLOGY_PARENT_S;
6583ec638c9Svisa 		val |= (uint64_t)0xf << PKO3_L4_SQ_TOPOLOGY_RR_PRIO_S;
6593ec638c9Svisa 		PKO3_WR_8(node, PKO3_L4_SQ_TOPOLOGY(L4_QUEUE(sc)), val);
6603ec638c9Svisa 
6613ec638c9Svisa 		/* L5 */
6623ec638c9Svisa 
6633ec638c9Svisa 		val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
6643ec638c9Svisa 		PKO3_WR_8(node, PKO3_L5_SQ_SCHEDULE(L5_QUEUE(sc)), val);
6653ec638c9Svisa 
6663ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_L5_SQ_TOPOLOGY(L5_QUEUE(sc)));
6673ec638c9Svisa 		val &= ~PKO3_L5_SQ_TOPOLOGY_PRIO_ANCHOR_M;
6683ec638c9Svisa 		val &= ~PKO3_L5_SQ_TOPOLOGY_PARENT_M;
6693ec638c9Svisa 		val &= ~PKO3_L5_SQ_TOPOLOGY_RR_PRIO_M;
6703ec638c9Svisa 		val |= (uint64_t)DESC_QUEUE(sc) <<
6713ec638c9Svisa 		    PKO3_L5_SQ_TOPOLOGY_PRIO_ANCHOR_S;
6723ec638c9Svisa 		val |= (uint64_t)L4_QUEUE(sc) << PKO3_L5_SQ_TOPOLOGY_PARENT_S;
6733ec638c9Svisa 		val |= (uint64_t)0xf << PKO3_L5_SQ_TOPOLOGY_RR_PRIO_S;
6743ec638c9Svisa 		PKO3_WR_8(node, PKO3_L5_SQ_TOPOLOGY(L5_QUEUE(sc)), val);
6753ec638c9Svisa 
6763ec638c9Svisa 		/* Descriptor queue */
6773ec638c9Svisa 
6783ec638c9Svisa 		val = (uint64_t)0x10 << PKO3_LX_SQ_SCHEDULE_RR_QUANTUM_S;
6793ec638c9Svisa 		PKO3_WR_8(node, PKO3_DQ_SCHEDULE(DESC_QUEUE(sc)), val);
6803ec638c9Svisa 
6813ec638c9Svisa 		val = (uint64_t)L5_QUEUE(sc) << PKO3_DQ_TOPOLOGY_PARENT_S;
6823ec638c9Svisa 		PKO3_WR_8(node, PKO3_DQ_TOPOLOGY(DESC_QUEUE(sc)), val);
6833ec638c9Svisa 
6843ec638c9Svisa 		break;
6853ec638c9Svisa 
6863ec638c9Svisa 	default:
6873ec638c9Svisa 		printf(": unhandled number of PKO levels (%u)\n",
6883ec638c9Svisa 		    cfg->cfg_npkolvl);
6893ec638c9Svisa 		return;
6903ec638c9Svisa 	}
6913ec638c9Svisa 
6923ec638c9Svisa 	/* Descriptor queue, common part */
6933ec638c9Svisa 
6943ec638c9Svisa 	PKO3_WR_8(node, PKO3_DQ_WM_CTL(DESC_QUEUE(sc)), PKO3_DQ_WM_CTL_KIND);
6953ec638c9Svisa 
6963ec638c9Svisa 	val = PKO3_RD_8(node, PKO3_PDM_DQ_MINPAD(DESC_QUEUE(sc)));
6973ec638c9Svisa 	val &= ~PKO3_PDM_DQ_MINPAD_MINPAD;
6983ec638c9Svisa 	PKO3_WR_8(node, PKO3_PDM_DQ_MINPAD(DESC_QUEUE(sc)), val);
6993ec638c9Svisa 
7003ec638c9Svisa 	lut_index = sc->sc_bgxid * 0x40 + lmac * 0x10;
7013ec638c9Svisa 	val = PKO3_LUT_VALID | (L1_QUEUE(sc) << PKO3_LUT_PQ_IDX_S) |
7023ec638c9Svisa 	    (L2_QUEUE(sc) << PKO3_LUT_QUEUE_NUM_S);
7033ec638c9Svisa 	PKO3_WR_8(node, PKO3_LUT(lut_index), val);
7043ec638c9Svisa 
7057284f6b7Svisa #if NKSTAT > 0
7067284f6b7Svisa 	ogx_kstat_attach(sc);
7077284f6b7Svisa #endif
7087284f6b7Svisa 
7093ec638c9Svisa 	fifogrp = &node->node_fifogrp[fgindex];
7103ec638c9Svisa 	fifogrp->fg_speed += sc->sc_link_ops->link_fifo_speed;
7113ec638c9Svisa 
7123ec638c9Svisa 	/*
7133ec638c9Svisa 	 * Defer the rest of the initialization so that FIFO groups
7143ec638c9Svisa 	 * can be configured properly.
7153ec638c9Svisa 	 */
7163ec638c9Svisa 	config_defer(&sc->sc_dev, ogx_defer);
7173ec638c9Svisa }
7183ec638c9Svisa 
7193ec638c9Svisa void
ogx_defer(struct device * dev)7203ec638c9Svisa ogx_defer(struct device *dev)
7213ec638c9Svisa {
7223ec638c9Svisa 	struct ogx_fifo_group *fifogrp;
7233ec638c9Svisa 	struct ogx_softc *sc = (struct ogx_softc *)dev;
7243ec638c9Svisa 	struct ogx_node *node = sc->sc_node;
7253ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
7263ec638c9Svisa 	uint64_t grprate, val;
7273ec638c9Svisa 	int fgindex = PORT_FIFO(sc) >> 2;
7283ec638c9Svisa 
7293ec638c9Svisa 	fifogrp = &node->node_fifogrp[fgindex];
7303ec638c9Svisa 	if (fifogrp->fg_inited == 0) {
7313ec638c9Svisa 		/* Adjust the total rate of the fifo group. */
7323ec638c9Svisa 		grprate = 0;
7333ec638c9Svisa 		while (fifogrp->fg_speed > (6250 << grprate))
7343ec638c9Svisa 			grprate++;
7353ec638c9Svisa 		if (grprate > 5)
7363ec638c9Svisa 			grprate = 5;
7373ec638c9Svisa 
7383ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_PTGF_CFG(fgindex));
7393ec638c9Svisa 		val &= ~PKO3_PTGF_CFG_RATE_M;
7403ec638c9Svisa 		val |= grprate << PKO3_PTGF_CFG_RATE_S;
7413ec638c9Svisa 		PKO3_WR_8(node, PKO3_PTGF_CFG(fgindex), val);
7423ec638c9Svisa 
7433ec638c9Svisa 		fifogrp->fg_inited = 1;
7443ec638c9Svisa 	}
7453ec638c9Svisa 
7463ec638c9Svisa 	if_attach(ifp);
7473ec638c9Svisa 	ether_ifattach(ifp);
7483ec638c9Svisa }
7493ec638c9Svisa 
7503ec638c9Svisa int
ogx_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)7513ec638c9Svisa ogx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
7523ec638c9Svisa {
7533ec638c9Svisa 	struct ogx_softc *sc = ifp->if_softc;
7543ec638c9Svisa 	struct ifreq *ifr = (struct ifreq *)data;
7553ec638c9Svisa 	int error = 0;
7563ec638c9Svisa 	int s;
7573ec638c9Svisa 
7583ec638c9Svisa 	s = splnet();
7593ec638c9Svisa 
7603ec638c9Svisa 	switch (cmd) {
7613ec638c9Svisa 	case SIOCSIFADDR:
7623ec638c9Svisa 		ifp->if_flags |= IFF_UP;
7633ec638c9Svisa 		/* FALLTHROUGH */
7643ec638c9Svisa 
7653ec638c9Svisa 	case SIOCSIFFLAGS:
7663ec638c9Svisa 		if (ISSET(ifp->if_flags, IFF_UP)) {
7673ec638c9Svisa 			if (ISSET(ifp->if_flags, IFF_RUNNING))
7683ec638c9Svisa 				error = ENETRESET;
7693ec638c9Svisa 			else
7703ec638c9Svisa 				error = ogx_init(sc);
7713ec638c9Svisa 		} else {
7723ec638c9Svisa 			if (ISSET(ifp->if_flags, IFF_RUNNING))
7733ec638c9Svisa 				ogx_down(sc);
7743ec638c9Svisa 		}
7753ec638c9Svisa 		break;
7763ec638c9Svisa 
7773ec638c9Svisa 	case SIOCGIFMEDIA:
7783ec638c9Svisa 	case SIOCSIFMEDIA:
7793ec638c9Svisa 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
7803ec638c9Svisa 		break;
7813ec638c9Svisa 
7823ec638c9Svisa 	default:
7833ec638c9Svisa 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
7843ec638c9Svisa 		break;
7853ec638c9Svisa 	}
7863ec638c9Svisa 
7873ec638c9Svisa 	if (error == ENETRESET) {
7883ec638c9Svisa 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
7893ec638c9Svisa 		    (IFF_UP | IFF_RUNNING))
7903ec638c9Svisa 			ogx_iff(sc);
7913ec638c9Svisa 		error = 0;
7923ec638c9Svisa 	}
7933ec638c9Svisa 
7943ec638c9Svisa 	splx(s);
7953ec638c9Svisa 
7963ec638c9Svisa 	return error;
7973ec638c9Svisa }
7983ec638c9Svisa 
7993ec638c9Svisa int
ogx_init(struct ogx_softc * sc)8003ec638c9Svisa ogx_init(struct ogx_softc *sc)
8013ec638c9Svisa {
8023ec638c9Svisa 	struct ogx_node *node = sc->sc_node;
8033ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
8043ec638c9Svisa 	uint64_t op;
8053ec638c9Svisa 	int error;
8063ec638c9Svisa 
8073ec638c9Svisa 	error = ogx_node_load_firmware(node);
8083ec638c9Svisa 	if (error != 0)
8093ec638c9Svisa 		return error;
8103ec638c9Svisa 
8117284f6b7Svisa #if NKSTAT > 0
8127284f6b7Svisa 	ogx_kstat_start(sc);
8137284f6b7Svisa #endif
8147284f6b7Svisa 
8153ec638c9Svisa 	ogx_iff(sc);
8163ec638c9Svisa 
8173ec638c9Svisa 	SSO_WR_8(sc->sc_node, SSO_GRP_INT_THR(PORT_GROUP_RX(sc)), 1);
8183ec638c9Svisa 	SSO_WR_8(sc->sc_node, SSO_GRP_INT_THR(PORT_GROUP_TX(sc)), 1);
8193ec638c9Svisa 
8203ec638c9Svisa 	sc->sc_link_ops->link_init(sc);
8213ec638c9Svisa 	if (!LIST_EMPTY(&sc->sc_mii.mii_phys))
8223ec638c9Svisa 		mii_mediachg(&sc->sc_mii);
8233ec638c9Svisa 
8243ec638c9Svisa 	/* Open the descriptor queue. */
8253ec638c9Svisa 	op = PKO3_LD_IO | PKO3_LD_DID;
8263ec638c9Svisa 	op |= node->node_id << PKO3_LD_NODE_S;
8273ec638c9Svisa 	op |= PKO3_DQOP_OPEN << PKO3_LD_OP_S;
8283ec638c9Svisa 	op |= DESC_QUEUE(sc) << PKO3_LD_DQ_S;
8293ec638c9Svisa 	(void)octeon_xkphys_read_8(op);
8303ec638c9Svisa 
8313ec638c9Svisa 	ifp->if_flags |= IFF_RUNNING;
8323ec638c9Svisa 	ifq_restart(&ifp->if_snd);
8333ec638c9Svisa 
8343ec638c9Svisa 	timeout_add(&sc->sc_rxrefill, 1);
8353ec638c9Svisa 	timeout_add_sec(&sc->sc_tick, 1);
8363ec638c9Svisa 
8373ec638c9Svisa 	return 0;
8383ec638c9Svisa }
8393ec638c9Svisa 
8403ec638c9Svisa void
ogx_down(struct ogx_softc * sc)8413ec638c9Svisa ogx_down(struct ogx_softc *sc)
8423ec638c9Svisa {
8433ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
8443ec638c9Svisa 	struct ogx_node *node = sc->sc_node;
8453ec638c9Svisa 	uint64_t op, val;
8463ec638c9Svisa 	unsigned int nused;
8473ec638c9Svisa 
8483ec638c9Svisa 	CLR(ifp->if_flags, IFF_RUNNING);
8493ec638c9Svisa 
8503ec638c9Svisa 	/* Drain the descriptor queue. */
8513ec638c9Svisa 	val = PKO3_LX_SQ_SW_XOFF_DRAIN;
8523ec638c9Svisa 	val |= PKO3_LX_SQ_SW_XOFF_DRAIN_NULL_LINK;
8533ec638c9Svisa 	PKO3_WR_8(node, PKO3_DQ_SW_XOFF(DESC_QUEUE(sc)), val);
8543ec638c9Svisa 	(void)PKO3_RD_8(node, PKO3_DQ_SW_XOFF(DESC_QUEUE(sc)));
8553ec638c9Svisa 
8563ec638c9Svisa 	delay(1000);
8573ec638c9Svisa 
8583ec638c9Svisa 	/* Finish the drain operation. */
8593ec638c9Svisa 	PKO3_WR_8(node, PKO3_DQ_SW_XOFF(DESC_QUEUE(sc)), 0);
8603ec638c9Svisa 	(void)PKO3_RD_8(node, PKO3_DQ_SW_XOFF(DESC_QUEUE(sc)));
8613ec638c9Svisa 
8623ec638c9Svisa 	/* Close the descriptor queue. */
8633ec638c9Svisa 	op = PKO3_LD_IO | PKO3_LD_DID;
8643ec638c9Svisa 	op |= node->node_id << PKO3_LD_NODE_S;
8653ec638c9Svisa 	op |= PKO3_DQOP_CLOSE << PKO3_LD_OP_S;
8663ec638c9Svisa 	op |= DESC_QUEUE(sc) << PKO3_LD_DQ_S;
8673ec638c9Svisa 	(void)octeon_xkphys_read_8(op);
8683ec638c9Svisa 
8693ec638c9Svisa 	/* Disable data transfer. */
8703ec638c9Svisa 	val = PORT_RD_8(sc, BGX_CMR_CONFIG);
8713ec638c9Svisa 	val &= ~BGX_CMR_CONFIG_DATA_PKT_RX_EN;
8723ec638c9Svisa 	val &= ~BGX_CMR_CONFIG_DATA_PKT_TX_EN;
8733ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_CONFIG, val);
8743ec638c9Svisa 	(void)PORT_RD_8(sc, BGX_CMR_CONFIG);
8753ec638c9Svisa 
8763ec638c9Svisa 	if (!LIST_EMPTY(&sc->sc_mii.mii_phys))
8773ec638c9Svisa 		mii_down(&sc->sc_mii);
8783ec638c9Svisa 	sc->sc_link_ops->link_down(sc);
8793ec638c9Svisa 
8803ec638c9Svisa 	ifq_clr_oactive(&ifp->if_snd);
8813ec638c9Svisa 	ifq_barrier(&ifp->if_snd);
8823ec638c9Svisa 
8833ec638c9Svisa 	timeout_del_barrier(&sc->sc_rxrefill);
8843ec638c9Svisa 	timeout_del_barrier(&sc->sc_tick);
8853ec638c9Svisa 
8867284f6b7Svisa #if NKSTAT > 0
8877284f6b7Svisa 	ogx_kstat_stop(sc);
8887284f6b7Svisa #endif
8897284f6b7Svisa 
8903ec638c9Svisa 	nused = ogx_unload_mbufs(sc);
8913ec638c9Svisa 	atomic_add_int(&sc->sc_rxused, nused);
8923ec638c9Svisa }
8933ec638c9Svisa 
8943ec638c9Svisa void
ogx_iff(struct ogx_softc * sc)8953ec638c9Svisa ogx_iff(struct ogx_softc *sc)
8963ec638c9Svisa {
8973ec638c9Svisa 	struct arpcom *ac = &sc->sc_ac;
8983ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
8993ec638c9Svisa 	struct ether_multi *enm;
9003ec638c9Svisa 	struct ether_multistep step;
9013ec638c9Svisa 	uint64_t rx_adr_ctl;
9023ec638c9Svisa 	uint64_t val;
9033ec638c9Svisa 	int cidx, clast, i;
9043ec638c9Svisa 
9053ec638c9Svisa 	rx_adr_ctl = PORT_RD_8(sc, BGX_CMR_RX_ADR_CTL);
9063ec638c9Svisa 	rx_adr_ctl |= BGX_CMR_RX_ADR_CTL_BCST_ACCEPT;
9073ec638c9Svisa 	rx_adr_ctl |= BGX_CMR_RX_ADR_CTL_CAM_ACCEPT;
9083ec638c9Svisa 	rx_adr_ctl &= ~BGX_CMR_RX_ADR_CTL_MCST_MODE_ALL;
9093ec638c9Svisa 	ifp->if_flags &= ~IFF_ALLMULTI;
9103ec638c9Svisa 
9113ec638c9Svisa 	if (ISSET(ifp->if_flags, IFF_PROMISC)) {
9123ec638c9Svisa 		ifp->if_flags |= IFF_ALLMULTI;
9133ec638c9Svisa 		rx_adr_ctl &= ~BGX_CMR_RX_ADR_CTL_CAM_ACCEPT;
9143ec638c9Svisa 		rx_adr_ctl |= BGX_CMR_RX_ADR_CTL_MCST_MODE_ALL;
9153ec638c9Svisa 	} else if (ac->ac_multirangecnt > 0 || ac->ac_multicnt >= OGX_NCAM) {
9163ec638c9Svisa 		ifp->if_flags |= IFF_ALLMULTI;
9173ec638c9Svisa 		rx_adr_ctl |= BGX_CMR_RX_ADR_CTL_MCST_MODE_ALL;
9183ec638c9Svisa 	} else {
9193ec638c9Svisa 		rx_adr_ctl |= BGX_CMR_RX_ADR_CTL_MCST_MODE_CAM;
9203ec638c9Svisa 	}
9213ec638c9Svisa 
9223ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_RX_ADR_CTL, rx_adr_ctl);
9233ec638c9Svisa 
9243ec638c9Svisa 	cidx = sc->sc_lmacid * OGX_NCAM;
9253ec638c9Svisa 	clast = (sc->sc_lmacid + 1) * OGX_NCAM;
9263ec638c9Svisa 
9273ec638c9Svisa 	if (!ISSET(ifp->if_flags, IFF_PROMISC)) {
9283ec638c9Svisa 		val = BGX_CMR_RX_ADR_CAM_EN | ((uint64_t)sc->sc_lmacid
9293ec638c9Svisa 		    << BGX_CMR_RX_ADR_CAM_ID_S);
9303ec638c9Svisa 		for (i = 0; i < ETHER_ADDR_LEN; i++) {
9313ec638c9Svisa 			val |= (uint64_t)ac->ac_enaddr[i] <<
9323ec638c9Svisa 			    ((ETHER_ADDR_LEN - 1 - i) * 8);
9333ec638c9Svisa 		}
9343ec638c9Svisa 		NEXUS_WR_8(sc, BGX_CMR_RX_ADR_CAM(cidx++), val);
9353ec638c9Svisa 	}
9363ec638c9Svisa 
9373ec638c9Svisa 	if (!ISSET(ifp->if_flags, IFF_ALLMULTI)) {
9383ec638c9Svisa 		ETHER_FIRST_MULTI(step, ac, enm);
9393ec638c9Svisa 		while (enm != NULL) {
9403ec638c9Svisa 			val = BGX_CMR_RX_ADR_CAM_EN | ((uint64_t)sc->sc_lmacid
9413ec638c9Svisa 			    << BGX_CMR_RX_ADR_CAM_ID_S);
9423ec638c9Svisa 			for (i = 0; i < ETHER_ADDR_LEN; i++)
9433ec638c9Svisa 				val |= (uint64_t)enm->enm_addrlo[i] << (i * 8);
9443ec638c9Svisa 			KASSERT(cidx < clast);
9453ec638c9Svisa 			NEXUS_WR_8(sc, BGX_CMR_RX_ADR_CAM(cidx++), val);
9463ec638c9Svisa 
9473ec638c9Svisa 			ETHER_NEXT_MULTI(step, enm);
9483ec638c9Svisa 		}
9493ec638c9Svisa 	}
9503ec638c9Svisa 
9513ec638c9Svisa 	/* Disable any remaining address CAM entries. */
9523ec638c9Svisa 	while (cidx < clast)
9533ec638c9Svisa 		NEXUS_WR_8(sc, BGX_CMR_RX_ADR_CAM(cidx++), 0);
9543ec638c9Svisa }
9553ec638c9Svisa 
9563ec638c9Svisa static inline uint64_t *
ogx_get_work(struct ogx_node * node,uint32_t group)9573ec638c9Svisa ogx_get_work(struct ogx_node *node, uint32_t group)
9583ec638c9Svisa {
9593ec638c9Svisa 	uint64_t op, resp;
9603ec638c9Svisa 
9613ec638c9Svisa 	op = SSO_LD_IO | SSO_LD_DID;
9623ec638c9Svisa 	op |= node->node_id << SSO_LD_NODE_S;
9633ec638c9Svisa 	op |= SSO_LD_GROUPED | (group << SSO_LD_INDEX_S);
9643ec638c9Svisa 	resp = octeon_xkphys_read_8(op);
9653ec638c9Svisa 
9663ec638c9Svisa 	if (resp & SSO_LD_RTN_NO_WORK)
9673ec638c9Svisa 		return NULL;
9683ec638c9Svisa 
9693ec638c9Svisa 	return (uint64_t *)PHYS_TO_XKPHYS(resp & SSO_LD_RTN_ADDR_M, CCA_CACHED);
9703ec638c9Svisa }
9713ec638c9Svisa 
9723ec638c9Svisa static inline struct mbuf *
ogx_extract_mbuf(struct ogx_softc * sc,paddr_t pktbuf)9733ec638c9Svisa ogx_extract_mbuf(struct ogx_softc *sc, paddr_t pktbuf)
9743ec638c9Svisa {
9753ec638c9Svisa 	struct mbuf *m, **pm;
9763ec638c9Svisa 
9773ec638c9Svisa 	pm = (struct mbuf **)PHYS_TO_XKPHYS(pktbuf, CCA_CACHED) - 1;
9783ec638c9Svisa 	m = *pm;
9793ec638c9Svisa 	*pm = NULL;
9803ec638c9Svisa 	KASSERTMSG((paddr_t)m->m_pkthdr.ph_cookie == pktbuf,
9813ec638c9Svisa 	    "%s: corrupt packet pool, mbuf cookie %p != pktbuf %p",
9823ec638c9Svisa 	    DEVNAME(sc), m->m_pkthdr.ph_cookie, (void *)pktbuf);
9833ec638c9Svisa 	m->m_pkthdr.ph_cookie = NULL;
9843ec638c9Svisa 	return m;
9853ec638c9Svisa }
9863ec638c9Svisa 
9873ec638c9Svisa void
ogx_rxrefill(void * arg)9883ec638c9Svisa ogx_rxrefill(void *arg)
9893ec638c9Svisa {
9903ec638c9Svisa 	struct ogx_softc *sc = arg;
9913ec638c9Svisa 	unsigned int to_alloc;
9923ec638c9Svisa 
9933ec638c9Svisa 	if (sc->sc_rxused > 0) {
9943ec638c9Svisa 		to_alloc = atomic_swap_uint(&sc->sc_rxused, 0);
9953ec638c9Svisa 		to_alloc = ogx_load_mbufs(sc, to_alloc);
9963ec638c9Svisa 		if (to_alloc > 0) {
9973ec638c9Svisa 			atomic_add_int(&sc->sc_rxused, to_alloc);
9983ec638c9Svisa 			timeout_add(&sc->sc_rxrefill, 1);
9993ec638c9Svisa 		}
10003ec638c9Svisa 	}
10013ec638c9Svisa }
10023ec638c9Svisa 
10033ec638c9Svisa void
ogx_tick(void * arg)10043ec638c9Svisa ogx_tick(void *arg)
10053ec638c9Svisa {
10063ec638c9Svisa 	struct ogx_softc *sc = arg;
10073ec638c9Svisa 	int s;
10083ec638c9Svisa 
10093ec638c9Svisa 	s = splnet();
10103ec638c9Svisa 	if (!LIST_EMPTY(&sc->sc_mii.mii_phys)) {
10113ec638c9Svisa 		mii_tick(&sc->sc_mii);
10123ec638c9Svisa 	} else {
10133ec638c9Svisa 		if (sc->sc_link_ops->link_status(sc))
10143ec638c9Svisa 			sc->sc_link_ops->link_change(sc);
10153ec638c9Svisa 	}
10163ec638c9Svisa 	splx(s);
10173ec638c9Svisa 
10183ec638c9Svisa 	timeout_add_sec(&sc->sc_tick, 1);
10193ec638c9Svisa }
10203ec638c9Svisa 
10213ec638c9Svisa int
ogx_rxintr(void * arg)10223ec638c9Svisa ogx_rxintr(void *arg)
10233ec638c9Svisa {
10243ec638c9Svisa 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
10253ec638c9Svisa 	struct mbuf *m, *m0, *mprev;
10263ec638c9Svisa 	struct ogx_softc *sc = arg;
10273ec638c9Svisa 	struct ogx_node *node = sc->sc_node;
10283ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
10293ec638c9Svisa 	paddr_t pktbuf, pktdata;
10303ec638c9Svisa 	uint64_t *work;
10313ec638c9Svisa 	uint64_t nsegs;
10323ec638c9Svisa 	unsigned int rxused = 0;
10333ec638c9Svisa 
10343ec638c9Svisa 	/* Acknowledge the interrupt. */
10353ec638c9Svisa 	SSO_WR_8(node, SSO_GRP_INT(PORT_GROUP_RX(sc)), SSO_GRP_INT_EXE_INT);
10363ec638c9Svisa 
10373ec638c9Svisa 	for (;;) {
10383ec638c9Svisa 		uint64_t errcode, errlevel;
10393ec638c9Svisa 		uint64_t word3;
10403ec638c9Svisa 		size_t pktlen, left;
10413ec638c9Svisa #ifdef DIAGNOSTIC
10423ec638c9Svisa 		unsigned int pkind;
10433ec638c9Svisa #endif
10443ec638c9Svisa 
10453ec638c9Svisa 		work = ogx_get_work(sc->sc_node, PORT_GROUP_RX(sc));
10463ec638c9Svisa 		if (work == NULL)
10473ec638c9Svisa 			break;
10483ec638c9Svisa 
10493ec638c9Svisa #ifdef DIAGNOSTIC
10503ec638c9Svisa 		pkind = (work[0] & PKI_WORD0_PKIND_M) >> PKI_WORD0_PKIND_S;
10513ec638c9Svisa 		if (__predict_false(pkind != PORT_PKIND(sc))) {
10523ec638c9Svisa 			printf("%s: unexpected pkind %u, should be %u\n",
10533ec638c9Svisa 			    DEVNAME(sc), pkind, PORT_PKIND(sc));
10543ec638c9Svisa 			goto wqe_error;
10553ec638c9Svisa 		}
10563ec638c9Svisa #endif
10573ec638c9Svisa 
10583ec638c9Svisa 		nsegs = (work[0] & PKI_WORD0_BUFS_M) >> PKI_WORD0_BUFS_S;
10593ec638c9Svisa 		word3 = work[3];
10603ec638c9Svisa 
10613ec638c9Svisa 		errlevel = (work[2] & PKI_WORD2_ERR_LEVEL_M) >>
10623ec638c9Svisa 		    PKI_WORD2_ERR_LEVEL_S;
10633ec638c9Svisa 		errcode = (work[2] & PKI_WORD2_ERR_CODE_M) >>
10643ec638c9Svisa 		    PKI_WORD2_ERR_CODE_S;
10653ec638c9Svisa 		if (__predict_false(errlevel <= 1 && errcode != 0)) {
10663ec638c9Svisa 			ifp->if_ierrors++;
10673ec638c9Svisa 			goto drop;
10683ec638c9Svisa 		}
10693ec638c9Svisa 
10703ec638c9Svisa 		KASSERT(nsegs > 0);
10713ec638c9Svisa 		rxused += nsegs;
10723ec638c9Svisa 
10733ec638c9Svisa 		pktlen = (work[1] & PKI_WORD1_LEN_M) >> PKI_WORD1_LEN_S;
10743ec638c9Svisa 		left = pktlen;
10753ec638c9Svisa 
10763ec638c9Svisa 		m0 = NULL;
10773ec638c9Svisa 		mprev = NULL;
10783ec638c9Svisa 		while (nsegs-- > 0) {
10793ec638c9Svisa 			size_t size;
10803ec638c9Svisa 
10813ec638c9Svisa 			pktdata = (word3 & PKI_WORD3_ADDR_M) >>
10823ec638c9Svisa 			    PKI_WORD3_ADDR_S;
10833ec638c9Svisa 			pktbuf = pktdata & ~(CACHELINESIZE - 1);
10843ec638c9Svisa 			size = (word3 & PKI_WORD3_SIZE_M) >> PKI_WORD3_SIZE_S;
10853ec638c9Svisa 			if (size > left)
10863ec638c9Svisa 				size = left;
10873ec638c9Svisa 
10883ec638c9Svisa 			m = ogx_extract_mbuf(sc, pktbuf);
10893ec638c9Svisa 			m->m_data += (pktdata - pktbuf) & (CACHELINESIZE - 1);
10903ec638c9Svisa 			m->m_len = size;
10913ec638c9Svisa 			left -= size;
10923ec638c9Svisa 
10933ec638c9Svisa 			/* pktdata can be unaligned. */
10943ec638c9Svisa 			memcpy(&word3, (void *)PHYS_TO_XKPHYS(pktdata -
10953ec638c9Svisa 			    sizeof(uint64_t), CCA_CACHED), sizeof(uint64_t));
10963ec638c9Svisa 
10973ec638c9Svisa 			if (m0 == NULL) {
10983ec638c9Svisa 				m0 = m;
10993ec638c9Svisa 			} else {
11003ec638c9Svisa 				m->m_flags &= ~M_PKTHDR;
11013ec638c9Svisa 				mprev->m_next = m;
11023ec638c9Svisa 			}
11033ec638c9Svisa 			mprev = m;
11043ec638c9Svisa 		}
11053ec638c9Svisa 
11063ec638c9Svisa 		m0->m_pkthdr.len = pktlen;
11073ec638c9Svisa 		ml_enqueue(&ml, m0);
11083ec638c9Svisa 
11093ec638c9Svisa 		continue;
11103ec638c9Svisa 
11113ec638c9Svisa drop:
11123ec638c9Svisa 		/* Return the buffers back to the pool. */
11133ec638c9Svisa 		while (nsegs-- > 0) {
11143ec638c9Svisa 			pktdata = (word3 & PKI_WORD3_ADDR_M) >>
11153ec638c9Svisa 			    PKI_WORD3_ADDR_S;
11163ec638c9Svisa 			pktbuf = pktdata & ~(CACHELINESIZE - 1);
11173ec638c9Svisa 			/* pktdata can be unaligned. */
11183ec638c9Svisa 			memcpy(&word3, (void *)PHYS_TO_XKPHYS(pktdata -
11193ec638c9Svisa 			    sizeof(uint64_t), CCA_CACHED), sizeof(uint64_t));
11203ec638c9Svisa 			ogx_fpa3_free(&sc->sc_pkt_aura, pktbuf);
11213ec638c9Svisa 		}
11223ec638c9Svisa 	}
11233ec638c9Svisa 
11243ec638c9Svisa 	if_input(ifp, &ml);
11253ec638c9Svisa 
11263ec638c9Svisa 	rxused = ogx_load_mbufs(sc, rxused);
11273ec638c9Svisa 	if (rxused != 0) {
11283ec638c9Svisa 		atomic_add_int(&sc->sc_rxused, rxused);
11293ec638c9Svisa 		timeout_add(&sc->sc_rxrefill, 1);
11303ec638c9Svisa 	}
11313ec638c9Svisa 
11323ec638c9Svisa 	return 1;
11333ec638c9Svisa 
11343ec638c9Svisa #ifdef DIAGNOSTIC
11353ec638c9Svisa wqe_error:
11363ec638c9Svisa 	printf("work0: %016llx\n", work[0]);
11373ec638c9Svisa 	printf("work1: %016llx\n", work[1]);
11383ec638c9Svisa 	printf("work2: %016llx\n", work[2]);
11393ec638c9Svisa 	printf("work3: %016llx\n", work[3]);
11403ec638c9Svisa 	printf("work4: %016llx\n", work[4]);
11413ec638c9Svisa 	panic("%s: %s: wqe error", DEVNAME(sc), __func__);
11423ec638c9Svisa #endif
11433ec638c9Svisa }
11443ec638c9Svisa 
11453ec638c9Svisa int
ogx_txintr(void * arg)11463ec638c9Svisa ogx_txintr(void *arg)
11473ec638c9Svisa {
11483ec638c9Svisa 	struct ogx_softc *sc = arg;
11493ec638c9Svisa 	struct ogx_node *node = sc->sc_node;
11503ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
11513ec638c9Svisa 	struct mbuf *m;
11523ec638c9Svisa 	uint64_t *work;
11533ec638c9Svisa 	unsigned int nfreed = 0;
11543ec638c9Svisa 
11553ec638c9Svisa 	/* Acknowledge the interrupt. */
11563ec638c9Svisa 	SSO_WR_8(node, SSO_GRP_INT(PORT_GROUP_TX(sc)), SSO_GRP_INT_EXE_INT);
11573ec638c9Svisa 
11583ec638c9Svisa 	for (;;) {
11593ec638c9Svisa 		work = ogx_get_work(node, PORT_GROUP_TX(sc));
11603ec638c9Svisa 		if (work == NULL)
11613ec638c9Svisa 			break;
11623ec638c9Svisa 
11633ec638c9Svisa 		/*
11643ec638c9Svisa 		 * work points to ph_cookie via the xkphys segment.
11653ec638c9Svisa 		 * ph_cookie contains the original mbuf pointer.
11663ec638c9Svisa 		 */
11673ec638c9Svisa 		m = *(struct mbuf **)work;
11683ec638c9Svisa 		KASSERT(m->m_pkthdr.ph_ifidx == (u_int)(uintptr_t)sc);
11693ec638c9Svisa 		m->m_pkthdr.ph_ifidx = 0;
11703ec638c9Svisa 		m_freem(m);
11713ec638c9Svisa 		nfreed++;
11723ec638c9Svisa 	}
11733ec638c9Svisa 
11743ec638c9Svisa 	if (nfreed > 0 && atomic_add_int_nv(&sc->sc_txfree, nfreed) == nfreed)
11753ec638c9Svisa 		ifq_restart(&ifp->if_snd);
11763ec638c9Svisa 
11773ec638c9Svisa 	return 1;
11783ec638c9Svisa }
11793ec638c9Svisa 
11803ec638c9Svisa unsigned int
ogx_load_mbufs(struct ogx_softc * sc,unsigned int n)11813ec638c9Svisa ogx_load_mbufs(struct ogx_softc *sc, unsigned int n)
11823ec638c9Svisa {
11833ec638c9Svisa 	struct mbuf *m;
11843ec638c9Svisa 	paddr_t pktbuf;
11853ec638c9Svisa 
11863ec638c9Svisa 	for ( ; n > 0; n--) {
1187471f2571Sjan 		m = MCLGETL(NULL, M_NOWAIT, MCLBYTES);
11883ec638c9Svisa 		if (m == NULL)
11893ec638c9Svisa 			break;
11903ec638c9Svisa 
11913ec638c9Svisa 		m->m_data = (void *)(((vaddr_t)m->m_data + CACHELINESIZE) &
11923ec638c9Svisa 		    ~(CACHELINESIZE - 1));
11933ec638c9Svisa 		((struct mbuf **)m->m_data)[-1] = m;
11943ec638c9Svisa 
11953ec638c9Svisa 		pktbuf = KVTOPHYS(m->m_data);
11963ec638c9Svisa 		m->m_pkthdr.ph_cookie = (void *)pktbuf;
11973ec638c9Svisa 		ogx_fpa3_free(&sc->sc_pkt_aura, pktbuf);
11983ec638c9Svisa 	}
11993ec638c9Svisa 	return n;
12003ec638c9Svisa }
12013ec638c9Svisa 
12023ec638c9Svisa unsigned int
ogx_unload_mbufs(struct ogx_softc * sc)12033ec638c9Svisa ogx_unload_mbufs(struct ogx_softc *sc)
12043ec638c9Svisa {
12053ec638c9Svisa 	struct mbuf *m;
12063ec638c9Svisa 	paddr_t pktbuf;
12073ec638c9Svisa 	unsigned int n = 0;
12083ec638c9Svisa 
12093ec638c9Svisa 	for (;;) {
12103ec638c9Svisa 		pktbuf = ogx_fpa3_alloc(&sc->sc_pkt_aura);
12113ec638c9Svisa 		if (pktbuf == 0)
12123ec638c9Svisa 			break;
12133ec638c9Svisa 		m = ogx_extract_mbuf(sc, pktbuf);
12143ec638c9Svisa 		m_freem(m);
12153ec638c9Svisa 		n++;
12163ec638c9Svisa 	}
12173ec638c9Svisa 	return n;
12183ec638c9Svisa }
12193ec638c9Svisa 
12203ec638c9Svisa void
ogx_start(struct ifqueue * ifq)12213ec638c9Svisa ogx_start(struct ifqueue *ifq)
12223ec638c9Svisa {
12233ec638c9Svisa 	struct ifnet *ifp = ifq->ifq_if;
12243ec638c9Svisa 	struct ogx_softc *sc = ifp->if_softc;
12253ec638c9Svisa 	struct mbuf *m;
12263ec638c9Svisa 	unsigned int txfree, txused;
12273ec638c9Svisa 
12283ec638c9Svisa 	txfree = READ_ONCE(sc->sc_txfree);
12293ec638c9Svisa 	txused = 0;
12303ec638c9Svisa 
12313ec638c9Svisa 	while (txused < txfree) {
12323ec638c9Svisa 		m = ifq_dequeue(ifq);
12333ec638c9Svisa 		if (m == NULL)
12343ec638c9Svisa 			break;
12353ec638c9Svisa 
12363ec638c9Svisa #if NBPFILTER > 0
12373ec638c9Svisa 		if (ifp->if_bpf != NULL)
12383ec638c9Svisa 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
12393ec638c9Svisa #endif
12403ec638c9Svisa 
12413ec638c9Svisa 		if (ogx_send_mbuf(sc, m) != 0) {
12423ec638c9Svisa 			m_freem(m);
12433ec638c9Svisa 			ifp->if_oerrors++;
12443ec638c9Svisa 			continue;
12453ec638c9Svisa 		}
12463ec638c9Svisa 		txused++;
12473ec638c9Svisa 	}
12483ec638c9Svisa 
12493ec638c9Svisa 	if (atomic_sub_int_nv(&sc->sc_txfree, txused) == 0)
12503ec638c9Svisa 		ifq_set_oactive(ifq);
12513ec638c9Svisa }
12523ec638c9Svisa 
12533ec638c9Svisa int
ogx_send_mbuf(struct ogx_softc * sc,struct mbuf * m0)12543ec638c9Svisa ogx_send_mbuf(struct ogx_softc *sc, struct mbuf *m0)
12553ec638c9Svisa {
12563a44e889Svisa 	struct ether_header *eh;
12573ec638c9Svisa 	struct mbuf *m;
12583a44e889Svisa 	uint64_t ehdrlen, hdr, scroff, word;
12593ec638c9Svisa 	unsigned int nfrags;
12603ec638c9Svisa 
12613ec638c9Svisa 	/* Save original pointer for freeing after transmission. */
12623ec638c9Svisa 	m0->m_pkthdr.ph_cookie = m0;
12633ec638c9Svisa 	/* Add a tag for sanity checking. */
12643ec638c9Svisa 	m0->m_pkthdr.ph_ifidx = (u_int)(uintptr_t)sc;
12653ec638c9Svisa 
12663a44e889Svisa 	hdr = PKO3_SEND_HDR_DF;
12673a44e889Svisa 	hdr |= m0->m_pkthdr.len << PKO3_SEND_HDR_TOTAL_S;
12683a44e889Svisa 
12693a44e889Svisa 	if (m0->m_pkthdr.csum_flags &
12703a44e889Svisa 	    (M_IPV4_CSUM_OUT | M_TCP_CSUM_OUT | M_UDP_CSUM_OUT)) {
12713a44e889Svisa 		eh = mtod(m0, struct ether_header *);
12723a44e889Svisa 		ehdrlen = ETHER_HDR_LEN;
12733a44e889Svisa 
12743a44e889Svisa 		switch (ntohs(eh->ether_type)) {
12753a44e889Svisa 		case ETHERTYPE_IP:
12763a44e889Svisa 			hdr |= ehdrlen << PKO3_SEND_HDR_L3PTR_S;
12773a44e889Svisa 			hdr |= (ehdrlen + sizeof(struct ip)) <<
12783a44e889Svisa 			    PKO3_SEND_HDR_L4PTR_S;
12793a44e889Svisa 			break;
12803a44e889Svisa 		case ETHERTYPE_IPV6:
12813a44e889Svisa 			hdr |= ehdrlen << PKO3_SEND_HDR_L3PTR_S;
12823a44e889Svisa 			hdr |= (ehdrlen + sizeof(struct ip6_hdr)) <<
12833a44e889Svisa 			    PKO3_SEND_HDR_L4PTR_S;
12843a44e889Svisa 			break;
12853a44e889Svisa 		default:
12863a44e889Svisa 			break;
12873a44e889Svisa 		}
12883a44e889Svisa 
12893a44e889Svisa 		if (m0->m_pkthdr.csum_flags & M_IPV4_CSUM_OUT)
12903a44e889Svisa 			hdr |= PKO3_SEND_HDR_CKL3;
12913a44e889Svisa 		if (m0->m_pkthdr.csum_flags & M_TCP_CSUM_OUT)
12923a44e889Svisa 			hdr |= PKO3_SEND_HDR_CKL4_TCP;
12933a44e889Svisa 		if (m0->m_pkthdr.csum_flags & M_UDP_CSUM_OUT)
12943a44e889Svisa 			hdr |= PKO3_SEND_HDR_CKL4_UDP;
12953a44e889Svisa 	}
12963a44e889Svisa 
12973ec638c9Svisa 	/* Flush pending writes before packet submission. */
12983ec638c9Svisa 	octeon_syncw();
12993ec638c9Svisa 
13003ec638c9Svisa 	/* Block until any previous LMTDMA request has been processed. */
13013ec638c9Svisa 	octeon_synciobdma();
13023ec638c9Svisa 
13033ec638c9Svisa 	/* Get the LMTDMA region offset in the scratchpad. */
13043ec638c9Svisa 	scroff = 2 * 0x80;
13053ec638c9Svisa 
13063a44e889Svisa 	octeon_cvmseg_write_8(scroff, hdr);
13073a44e889Svisa 	scroff += sizeof(hdr);
13083ec638c9Svisa 
13093ec638c9Svisa 	for (m = m0, nfrags = 0; m != NULL && nfrags < 13;
13103ec638c9Svisa 	    m = m->m_next, nfrags++) {
13113ec638c9Svisa 		word = PKO3_SUBDC3_SEND_GATHER << PKO3_SUBC_BUF_PTR_SUBDC3_S;
13123ec638c9Svisa 		word |= KVTOPHYS(m->m_data) << PKO3_SUBC_BUF_PTR_ADDR_S;
13133ec638c9Svisa 		word |= (uint64_t)m->m_len << PKO3_SUBC_BUF_PTR_SIZE_S;
13143ec638c9Svisa 		octeon_cvmseg_write_8(scroff, word);
13153ec638c9Svisa 		scroff += sizeof(word);
13163ec638c9Svisa 	}
13173ec638c9Svisa 
13183ec638c9Svisa 	if (m != NULL) {
13193ec638c9Svisa 		if (m_defrag(m0, M_DONTWAIT) != 0)
13203ec638c9Svisa 			return ENOMEM;
13213ec638c9Svisa 
13223ec638c9Svisa 		/* Discard previously set fragments. */
13233ec638c9Svisa 		scroff -= sizeof(word) * nfrags;
13243ec638c9Svisa 
13253ec638c9Svisa 		word = PKO3_SUBDC3_SEND_GATHER << PKO3_SUBC_BUF_PTR_SUBDC3_S;
13263ec638c9Svisa 		word |= KVTOPHYS(m0->m_data) << PKO3_SUBC_BUF_PTR_ADDR_S;
13273ec638c9Svisa 		word |= (uint64_t)m0->m_len << PKO3_SUBC_BUF_PTR_SIZE_S;
13283ec638c9Svisa 		octeon_cvmseg_write_8(scroff, word);
13293ec638c9Svisa 		scroff += sizeof(word);
13303ec638c9Svisa 	}
13313ec638c9Svisa 
13323ec638c9Svisa 	/* Send work when ready to free the mbuf. */
13333ec638c9Svisa 	word = PKO3_SEND_WORK_CODE << PKO3_SEND_SUBDC4_CODE_S;
13343ec638c9Svisa 	word |= KVTOPHYS(&m0->m_pkthdr.ph_cookie) << PKO3_SEND_WORK_ADDR_S;
13353ec638c9Svisa 	word |= (uint64_t)PORT_GROUP_TX(sc) << PKO3_SEND_WORK_GRP_S;
13363ec638c9Svisa 	word |= 2ULL << PKO3_SEND_WORK_TT_S;
13373ec638c9Svisa 	octeon_cvmseg_write_8(scroff, word);
13383ec638c9Svisa 	scroff += sizeof(word);
13393ec638c9Svisa 
13403ec638c9Svisa 	/* Submit the command. */
13413ec638c9Svisa 	word = PKO3_LMTDMA_DID;
13423ec638c9Svisa 	word |= ((2ULL * 0x80) >> 3) << PKO3_LMTDMA_SCRADDR_S;
13433ec638c9Svisa 	word |= 1ULL << PKO3_LMTDMA_RTNLEN_S;
13443ec638c9Svisa 	word |= DESC_QUEUE(sc) << PKO3_LMTDMA_DQ_S;
13453ec638c9Svisa 	octeon_lmtdma_write_8((scroff - 8) & 0x78, word);
13463ec638c9Svisa 
13473ec638c9Svisa 	return 0;
13483ec638c9Svisa }
13493ec638c9Svisa 
13503ec638c9Svisa int
ogx_media_change(struct ifnet * ifp)13513ec638c9Svisa ogx_media_change(struct ifnet *ifp)
13523ec638c9Svisa {
13533ec638c9Svisa 	struct ogx_softc *sc = ifp->if_softc;
13543ec638c9Svisa 
13553ec638c9Svisa 	if (!LIST_EMPTY(&sc->sc_mii.mii_phys))
13563ec638c9Svisa 		mii_mediachg(&sc->sc_mii);
13573ec638c9Svisa 
13583ec638c9Svisa 	return 0;
13593ec638c9Svisa }
13603ec638c9Svisa 
13613ec638c9Svisa void
ogx_media_status(struct ifnet * ifp,struct ifmediareq * imr)13623ec638c9Svisa ogx_media_status(struct ifnet *ifp, struct ifmediareq *imr)
13633ec638c9Svisa {
13643ec638c9Svisa 	struct ogx_softc *sc = ifp->if_softc;
13653ec638c9Svisa 
13663ec638c9Svisa 	if (!LIST_EMPTY(&sc->sc_mii.mii_phys)) {
13673ec638c9Svisa 		mii_pollstat(&sc->sc_mii);
13683ec638c9Svisa 		imr->ifm_status = sc->sc_mii.mii_media_status;
13693ec638c9Svisa 		imr->ifm_active = sc->sc_mii.mii_media_active;
13703ec638c9Svisa 	}
13713ec638c9Svisa }
13723ec638c9Svisa 
13733ec638c9Svisa int
ogx_mii_readreg(struct device * self,int phy_no,int reg)13743ec638c9Svisa ogx_mii_readreg(struct device *self, int phy_no, int reg)
13753ec638c9Svisa {
13763ec638c9Svisa 	struct ogx_softc *sc = (struct ogx_softc *)self;
13773ec638c9Svisa 
13783ec638c9Svisa 	return cn30xxsmi_read(sc->sc_smi, phy_no, reg);
13793ec638c9Svisa }
13803ec638c9Svisa 
13813ec638c9Svisa void
ogx_mii_writereg(struct device * self,int phy_no,int reg,int value)13823ec638c9Svisa ogx_mii_writereg(struct device *self, int phy_no, int reg, int value)
13833ec638c9Svisa {
13843ec638c9Svisa 	struct ogx_softc *sc = (struct ogx_softc *)self;
13853ec638c9Svisa 
13863ec638c9Svisa 	cn30xxsmi_write(sc->sc_smi, phy_no, reg, value);
13873ec638c9Svisa }
13883ec638c9Svisa 
13893ec638c9Svisa void
ogx_mii_statchg(struct device * self)13903ec638c9Svisa ogx_mii_statchg(struct device *self)
13913ec638c9Svisa {
13923ec638c9Svisa 	struct ogx_softc *sc = (struct ogx_softc *)self;
13933ec638c9Svisa 
13943ec638c9Svisa 	if (ISSET(sc->sc_mii.mii_media_active, IFM_FDX))
13953ec638c9Svisa 		sc->sc_link_duplex = 1;
13963ec638c9Svisa 	else
13973ec638c9Svisa 		sc->sc_link_duplex = 0;
13983ec638c9Svisa 	sc->sc_link_ops->link_change(sc);
13993ec638c9Svisa }
14003ec638c9Svisa 
14013ec638c9Svisa int
ogx_sgmii_link_init(struct ogx_softc * sc)14023ec638c9Svisa ogx_sgmii_link_init(struct ogx_softc *sc)
14033ec638c9Svisa {
14043ec638c9Svisa 	uint64_t cpu_freq = octeon_boot_info->eclock / 1000000;
14053ec638c9Svisa 	uint64_t val;
14063ec638c9Svisa 	int align = 1;
14073ec638c9Svisa 
14083ec638c9Svisa 	val = PORT_RD_8(sc, BGX_GMP_GMI_TX_APPEND);
14093ec638c9Svisa 	val |= BGX_GMP_GMI_TX_APPEND_FCS;
14103ec638c9Svisa 	val |= BGX_GMP_GMI_TX_APPEND_PAD;
14113ec638c9Svisa 	if (ISSET(val, BGX_GMP_GMI_TX_APPEND_PREAMBLE))
14123ec638c9Svisa 		align = 0;
14133ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_GMI_TX_APPEND, val);
14143ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_GMI_TX_MIN_PKT, 59);
14153ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_GMI_TX_THRESH, 0x20);
14163ec638c9Svisa 
14173ec638c9Svisa 	val = PORT_RD_8(sc, BGX_GMP_GMI_TX_SGMII_CTL);
14183ec638c9Svisa 	if (align)
14193ec638c9Svisa 		val |= BGX_GMP_GMI_TX_SGMII_CTL_ALIGN;
14203ec638c9Svisa 	else
14213ec638c9Svisa 		val &= ~BGX_GMP_GMI_TX_SGMII_CTL_ALIGN;
14223ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_GMI_TX_SGMII_CTL, val);
14233ec638c9Svisa 
14243ec638c9Svisa 	/* Set timing for SGMII. */
14253ec638c9Svisa 	val = PORT_RD_8(sc, BGX_GMP_PCS_LINK_TIMER);
14263ec638c9Svisa 	val &= ~BGX_GMP_PCS_LINK_TIMER_COUNT_M;
14273ec638c9Svisa 	val |= (1600 * cpu_freq) >> 10;
14283ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_PCS_LINK_TIMER, val);
14293ec638c9Svisa 
14303ec638c9Svisa 	return 0;
14313ec638c9Svisa }
14323ec638c9Svisa 
14333ec638c9Svisa void
ogx_sgmii_link_down(struct ogx_softc * sc)14343ec638c9Svisa ogx_sgmii_link_down(struct ogx_softc *sc)
14353ec638c9Svisa {
14363ec638c9Svisa 	uint64_t val;
14373ec638c9Svisa 	int timeout;
14383ec638c9Svisa 
14393ec638c9Svisa 	/* Wait until the port is idle. */
14403ec638c9Svisa 	for (timeout = 1000; timeout > 0; timeout--) {
14413ec638c9Svisa 		const uint64_t idlemask = BGX_GMP_GMI_PRT_CFG_RX_IDLE |
14423ec638c9Svisa 		    BGX_GMP_GMI_PRT_CFG_TX_IDLE;
14433ec638c9Svisa 		val = PORT_RD_8(sc, BGX_GMP_GMI_PRT_CFG);
14443ec638c9Svisa 		if ((val & idlemask) == idlemask)
14453ec638c9Svisa 			break;
14463ec638c9Svisa 		delay(1000);
14473ec638c9Svisa 	}
14483ec638c9Svisa 	if (timeout == 0)
14493ec638c9Svisa 		printf("%s: port idle timeout\n", DEVNAME(sc));
14503ec638c9Svisa 
14513ec638c9Svisa 	/* Disable autonegotiation and power down the link. */
14523ec638c9Svisa 	val = PORT_RD_8(sc, BGX_GMP_PCS_MR_CONTROL);
14533ec638c9Svisa 	val &= ~BGX_GMP_PCS_MR_CONTROL_AN_EN;
14543ec638c9Svisa 	val |= BGX_GMP_PCS_MR_CONTROL_PWR_DN;
14553ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_PCS_MR_CONTROL, val);
14563ec638c9Svisa }
14573ec638c9Svisa 
14583ec638c9Svisa void
ogx_sgmii_link_change(struct ogx_softc * sc)14593ec638c9Svisa ogx_sgmii_link_change(struct ogx_softc *sc)
14603ec638c9Svisa {
14613ec638c9Svisa 	struct ifnet *ifp = &sc->sc_ac.ac_if;
14623ec638c9Svisa 	uint64_t config;
14633ec638c9Svisa 	uint64_t misc_ctl;
14643ec638c9Svisa 	uint64_t prt_cfg = 0;
14653ec638c9Svisa 	uint64_t samp_pt;
14663ec638c9Svisa 	uint64_t tx_burst, tx_slot;
14673ec638c9Svisa 	uint64_t val;
14683ec638c9Svisa 	int timeout;
14693ec638c9Svisa 
14703ec638c9Svisa 	if (!LINK_STATE_IS_UP(ifp->if_link_state)) {
14713ec638c9Svisa 		misc_ctl = PORT_RD_8(sc, BGX_GMP_PCS_MISC_CTL);
14723ec638c9Svisa 		misc_ctl |= BGX_GMP_PCS_MISC_CTL_GMXENO;
14733ec638c9Svisa 		PORT_WR_8(sc, BGX_GMP_PCS_MISC_CTL, misc_ctl);
14743ec638c9Svisa 		return;
14753ec638c9Svisa 	}
14763ec638c9Svisa 
14773ec638c9Svisa 	val = PORT_RD_8(sc, BGX_CMR_CONFIG);
14783ec638c9Svisa 	val |= BGX_CMR_CONFIG_ENABLE;
14793ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_CONFIG, val);
14803ec638c9Svisa 
14813ec638c9Svisa 	/* Reset the PCS. */
14823ec638c9Svisa 	val = PORT_RD_8(sc, BGX_GMP_PCS_MR_CONTROL);
14833ec638c9Svisa 	val |= BGX_GMP_PCS_MR_CONTROL_RESET;
14843ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_PCS_MR_CONTROL_RESET, val);
14853ec638c9Svisa 
14863ec638c9Svisa 	/* Wait for the reset to complete. */
14873ec638c9Svisa 	timeout = 100000;
14883ec638c9Svisa 	while (timeout-- > 0) {
14893ec638c9Svisa 		val = PORT_RD_8(sc, BGX_GMP_PCS_MR_CONTROL);
14903ec638c9Svisa 		if (!ISSET(val, BGX_GMP_PCS_MR_CONTROL_RESET))
14913ec638c9Svisa 			break;
14923ec638c9Svisa 		delay(10);
14933ec638c9Svisa 	}
14943ec638c9Svisa 	if (timeout == 0)
14953ec638c9Svisa 		printf("%s: SGMII reset timeout\n", DEVNAME(sc));
14963ec638c9Svisa 
14973ec638c9Svisa 	/* Use MAC mode. */
14983ec638c9Svisa 	val = PORT_RD_8(sc, BGX_GMP_PCS_MISC_CTL);
14993ec638c9Svisa 	val &= ~BGX_GMP_PCS_MISC_CTL_MAC_PHY;
15003ec638c9Svisa 	val &= ~BGX_GMP_PCS_MISC_CTL_MODE;
15013ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_PCS_MISC_CTL, val);
15023ec638c9Svisa 
15033ec638c9Svisa 	/* Start autonegotiation between the SoC and the PHY. */
15043ec638c9Svisa 	val = PORT_RD_8(sc, BGX_GMP_PCS_MR_CONTROL);
15053ec638c9Svisa 	val |= BGX_GMP_PCS_MR_CONTROL_AN_EN;
15063ec638c9Svisa 	val |= BGX_GMP_PCS_MR_CONTROL_RST_AN;
15073ec638c9Svisa 	val &= ~BGX_GMP_PCS_MR_CONTROL_PWR_DN;
15083ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_PCS_MR_CONTROL, val);
15093ec638c9Svisa 
15103ec638c9Svisa 	/* Wait for the autonegotiation to complete. */
15113ec638c9Svisa 	timeout = 100000;
15123ec638c9Svisa 	while (timeout-- > 0) {
15133ec638c9Svisa 		val = PORT_RD_8(sc, BGX_GMP_PCS_MR_STATUS);
15143ec638c9Svisa 		if (ISSET(val, BGX_GMP_PCS_MR_STATUS_AN_CPT))
15153ec638c9Svisa 			break;
15163ec638c9Svisa 		delay(10);
15173ec638c9Svisa 	}
15183ec638c9Svisa 	if (timeout == 0)
15193ec638c9Svisa 		printf("%s: SGMII autonegotiation timeout\n", DEVNAME(sc));
15203ec638c9Svisa 
15213ec638c9Svisa 	/* Stop Rx and Tx engines. */
15223ec638c9Svisa 	config = PORT_RD_8(sc, BGX_CMR_CONFIG);
15233ec638c9Svisa 	config &= ~BGX_CMR_CONFIG_DATA_PKT_RX_EN;
15243ec638c9Svisa 	config &= ~BGX_CMR_CONFIG_DATA_PKT_TX_EN;
15253ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_CONFIG, config);
15263ec638c9Svisa 	(void)PORT_RD_8(sc, BGX_CMR_CONFIG);
15273ec638c9Svisa 
15283ec638c9Svisa 	/* Wait until the engines are idle. */
15293ec638c9Svisa 	for (timeout = 1000000; timeout > 0; timeout--) {
15303ec638c9Svisa 		const uint64_t idlemask = BGX_GMP_GMI_PRT_CFG_RX_IDLE |
15313ec638c9Svisa 		    BGX_GMP_GMI_PRT_CFG_TX_IDLE;
15323ec638c9Svisa 		prt_cfg = PORT_RD_8(sc, BGX_GMP_GMI_PRT_CFG);
15333ec638c9Svisa 		if ((prt_cfg & idlemask) == idlemask)
15343ec638c9Svisa 			break;
15353ec638c9Svisa 		delay(1);
15363ec638c9Svisa 	}
15373ec638c9Svisa 	if (timeout == 0)
15383ec638c9Svisa 		printf("%s: port idle timeout\n", DEVNAME(sc));
15393ec638c9Svisa 
15403ec638c9Svisa 	if (sc->sc_link_duplex)
15413ec638c9Svisa 		prt_cfg |= BGX_GMP_GMI_PRT_CFG_DUPLEX;
15423ec638c9Svisa 	else
15433ec638c9Svisa 		prt_cfg &= ~BGX_GMP_GMI_PRT_CFG_DUPLEX;
15443ec638c9Svisa 
15453ec638c9Svisa 	switch (ifp->if_baudrate) {
15463ec638c9Svisa 	case IF_Mbps(10):
15473ec638c9Svisa 		prt_cfg &= ~BGX_GMP_GMI_PRT_CFG_SPEED;
15483ec638c9Svisa 		prt_cfg |= BGX_GMP_GMI_PRT_CFG_SPEED_MSB;
15493ec638c9Svisa 		prt_cfg &= ~BGX_GMP_GMI_PRT_CFG_SLOTTIME;
15503ec638c9Svisa 		samp_pt = 25;
15513ec638c9Svisa 		tx_slot = 0x40;
15523ec638c9Svisa 		tx_burst = 0;
15533ec638c9Svisa 		break;
15543ec638c9Svisa 	case IF_Mbps(100):
15553ec638c9Svisa 		prt_cfg &= ~BGX_GMP_GMI_PRT_CFG_SPEED;
15563ec638c9Svisa 		prt_cfg &= ~BGX_GMP_GMI_PRT_CFG_SPEED_MSB;
15573ec638c9Svisa 		prt_cfg &= ~BGX_GMP_GMI_PRT_CFG_SLOTTIME;
15583ec638c9Svisa 		samp_pt = 5;
15593ec638c9Svisa 		tx_slot = 0x40;
15603ec638c9Svisa 		tx_burst = 0;
15613ec638c9Svisa 		break;
15623ec638c9Svisa 	case IF_Gbps(1):
15633ec638c9Svisa 	default:
15643ec638c9Svisa 		prt_cfg |= BGX_GMP_GMI_PRT_CFG_SPEED;
15653ec638c9Svisa 		prt_cfg &= ~BGX_GMP_GMI_PRT_CFG_SPEED_MSB;
15663ec638c9Svisa 		prt_cfg |= BGX_GMP_GMI_PRT_CFG_SLOTTIME;
15673ec638c9Svisa 		samp_pt = 1;
15683ec638c9Svisa 		tx_slot = 0x200;
15693ec638c9Svisa 		if (sc->sc_link_duplex)
15703ec638c9Svisa 			tx_burst = 0;
15713ec638c9Svisa 		else
15723ec638c9Svisa 			tx_burst = 0x2000;
15733ec638c9Svisa 		break;
15743ec638c9Svisa 	}
15753ec638c9Svisa 
15763ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_GMI_TX_SLOT, tx_slot);
15773ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_GMI_TX_BURST, tx_burst);
15783ec638c9Svisa 
15793ec638c9Svisa 	misc_ctl = PORT_RD_8(sc, BGX_GMP_PCS_MISC_CTL);
15803ec638c9Svisa 	misc_ctl &= ~BGX_GMP_PCS_MISC_CTL_GMXENO;
15813ec638c9Svisa 	misc_ctl &= ~BGX_GMP_PCS_MISC_CTL_SAMP_PT_M;
15823ec638c9Svisa 	misc_ctl |= samp_pt << BGX_GMP_PCS_MISC_CTL_SAMP_PT_S;
15833ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_PCS_MISC_CTL, misc_ctl);
15843ec638c9Svisa 	(void)PORT_RD_8(sc, BGX_GMP_PCS_MISC_CTL);
15853ec638c9Svisa 
15863ec638c9Svisa 	PORT_WR_8(sc, BGX_GMP_GMI_PRT_CFG, prt_cfg);
15873ec638c9Svisa 	(void)PORT_RD_8(sc, BGX_GMP_GMI_PRT_CFG);
15883ec638c9Svisa 
15893ec638c9Svisa 	config = PORT_RD_8(sc, BGX_CMR_CONFIG);
15903ec638c9Svisa 	config |= BGX_CMR_CONFIG_ENABLE |
15913ec638c9Svisa 	    BGX_CMR_CONFIG_DATA_PKT_RX_EN |
15923ec638c9Svisa 	    BGX_CMR_CONFIG_DATA_PKT_TX_EN;
15933ec638c9Svisa 	PORT_WR_8(sc, BGX_CMR_CONFIG, config);
15943ec638c9Svisa 	(void)PORT_RD_8(sc, BGX_CMR_CONFIG);
15953ec638c9Svisa }
15963ec638c9Svisa 
15977284f6b7Svisa #if NKSTAT > 0
15987284f6b7Svisa enum ogx_stat {
15997284f6b7Svisa 	ogx_stat_rx_hmin,
16007284f6b7Svisa 	ogx_stat_rx_h64,
16017284f6b7Svisa 	ogx_stat_rx_h128,
16027284f6b7Svisa 	ogx_stat_rx_h256,
16037284f6b7Svisa 	ogx_stat_rx_h512,
16047284f6b7Svisa 	ogx_stat_rx_h1024,
16057284f6b7Svisa 	ogx_stat_rx_hmax,
16067284f6b7Svisa 	ogx_stat_rx_totp_pki,
16077284f6b7Svisa 	ogx_stat_rx_toto_pki,
16087284f6b7Svisa 	ogx_stat_rx_raw,
16097284f6b7Svisa 	ogx_stat_rx_drop,
16107284f6b7Svisa 	ogx_stat_rx_bcast,
16117284f6b7Svisa 	ogx_stat_rx_mcast,
16127284f6b7Svisa 	ogx_stat_rx_fcs_error,
16137284f6b7Svisa 	ogx_stat_rx_fcs_undersz,
16147284f6b7Svisa 	ogx_stat_rx_undersz,
16157284f6b7Svisa 	ogx_stat_rx_fcs_oversz,
16167284f6b7Svisa 	ogx_stat_rx_oversz,
16177284f6b7Svisa 	ogx_stat_rx_error,
16187284f6b7Svisa 	ogx_stat_rx_special,
16197284f6b7Svisa 	ogx_stat_rx_bdrop,
16207284f6b7Svisa 	ogx_stat_rx_mdrop,
16217284f6b7Svisa 	ogx_stat_rx_ipbdrop,
16227284f6b7Svisa 	ogx_stat_rx_ipmdrop,
16237284f6b7Svisa 	ogx_stat_rx_sdrop,
16247284f6b7Svisa 	ogx_stat_rx_totp_bgx,
16257284f6b7Svisa 	ogx_stat_rx_toto_bgx,
16267284f6b7Svisa 	ogx_stat_rx_pause,
16277284f6b7Svisa 	ogx_stat_rx_dmac,
16287284f6b7Svisa 	ogx_stat_rx_bgx_drop,
16297284f6b7Svisa 	ogx_stat_rx_bgx_error,
16307284f6b7Svisa 	ogx_stat_tx_hmin,
16317284f6b7Svisa 	ogx_stat_tx_h64,
16327284f6b7Svisa 	ogx_stat_tx_h65,
16337284f6b7Svisa 	ogx_stat_tx_h128,
16347284f6b7Svisa 	ogx_stat_tx_h256,
16357284f6b7Svisa 	ogx_stat_tx_h512,
16367284f6b7Svisa 	ogx_stat_tx_h1024,
16377284f6b7Svisa 	ogx_stat_tx_hmax,
16387284f6b7Svisa 	ogx_stat_tx_coll,
16397284f6b7Svisa 	ogx_stat_tx_defer,
16407284f6b7Svisa 	ogx_stat_tx_mcoll,
16417284f6b7Svisa 	ogx_stat_tx_scoll,
16427284f6b7Svisa 	ogx_stat_tx_toto_bgx,
16437284f6b7Svisa 	ogx_stat_tx_totp_bgx,
16447284f6b7Svisa 	ogx_stat_tx_bcast,
16457284f6b7Svisa 	ogx_stat_tx_mcast,
16467284f6b7Svisa 	ogx_stat_tx_uflow,
16477284f6b7Svisa 	ogx_stat_tx_control,
16487284f6b7Svisa 	ogx_stat_count
16497284f6b7Svisa };
16507284f6b7Svisa 
16517284f6b7Svisa enum ogx_counter_type {
16527284f6b7Svisa 	C_NONE = 0,
16537284f6b7Svisa 	C_BGX,
16547284f6b7Svisa 	C_PKI,
16557284f6b7Svisa };
16567284f6b7Svisa 
16577284f6b7Svisa struct ogx_counter {
16587284f6b7Svisa 	const char		*c_name;
16597284f6b7Svisa 	enum kstat_kv_unit	 c_unit;
16607284f6b7Svisa 	enum ogx_counter_type	 c_type;
16617284f6b7Svisa 	uint32_t		 c_reg;
16627284f6b7Svisa };
16637284f6b7Svisa 
16647284f6b7Svisa static const struct ogx_counter ogx_counters[ogx_stat_count] = {
16657284f6b7Svisa 	[ogx_stat_rx_hmin] =
16667284f6b7Svisa 	    { "rx 1-63B",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_HIST0 },
16677284f6b7Svisa 	[ogx_stat_rx_h64] =
16687284f6b7Svisa 	    { "rx 64-127B",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_HIST1 },
16697284f6b7Svisa 	[ogx_stat_rx_h128] =
16707284f6b7Svisa 	    { "rx 128-255B",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_HIST2 },
16717284f6b7Svisa 	[ogx_stat_rx_h256] =
16727284f6b7Svisa 	    { "rx 256-511B",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_HIST3 },
16737284f6b7Svisa 	[ogx_stat_rx_h512] =
16747284f6b7Svisa 	    { "rx 512-1023B",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_HIST4 },
16757284f6b7Svisa 	[ogx_stat_rx_h1024] =
16767284f6b7Svisa 	    { "rx 1024-1518B",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_HIST5 },
16777284f6b7Svisa 	[ogx_stat_rx_hmax] =
16787284f6b7Svisa 	    { "rx 1519-maxB",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_HIST6 },
16797284f6b7Svisa 	[ogx_stat_rx_totp_pki] =
16807284f6b7Svisa 	    { "rx total pki",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT0 },
16817284f6b7Svisa 	[ogx_stat_rx_toto_pki] =
16827284f6b7Svisa 	    { "rx total pki",	KSTAT_KV_U_BYTES, C_PKI, PKI_STAT_STAT1 },
16837284f6b7Svisa 	[ogx_stat_rx_raw] =
16847284f6b7Svisa 	    { "rx raw",		KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT2 },
16857284f6b7Svisa 	[ogx_stat_rx_drop] =
16867284f6b7Svisa 	    { "rx drop",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT3 },
16877284f6b7Svisa 	[ogx_stat_rx_bcast] =
16887284f6b7Svisa 	    { "rx bcast",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT5 },
16897284f6b7Svisa 	[ogx_stat_rx_mcast] =
16907284f6b7Svisa 	    { "rx mcast",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT6 },
16917284f6b7Svisa 	[ogx_stat_rx_fcs_error] =
16927284f6b7Svisa 	    { "rx fcs error",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT7 },
16937284f6b7Svisa 	[ogx_stat_rx_fcs_undersz] =
16947284f6b7Svisa 	    { "rx fcs undersz",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT8 },
16957284f6b7Svisa 	[ogx_stat_rx_undersz] =
16967284f6b7Svisa 	    { "rx undersz",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT9 },
16977284f6b7Svisa 	[ogx_stat_rx_fcs_oversz] =
16987284f6b7Svisa 	    { "rx fcs oversz",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT10 },
16997284f6b7Svisa 	[ogx_stat_rx_oversz] =
17007284f6b7Svisa 	    { "rx oversize",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT11 },
17017284f6b7Svisa 	[ogx_stat_rx_error] =
17027284f6b7Svisa 	    { "rx error",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT12 },
17037284f6b7Svisa 	[ogx_stat_rx_special] =
17047284f6b7Svisa 	    { "rx special",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT13 },
17057284f6b7Svisa 	[ogx_stat_rx_bdrop] =
17067284f6b7Svisa 	    { "rx drop bcast",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT14 },
17077284f6b7Svisa 	[ogx_stat_rx_mdrop] =
17087284f6b7Svisa 	    { "rx drop mcast",	KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT15 },
17097284f6b7Svisa 	[ogx_stat_rx_ipbdrop] =
17107284f6b7Svisa 	    { "rx drop ipbcast",KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT16 },
17117284f6b7Svisa 	[ogx_stat_rx_ipmdrop] =
17127284f6b7Svisa 	    { "rx drop ipmcast",KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT17 },
17137284f6b7Svisa 	[ogx_stat_rx_sdrop] =
17147284f6b7Svisa 	    { "rx drop special",KSTAT_KV_U_PACKETS, C_PKI, PKI_STAT_STAT18 },
17157284f6b7Svisa 	[ogx_stat_rx_totp_bgx] =
17167284f6b7Svisa 	    { "rx total bgx",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_RX_STAT0 },
17177284f6b7Svisa 	[ogx_stat_rx_toto_bgx] =
17187284f6b7Svisa 	    { "rx total bgx",	KSTAT_KV_U_BYTES, C_BGX, BGX_CMR_RX_STAT1 },
17197284f6b7Svisa 	[ogx_stat_rx_pause] =
17207284f6b7Svisa 	    { "rx bgx pause",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_RX_STAT2 },
17217284f6b7Svisa 	[ogx_stat_rx_dmac] =
17227284f6b7Svisa 	    { "rx bgx dmac",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_RX_STAT4 },
17237284f6b7Svisa 	[ogx_stat_rx_bgx_drop] =
17247284f6b7Svisa 	    { "rx bgx drop",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_RX_STAT6 },
17257284f6b7Svisa 	[ogx_stat_rx_bgx_error] =
17267284f6b7Svisa 	    { "rx bgx error",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_RX_STAT8 },
17277284f6b7Svisa 	[ogx_stat_tx_hmin] =
17287284f6b7Svisa 	    { "tx 1-63B",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT6 },
17297284f6b7Svisa 	[ogx_stat_tx_h64] =
17307284f6b7Svisa 	    { "tx 64B",		KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT7 },
17317284f6b7Svisa 	[ogx_stat_tx_h65] =
17327284f6b7Svisa 	    { "tx 65-127B",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT8 },
17337284f6b7Svisa 	[ogx_stat_tx_h128] =
17347284f6b7Svisa 	    { "tx 128-255B",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT9 },
17357284f6b7Svisa 	[ogx_stat_tx_h256] =
17367284f6b7Svisa 	    { "tx 256-511B",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT10 },
17377284f6b7Svisa 	[ogx_stat_tx_h512] =
17387284f6b7Svisa 	    { "tx 512-1023B",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT11 },
17397284f6b7Svisa 	[ogx_stat_tx_h1024] =
17407284f6b7Svisa 	    { "tx 1024-1518B",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT12 },
17417284f6b7Svisa 	[ogx_stat_tx_hmax] =
17427284f6b7Svisa 	    { "tx 1519-maxB",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT13 },
17437284f6b7Svisa 	[ogx_stat_tx_coll] =
17447284f6b7Svisa 	    { "tx coll",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT0 },
17457284f6b7Svisa 	[ogx_stat_tx_defer] =
17467284f6b7Svisa 	    { "tx defer",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT1 },
17477284f6b7Svisa 	[ogx_stat_tx_mcoll] =
17487284f6b7Svisa 	    { "tx mcoll",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT2 },
17497284f6b7Svisa 	[ogx_stat_tx_scoll] =
17507284f6b7Svisa 	    { "tx scoll",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT3 },
17517284f6b7Svisa 	[ogx_stat_tx_toto_bgx] =
17527284f6b7Svisa 	    { "tx total bgx",	KSTAT_KV_U_BYTES, C_BGX, BGX_CMR_TX_STAT4 },
17537284f6b7Svisa 	[ogx_stat_tx_totp_bgx] =
17547284f6b7Svisa 	    { "tx total bgx",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT5 },
17557284f6b7Svisa 	[ogx_stat_tx_bcast] =
17567284f6b7Svisa 	    { "tx bcast",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT14 },
17577284f6b7Svisa 	[ogx_stat_tx_mcast] =
17587284f6b7Svisa 	    { "tx mcast",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT15 },
17597284f6b7Svisa 	[ogx_stat_tx_uflow] =
17607284f6b7Svisa 	    { "tx underflow",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT16 },
17617284f6b7Svisa 	[ogx_stat_tx_control] =
17627284f6b7Svisa 	    { "tx control",	KSTAT_KV_U_PACKETS, C_BGX, BGX_CMR_TX_STAT17 },
17637284f6b7Svisa };
17647284f6b7Svisa 
17657284f6b7Svisa void
ogx_kstat_attach(struct ogx_softc * sc)17667284f6b7Svisa ogx_kstat_attach(struct ogx_softc *sc)
17677284f6b7Svisa {
17687284f6b7Svisa 	const struct ogx_counter *c;
17697284f6b7Svisa 	struct kstat *ks;
17707284f6b7Svisa 	struct kstat_kv *kvs;
17717284f6b7Svisa 	struct ogx_node *node = sc->sc_node;
17727284f6b7Svisa 	uint64_t *vals;
17737284f6b7Svisa 	int i;
17747284f6b7Svisa 
17757284f6b7Svisa 	mtx_init(&sc->sc_kstat_mtx, IPL_SOFTCLOCK);
17767284f6b7Svisa 	timeout_set(&sc->sc_kstat_tmo, ogx_kstat_tick, sc);
17777284f6b7Svisa 
17787284f6b7Svisa 	if (bus_space_subregion(node->node_iot, node->node_pki,
17797284f6b7Svisa 	    PKI_STAT_BASE(PORT_PKIND(sc)), PKI_STAT_SIZE,
17807284f6b7Svisa 	    &sc->sc_pki_stat_ioh) != 0)
17817284f6b7Svisa 		return;
17827284f6b7Svisa 
17837284f6b7Svisa 	ks = kstat_create(DEVNAME(sc), 0, "ogx-stats", 0, KSTAT_T_KV, 0);
17847284f6b7Svisa 	if (ks == NULL)
17857284f6b7Svisa 		return;
17867284f6b7Svisa 
17877284f6b7Svisa 	vals = mallocarray(nitems(ogx_counters), sizeof(*vals),
17887284f6b7Svisa 	    M_DEVBUF, M_WAITOK | M_ZERO);
17897284f6b7Svisa 	sc->sc_counter_vals = vals;
17907284f6b7Svisa 
17917284f6b7Svisa 	kvs = mallocarray(nitems(ogx_counters), sizeof(*kvs),
17927284f6b7Svisa 	    M_DEVBUF, M_WAITOK | M_ZERO);
17937284f6b7Svisa 	for (i = 0; i < nitems(ogx_counters); i++) {
17947284f6b7Svisa 		c = &ogx_counters[i];
17957284f6b7Svisa 		kstat_kv_unit_init(&kvs[i], c->c_name, KSTAT_KV_T_COUNTER64,
17967284f6b7Svisa 		    c->c_unit);
17977284f6b7Svisa 	}
17987284f6b7Svisa 
17997284f6b7Svisa 	kstat_set_mutex(ks, &sc->sc_kstat_mtx);
18007284f6b7Svisa 	ks->ks_softc = sc;
18017284f6b7Svisa 	ks->ks_data = kvs;
18027284f6b7Svisa 	ks->ks_datalen = nitems(ogx_counters) * sizeof(*kvs);
18037284f6b7Svisa 	ks->ks_read = ogx_kstat_read;
18047284f6b7Svisa 
18057284f6b7Svisa 	sc->sc_kstat = ks;
18067284f6b7Svisa 	kstat_install(ks);
18077284f6b7Svisa }
18087284f6b7Svisa 
18097284f6b7Svisa int
ogx_kstat_read(struct kstat * ks)18107284f6b7Svisa ogx_kstat_read(struct kstat *ks)
18117284f6b7Svisa {
18127284f6b7Svisa 	const struct ogx_counter *c;
18137284f6b7Svisa 	struct ogx_softc *sc = ks->ks_softc;
18147284f6b7Svisa 	struct kstat_kv *kvs = ks->ks_data;
18157284f6b7Svisa 	uint64_t *counter_vals = sc->sc_counter_vals;
18167284f6b7Svisa 	uint64_t delta, val;
18177284f6b7Svisa 	int i, timeout;
18187284f6b7Svisa 
18197284f6b7Svisa 	for (i = 0; i < nitems(ogx_counters); i++) {
18207284f6b7Svisa 		c = &ogx_counters[i];
18217284f6b7Svisa 		switch (c->c_type) {
18227284f6b7Svisa 		case C_BGX:
18237284f6b7Svisa 			val = PORT_RD_8(sc, c->c_reg);
18247284f6b7Svisa 			delta = (val - counter_vals[i]) & BGX_CMR_STAT_MASK;
18257284f6b7Svisa 			counter_vals[i] = val;
18267284f6b7Svisa 			kstat_kv_u64(&kvs[i]) += delta;
18277284f6b7Svisa 			break;
18287284f6b7Svisa 		case C_PKI:
18297284f6b7Svisa 			/*
18307284f6b7Svisa 			 * Retry the read if the value is bogus.
18317284f6b7Svisa 			 * This can happen on some hardware when
18327284f6b7Svisa 			 * the hardware is updating the value.
18337284f6b7Svisa 			 */
18347284f6b7Svisa 			for (timeout = 100; timeout > 0; timeout--) {
18357284f6b7Svisa 				val = bus_space_read_8(sc->sc_iot,
18367284f6b7Svisa 				    sc->sc_pki_stat_ioh, c->c_reg);
18377284f6b7Svisa 				if (val != ~0ULL) {
18387284f6b7Svisa 					delta = (val - counter_vals[i]) &
18397284f6b7Svisa 					    PKI_STAT_MASK;
18407284f6b7Svisa 					counter_vals[i] = val;
18417284f6b7Svisa 					kstat_kv_u64(&kvs[i]) += delta;
18427284f6b7Svisa 					break;
18437284f6b7Svisa 				}
18447284f6b7Svisa 				CPU_BUSY_CYCLE();
18457284f6b7Svisa 			}
18467284f6b7Svisa 			break;
18477284f6b7Svisa 		case C_NONE:
18487284f6b7Svisa 			break;
18497284f6b7Svisa 		}
18507284f6b7Svisa 	}
18517284f6b7Svisa 
18527284f6b7Svisa 	getnanouptime(&ks->ks_updated);
18537284f6b7Svisa 
18547284f6b7Svisa 	return 0;
18557284f6b7Svisa }
18567284f6b7Svisa 
18577284f6b7Svisa void
ogx_kstat_start(struct ogx_softc * sc)18587284f6b7Svisa ogx_kstat_start(struct ogx_softc *sc)
18597284f6b7Svisa {
18607284f6b7Svisa 	const struct ogx_counter *c;
18617284f6b7Svisa 	int i;
18627284f6b7Svisa 
18637284f6b7Svisa 	/* Zero the counters. */
18647284f6b7Svisa 	for (i = 0; i < nitems(ogx_counters); i++) {
18657284f6b7Svisa 		c = &ogx_counters[i];
18667284f6b7Svisa 		switch (c->c_type) {
18677284f6b7Svisa 		case C_BGX:
18687284f6b7Svisa 			PORT_WR_8(sc, c->c_reg, 0);
18697284f6b7Svisa 			break;
18707284f6b7Svisa 		case C_PKI:
18717284f6b7Svisa 			bus_space_write_8(sc->sc_iot, sc->sc_pki_stat_ioh,
18727284f6b7Svisa 			    c->c_reg, 0);
18737284f6b7Svisa 			break;
18747284f6b7Svisa 		case C_NONE:
18757284f6b7Svisa 			break;
18767284f6b7Svisa 		}
18777284f6b7Svisa 	}
18787284f6b7Svisa 	memset(sc->sc_counter_vals, 0,
18797284f6b7Svisa 	    nitems(ogx_counters) * sizeof(*sc->sc_counter_vals));
18807284f6b7Svisa 
18817284f6b7Svisa 	timeout_add_sec(&sc->sc_kstat_tmo, OGX_KSTAT_TICK_SECS);
18827284f6b7Svisa }
18837284f6b7Svisa 
18847284f6b7Svisa void
ogx_kstat_stop(struct ogx_softc * sc)18857284f6b7Svisa ogx_kstat_stop(struct ogx_softc *sc)
18867284f6b7Svisa {
18877284f6b7Svisa 	timeout_del_barrier(&sc->sc_kstat_tmo);
18887284f6b7Svisa 
18897284f6b7Svisa 	mtx_enter(&sc->sc_kstat_mtx);
18907284f6b7Svisa 	ogx_kstat_read(sc->sc_kstat);
18917284f6b7Svisa 	mtx_leave(&sc->sc_kstat_mtx);
18927284f6b7Svisa }
18937284f6b7Svisa 
18947284f6b7Svisa void
ogx_kstat_tick(void * arg)18957284f6b7Svisa ogx_kstat_tick(void *arg)
18967284f6b7Svisa {
18977284f6b7Svisa 	struct ogx_softc *sc = arg;
18987284f6b7Svisa 
18997284f6b7Svisa 	timeout_add_sec(&sc->sc_kstat_tmo, OGX_KSTAT_TICK_SECS);
19007284f6b7Svisa 
19017284f6b7Svisa 	if (mtx_enter_try(&sc->sc_kstat_mtx)) {
19027284f6b7Svisa 		ogx_kstat_read(sc->sc_kstat);
19037284f6b7Svisa 		mtx_leave(&sc->sc_kstat_mtx);
19047284f6b7Svisa 	}
19057284f6b7Svisa }
19067284f6b7Svisa #endif /* NKSTAT > 0 */
19077284f6b7Svisa 
19083ec638c9Svisa int
ogx_node_init(struct ogx_node ** pnode,bus_dma_tag_t dmat,bus_space_tag_t iot)19093ec638c9Svisa ogx_node_init(struct ogx_node **pnode, bus_dma_tag_t dmat, bus_space_tag_t iot)
19103ec638c9Svisa {
19113ec638c9Svisa 	const struct ogx_config *cfg;
19123ec638c9Svisa 	struct ogx_node *node = &ogx_node;
19133ec638c9Svisa 	uint64_t val;
19143ec638c9Svisa 	uint32_t chipid;
19153ec638c9Svisa 	int cl, i, timeout;
19163ec638c9Svisa 
19173ec638c9Svisa 	if (node->node_flags & NODE_INITED) {
19183ec638c9Svisa 		*pnode = node;
19193ec638c9Svisa 		return 0;
19203ec638c9Svisa 	}
19213ec638c9Svisa 
19223ec638c9Svisa 	chipid = octeon_get_chipid();
19233ec638c9Svisa 	switch (octeon_model_family(chipid)) {
19243ec638c9Svisa 	case OCTEON_MODEL_FAMILY_CN73XX:
19253ec638c9Svisa 		node->node_cfg = cfg = &ogx_cn73xx_config;
19263ec638c9Svisa 		break;
19273ec638c9Svisa 	case OCTEON_MODEL_FAMILY_CN78XX:
19283ec638c9Svisa 		node->node_cfg = cfg = &ogx_cn78xx_config;
19293ec638c9Svisa 		break;
19303ec638c9Svisa 	default:
19313ec638c9Svisa 		printf(": unhandled chipid 0x%x\n", chipid);
19323ec638c9Svisa 		return -1;
19333ec638c9Svisa 	}
19343ec638c9Svisa 
19353ec638c9Svisa 	rw_init(&node->node_lock, "ogxnlk");
19363ec638c9Svisa 
19373ec638c9Svisa 	node->node_dmat = dmat;
19383ec638c9Svisa 	node->node_iot = iot;
19393ec638c9Svisa 	if (bus_space_map(node->node_iot, FPA3_BASE, FPA3_SIZE, 0,
19403ec638c9Svisa 	    &node->node_fpa3)) {
19413ec638c9Svisa 		printf(": can't map FPA3\n");
19423ec638c9Svisa 		goto error;
19433ec638c9Svisa 	}
19443ec638c9Svisa 	if (bus_space_map(node->node_iot, PKI_BASE, PKI_SIZE, 0,
19453ec638c9Svisa 	    &node->node_pki)) {
19463ec638c9Svisa 		printf(": can't map PKI\n");
19473ec638c9Svisa 		goto error;
19483ec638c9Svisa 	}
19493ec638c9Svisa 	if (bus_space_map(node->node_iot, PKO3_BASE, PKO3_SIZE, 0,
19503ec638c9Svisa 	    &node->node_pko3)) {
19513ec638c9Svisa 		printf(": can't map PKO3\n");
19523ec638c9Svisa 		goto error;
19533ec638c9Svisa 	}
19543ec638c9Svisa 	if (bus_space_map(node->node_iot, SSO_BASE, SSO_SIZE, 0,
19553ec638c9Svisa 	    &node->node_sso)) {
19563ec638c9Svisa 		printf(": can't map SSO\n");
19573ec638c9Svisa 		goto error;
19583ec638c9Svisa 	}
19593ec638c9Svisa 
19603ec638c9Svisa 	/*
19613ec638c9Svisa 	 * The rest of this function handles errors by panicking.
19623ec638c9Svisa 	 */
19633ec638c9Svisa 
19643ec638c9Svisa 	node->node_flags |= NODE_INITED;
19653ec638c9Svisa 
19663ec638c9Svisa 	PKO3_WR_8(node, PKO3_CHANNEL_LEVEL, 0);
19673ec638c9Svisa 
19683ec638c9Svisa 	ogx_fpa3_pool_init(node, &node->node_pkt_pool, OGX_POOL_PKT, 1024 * 32);
19693ec638c9Svisa 	ogx_fpa3_pool_init(node, &node->node_pko_pool, OGX_POOL_PKO, 1024 * 32);
19703ec638c9Svisa 	ogx_fpa3_pool_init(node, &node->node_sso_pool, OGX_POOL_SSO, 1024 * 32);
19713ec638c9Svisa 
19723ec638c9Svisa 	ogx_fpa3_aura_init(node, &node->node_pko_aura, OGX_AURA_PKO,
19733ec638c9Svisa 	    &node->node_pko_pool);
19743ec638c9Svisa 	ogx_fpa3_aura_init(node, &node->node_sso_aura, OGX_AURA_SSO,
19753ec638c9Svisa 	    &node->node_sso_pool);
19763ec638c9Svisa 
19773ec638c9Svisa 	ogx_fpa3_aura_load(node, &node->node_sso_aura, 1024, 4096);
19783ec638c9Svisa 	ogx_fpa3_aura_load(node, &node->node_pko_aura, 1024, 4096);
19793ec638c9Svisa 
19803ec638c9Svisa 	/*
19813ec638c9Svisa 	 * Initialize the Schedule/Synchronization/Order (SSO) unit.
19823ec638c9Svisa 	 */
19833ec638c9Svisa 
19843ec638c9Svisa 	val = SSO_AW_CFG_LDWB | SSO_AW_CFG_LDT | SSO_AW_CFG_STT;
19853ec638c9Svisa 	SSO_WR_8(node, SSO_AW_CFG, val);
19863ec638c9Svisa 
19873ec638c9Svisa 	val = node->node_id << SSO_XAQ_AURA_NODE_S;
19883ec638c9Svisa 	val |= (uint64_t)OGX_AURA_SSO << SSO_XAQ_AURA_LAURA_S;
19893ec638c9Svisa 	SSO_WR_8(node, SSO_XAQ_AURA, val);
19903ec638c9Svisa 
19913ec638c9Svisa 	SSO_WR_8(node, SSO_ERR0, 0);
19923ec638c9Svisa 
19933ec638c9Svisa 	/* Initialize the hardware's linked lists. */
19943ec638c9Svisa 	for (i = 0; i < 64; i++) {
19953ec638c9Svisa 		paddr_t addr;
19963ec638c9Svisa 
19973ec638c9Svisa 		addr = ogx_fpa3_alloc(&node->node_sso_aura);
19983ec638c9Svisa 		if (addr == 0)
19993ec638c9Svisa 			panic("%s: could not alloc initial XAQ block %d",
20003ec638c9Svisa 			    __func__, i);
20013ec638c9Svisa 		SSO_WR_8(node, SSO_XAQ_HEAD_PTR(i), addr);
20023ec638c9Svisa 		SSO_WR_8(node, SSO_XAQ_TAIL_PTR(i), addr);
20033ec638c9Svisa 		SSO_WR_8(node, SSO_XAQ_HEAD_NEXT(i), addr);
20043ec638c9Svisa 		SSO_WR_8(node, SSO_XAQ_TAIL_NEXT(i), addr);
20053ec638c9Svisa 
20063ec638c9Svisa 		SSO_WR_8(node, SSO_GRP_PRI(i), SSO_GRP_PRI_WEIGHT_M);
20073ec638c9Svisa 	}
20083ec638c9Svisa 
20093ec638c9Svisa 	val = SSO_RD_8(node, SSO_AW_CFG);
20103ec638c9Svisa 	val |= SSO_AW_CFG_RWEN;
20113ec638c9Svisa 	SSO_WR_8(node, SSO_AW_CFG, val);
20123ec638c9Svisa 
20133ec638c9Svisa 	/*
20143ec638c9Svisa 	 * Initialize the Packet Input (PKI) unit.
20153ec638c9Svisa 	 */
20163ec638c9Svisa 
20173ec638c9Svisa 	/* Clear any previous style configuration. */
20183ec638c9Svisa 	for (cl = 0; cl < cfg->cfg_nclusters; cl++) {
20193ec638c9Svisa 		int pkind;
20203ec638c9Svisa 
20213ec638c9Svisa 		for (pkind = 0; pkind < 64; pkind++)
20223ec638c9Svisa 			PKI_WR_8(node, PKI_CL_PKIND_STYLE(cl, pkind), 0);
20233ec638c9Svisa 	}
20243ec638c9Svisa 
20253ec638c9Svisa 	/* Invalidate all PCAM entries. */
20263ec638c9Svisa 	for (cl = 0; cl < cfg->cfg_nclusters; cl++) {
20273ec638c9Svisa 		int bank;
20283ec638c9Svisa 
20293ec638c9Svisa 		for (bank = 0; bank < 2; bank++) {
20303ec638c9Svisa 			for (i = 0; i < 192; i++) {
20313ec638c9Svisa 				PKI_WR_8(node,
20323ec638c9Svisa 				    PKI_CL_PCAM_TERM(cl, bank, i), 0);
20333ec638c9Svisa 			}
20343ec638c9Svisa 		}
20353ec638c9Svisa 	}
20363ec638c9Svisa 
20373ec638c9Svisa 	PKI_WR_8(node, PKI_STAT_CTL, 0);
20383ec638c9Svisa 
20393ec638c9Svisa 	/* Enable input backpressure. */
20403ec638c9Svisa 	val = PKI_RD_8(node, PKI_BUF_CTL);
20413ec638c9Svisa 	val |= PKI_BUF_CTL_PBP_EN;
20423ec638c9Svisa 	PKI_WR_8(node, PKI_BUF_CTL, val);
20433ec638c9Svisa 
20443ec638c9Svisa 	/* Disable the parsing clusters until the firmware has been loaded. */
20453ec638c9Svisa 	for (cl = 0; cl < cfg->cfg_nclusters; cl++) {
20463ec638c9Svisa 		val = PKI_RD_8(node, PKI_ICG_CFG(cl));
20473ec638c9Svisa 		val &= ~PKI_ICG_CFG_PENA;
20483ec638c9Svisa 		PKI_WR_8(node, PKI_ICG_CFG(cl), val);
20493ec638c9Svisa 	}
20503ec638c9Svisa 
20513ec638c9Svisa 	val = PKI_RD_8(node, PKI_GBL_PEN);
20523ec638c9Svisa 	val &= ~PKI_GBL_PEN_M;
20533ec638c9Svisa 	val |= PKI_GBL_PEN_L3;
20543ec638c9Svisa 	val |= PKI_GBL_PEN_L4;
20553ec638c9Svisa 	PKI_WR_8(node, PKI_GBL_PEN, val);
20563ec638c9Svisa 
20573ec638c9Svisa 	for (i = 0; i < nitems(ogx_ltypes); i++) {
20583ec638c9Svisa 		val = PKI_RD_8(node, PKI_LTYPE_MAP(i));
20593ec638c9Svisa 		val &= ~0x7;
20603ec638c9Svisa 		val |= ogx_ltypes[i];
20613ec638c9Svisa 		PKI_WR_8(node, PKI_LTYPE_MAP(i), val);
20623ec638c9Svisa 	}
20633ec638c9Svisa 
20643ec638c9Svisa 	while (PKI_RD_8(node, PKI_SFT_RST) & PKI_SFT_RST_BUSY)
20653ec638c9Svisa 		delay(1);
20663ec638c9Svisa 
20673ec638c9Svisa 	val = PKI_RD_8(node, PKI_BUF_CTL);
20683ec638c9Svisa 	val |= PKI_BUF_CTL_PKI_EN;
20693ec638c9Svisa 	PKI_WR_8(node, PKI_BUF_CTL, val);
20703ec638c9Svisa 
20713ec638c9Svisa 	/*
20723ec638c9Svisa 	 * Initialize the Packet Output (PKO) unit.
20733ec638c9Svisa 	 */
20743ec638c9Svisa 
20753ec638c9Svisa 	/* Detach MACs from FIFOs. */
20763ec638c9Svisa 	for (i = 0; i < cfg->cfg_nmacs; i++) {
20773ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_MAC_CFG(i));
20783ec638c9Svisa 		val |= PKO3_MAC_CFG_FIFO_NUM_M;
20793ec638c9Svisa 		PKO3_WR_8(node, PKO3_MAC_CFG(i), val);
20803ec638c9Svisa 	}
20813ec638c9Svisa 
20823ec638c9Svisa 	/* Attach port queues to the NULL FIFO. */
20833ec638c9Svisa 	for (i = 0; i < cfg->cfg_npqs; i++) {
20843ec638c9Svisa 		val = (uint64_t)cfg->cfg_nullmac << PKO3_L1_SQ_TOPOLOGY_LINK_S;
20853ec638c9Svisa 		PKO3_WR_8(node, PKO3_L1_SQ_TOPOLOGY(i), val);
20863ec638c9Svisa 		val = (uint64_t)cfg->cfg_nullmac << PKO3_L1_SQ_SHAPE_LINK_S;
20873ec638c9Svisa 		PKO3_WR_8(node, PKO3_L1_SQ_SHAPE(i), val);
20883ec638c9Svisa 		val = (uint64_t)cfg->cfg_nullmac << PKO3_L1_SQ_LINK_LINK_S;
20893ec638c9Svisa 		PKO3_WR_8(node, PKO3_L1_SQ_LINK(i), val);
20903ec638c9Svisa 	}
20913ec638c9Svisa 
20923ec638c9Svisa 	/* Reset the FIFO groups to use 2.5 KB per each FIFO. */
20933ec638c9Svisa 	for (i = 0; i < cfg->cfg_nfifogrps; i++) {
20943ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_PTGF_CFG(i));
20953ec638c9Svisa 		val &= ~PKO3_PTGF_CFG_SIZE_M;
20963ec638c9Svisa 		val &= ~PKO3_PTGF_CFG_RATE_M;
20973ec638c9Svisa 		val |= 2 << PKO3_PTGF_CFG_RATE_S;
20983ec638c9Svisa 		val |= PKO3_PTGF_CFG_RESET;
20993ec638c9Svisa 		PKO3_WR_8(node, PKO3_PTGF_CFG(i), val);
21003ec638c9Svisa 
21013ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_PTGF_CFG(i));
21023ec638c9Svisa 		val &= ~PKO3_PTGF_CFG_RESET;
21033ec638c9Svisa 		PKO3_WR_8(node, PKO3_PTGF_CFG(i), val);
21043ec638c9Svisa 	}
21053ec638c9Svisa 
21063ec638c9Svisa 	PKO3_WR_8(node, PKO3_DPFI_FLUSH, 0);
21073ec638c9Svisa 
21083ec638c9Svisa 	/* Set PKO aura. */
21093ec638c9Svisa 	val = ((uint64_t)node->node_id << PKO3_DPFI_FPA_AURA_NODE_S) |
21103ec638c9Svisa 	    (OGX_AURA_PKO << PKO3_DPFI_FPA_AURA_AURA_S);
21113ec638c9Svisa 	PKO3_WR_8(node, PKO3_DPFI_FPA_AURA, val);
21123ec638c9Svisa 
21133ec638c9Svisa 	/* Allow PKO to use the FPA. */
21143ec638c9Svisa 	PKO3_WR_8(node, PKO3_DPFI_FPA_ENA, PKO3_DPFI_FPA_ENA_ENABLE);
21153ec638c9Svisa 
21163ec638c9Svisa 	timeout = 1000;
21173ec638c9Svisa 	while (timeout-- > 0) {
21183ec638c9Svisa 		val = PKO3_RD_8(node, PKO3_STATUS);
21193ec638c9Svisa 		if (ISSET(val, PKO3_STATUS_PKO_RDY))
21203ec638c9Svisa 			break;
21213ec638c9Svisa 		delay(1000);
21223ec638c9Svisa 	}
21233ec638c9Svisa 	if (timeout == 0)
21243ec638c9Svisa 		panic("PKO timeout");
21253ec638c9Svisa 
21263ec638c9Svisa 	val = 72 << PKO3_PTF_IOBP_CFG_MAX_RD_SZ_S;
21273ec638c9Svisa 	PKO3_WR_8(node, PKO3_PTF_IOBP_CFG, val);
21283ec638c9Svisa 
21293ec638c9Svisa 	val = 60 << PKO3_PDM_CFG_MIN_PAD_LEN_S;
21303ec638c9Svisa 	PKO3_WR_8(node, PKO3_PDM_CFG, val);
21313ec638c9Svisa 
21323ec638c9Svisa 	PKO3_WR_8(node, PKO3_ENABLE, PKO3_ENABLE_ENABLE);
21333ec638c9Svisa 
21343ec638c9Svisa 	*pnode = node;
21353ec638c9Svisa 	return 0;
21363ec638c9Svisa 
21373ec638c9Svisa error:
21383ec638c9Svisa 	if (node->node_sso != 0)
21393ec638c9Svisa 		bus_space_unmap(node->node_iot, node->node_sso, SSO_SIZE);
21403ec638c9Svisa 	if (node->node_pko3 != 0)
21413ec638c9Svisa 		bus_space_unmap(node->node_iot, node->node_pko3, PKO3_SIZE);
21423ec638c9Svisa 	if (node->node_pki != 0)
21433ec638c9Svisa 		bus_space_unmap(node->node_iot, node->node_pki, PKI_SIZE);
21443ec638c9Svisa 	if (node->node_fpa3 != 0)
21453ec638c9Svisa 		bus_space_unmap(node->node_iot, node->node_fpa3, FPA3_SIZE);
21463ec638c9Svisa 	node->node_sso = 0;
21473ec638c9Svisa 	node->node_pko3 = 0;
21483ec638c9Svisa 	node->node_pki = 0;
21493ec638c9Svisa 	node->node_fpa3 = 0;
21503ec638c9Svisa 	return 1;
21513ec638c9Svisa }
21523ec638c9Svisa 
21533ec638c9Svisa paddr_t
ogx_fpa3_alloc(struct fpa3aura * aura)21543ec638c9Svisa ogx_fpa3_alloc(struct fpa3aura *aura)
21553ec638c9Svisa {
21563ec638c9Svisa 	uint64_t op;
21573ec638c9Svisa 
21583ec638c9Svisa 	op = FPA3_LD_IO | FPA3_LD_DID;
21593ec638c9Svisa 	op |= (uint64_t)aura->nodeid << FPA3_LD_NODE_S;
21603ec638c9Svisa 	op |= (uint64_t)aura->auraid << FPA3_LD_AURA_S;
21613ec638c9Svisa 	return octeon_xkphys_read_8(op);
21623ec638c9Svisa }
21633ec638c9Svisa 
21643ec638c9Svisa void
ogx_fpa3_free(struct fpa3aura * aura,paddr_t addr)21653ec638c9Svisa ogx_fpa3_free(struct fpa3aura *aura, paddr_t addr)
21663ec638c9Svisa {
21673ec638c9Svisa 	uint64_t op;
21683ec638c9Svisa 
21693ec638c9Svisa 	/* Flush pending writes before the block is freed. */
21703ec638c9Svisa 	octeon_syncw();
21713ec638c9Svisa 
21723ec638c9Svisa 	op = FPA3_ST_IO | FPA3_ST_DID_FPA;
21733ec638c9Svisa 	op |= (uint64_t)aura->nodeid << FPA3_ST_NODE_S;
21743ec638c9Svisa 	op |= (uint64_t)aura->auraid << FPA3_ST_AURA_S;
21753ec638c9Svisa 	octeon_xkphys_write_8(op, addr);
21763ec638c9Svisa }
21773ec638c9Svisa 
21783ec638c9Svisa void
ogx_fpa3_pool_init(struct ogx_node * node,struct fpa3pool * pool,uint32_t poolid,uint32_t nentries)21793ec638c9Svisa ogx_fpa3_pool_init(struct ogx_node *node, struct fpa3pool *pool,
21803ec638c9Svisa     uint32_t poolid, uint32_t nentries)
21813ec638c9Svisa {
21823ec638c9Svisa 	size_t segsize;
21833ec638c9Svisa 	int rsegs;
21843ec638c9Svisa 
21853ec638c9Svisa 	segsize = nentries * 16;
21863ec638c9Svisa 
21873ec638c9Svisa 	pool->nodeid = node->node_id;
21883ec638c9Svisa 	pool->poolid = poolid;
21893ec638c9Svisa 
21903ec638c9Svisa 	if (bus_dmamap_create(node->node_dmat, segsize, 1, segsize, 0,
21913ec638c9Svisa 	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &pool->dmap))
21923ec638c9Svisa 		panic("%s: out of memory", __func__);
21933ec638c9Svisa 	if (bus_dmamem_alloc(node->node_dmat, segsize, CACHELINESIZE,
21943ec638c9Svisa 	    0, &pool->dmaseg, 1, &rsegs,
21953ec638c9Svisa 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO))
21963ec638c9Svisa 		panic("%s: out of memory", __func__);
21973ec638c9Svisa 	if (bus_dmamem_map(node->node_dmat, &pool->dmaseg, 1, segsize,
21983ec638c9Svisa 	    &pool->kva, BUS_DMA_NOWAIT | BUS_DMA_COHERENT))
21993ec638c9Svisa 		panic("%s: bus_dmamem_map", __func__);
22003ec638c9Svisa 	if (bus_dmamap_load(node->node_dmat, pool->dmap, pool->kva, segsize,
22013ec638c9Svisa 	    NULL, BUS_DMA_NOWAIT))
22023ec638c9Svisa 		panic("%s: bus_dmamap_load", __func__);
22033ec638c9Svisa 
22043ec638c9Svisa 	/* Disable the pool before setup. */
22053ec638c9Svisa 	FPA3_WR_8(node, FPA3_POOL_CFG(poolid), 0);
22063ec638c9Svisa 
22073ec638c9Svisa 	/* Set permitted address range of stored pointers. */
22083ec638c9Svisa 	FPA3_WR_8(node, FPA3_POOL_START_ADDR(poolid), CACHELINESIZE);
22093ec638c9Svisa 	FPA3_WR_8(node, FPA3_POOL_END_ADDR(poolid), UINT32_MAX);
22103ec638c9Svisa 
22113ec638c9Svisa 	/* Set up the pointer stack. */
22123ec638c9Svisa 	FPA3_WR_8(node, FPA3_POOL_STACK_BASE(poolid), pool->dmaseg.ds_addr);
22133ec638c9Svisa 	FPA3_WR_8(node, FPA3_POOL_STACK_ADDR(poolid), pool->dmaseg.ds_addr);
22143ec638c9Svisa 	FPA3_WR_8(node, FPA3_POOL_STACK_END(poolid), pool->dmaseg.ds_addr +
22153ec638c9Svisa 	    pool->dmaseg.ds_len);
22163ec638c9Svisa 
22173ec638c9Svisa 	/* Re-enable the pool. */
22183ec638c9Svisa 	FPA3_WR_8(node, FPA3_POOL_CFG(poolid), FPA3_POOL_CFG_ENA);
22193ec638c9Svisa }
22203ec638c9Svisa 
22213ec638c9Svisa void
ogx_fpa3_aura_init(struct ogx_node * node,struct fpa3aura * aura,uint32_t auraid,struct fpa3pool * pool)22223ec638c9Svisa ogx_fpa3_aura_init(struct ogx_node *node, struct fpa3aura *aura,
22233ec638c9Svisa     uint32_t auraid, struct fpa3pool *pool)
22243ec638c9Svisa {
22253ec638c9Svisa 	KASSERT(node->node_id == pool->nodeid);
22263ec638c9Svisa 
22273ec638c9Svisa 	aura->nodeid = pool->nodeid;
22283ec638c9Svisa 	aura->poolid = pool->poolid;
22293ec638c9Svisa 	aura->auraid = auraid;
22303ec638c9Svisa 
22313ec638c9Svisa 	/* Enable pointer counting. */
22323ec638c9Svisa 	FPA3_WR_8(node, FPA3_AURA_CFG(aura->auraid), 0);
22333ec638c9Svisa 	FPA3_WR_8(node, FPA3_AURA_CNT(aura->auraid), 1024);
22343ec638c9Svisa 	FPA3_WR_8(node, FPA3_AURA_CNT_LIMIT(aura->auraid), 1024);
22353ec638c9Svisa 
22363ec638c9Svisa 	/* Set the backend pool. */
22373ec638c9Svisa 	FPA3_WR_8(node, FPA3_AURA_POOL(aura->auraid), aura->poolid);
22383ec638c9Svisa }
22393ec638c9Svisa 
22403ec638c9Svisa void
ogx_fpa3_aura_load(struct ogx_node * node,struct fpa3aura * aura,size_t nelem,size_t size)22413ec638c9Svisa ogx_fpa3_aura_load(struct ogx_node *node, struct fpa3aura *aura, size_t nelem,
22423ec638c9Svisa     size_t size)
22433ec638c9Svisa {
22443ec638c9Svisa 	paddr_t addr;
22453ec638c9Svisa 	caddr_t kva;
22463ec638c9Svisa 	size_t i;
22473ec638c9Svisa 	size_t totsize;
22483ec638c9Svisa 	int rsegs;
22493ec638c9Svisa 
22503ec638c9Svisa 	KASSERT(size % CACHELINESIZE == 0);
22513ec638c9Svisa 
22523ec638c9Svisa 	if (nelem > SIZE_MAX / size)
22533ec638c9Svisa 		panic("%s: too large allocation", __func__);
22543ec638c9Svisa 	totsize = nelem * size;
22553ec638c9Svisa 
22563ec638c9Svisa 	if (bus_dmamap_create(node->node_dmat, totsize, 1, totsize, 0,
22573ec638c9Svisa 	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &aura->dmap))
22583ec638c9Svisa 		panic("%s: out of memory", __func__);
22593ec638c9Svisa 	if (bus_dmamem_alloc(node->node_dmat, totsize, CACHELINESIZE, 0,
22603ec638c9Svisa 	    &aura->dmaseg, 1, &rsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO))
22613ec638c9Svisa 		panic("%s: out of memory", __func__);
22623ec638c9Svisa 	if (bus_dmamem_map(node->node_dmat, &aura->dmaseg, rsegs, totsize,
22633ec638c9Svisa 	    &kva, BUS_DMA_NOWAIT | BUS_DMA_COHERENT))
22643ec638c9Svisa 		panic("%s: bus_dmamem_map failed", __func__);
22653ec638c9Svisa 	if (bus_dmamap_load(node->node_dmat, aura->dmap, kva, totsize, NULL,
22663ec638c9Svisa 	    BUS_DMA_NOWAIT))
22673ec638c9Svisa 		panic("%s: bus_dmamap_load failed", __func__);
22683ec638c9Svisa 
22693ec638c9Svisa 	for (i = 0, addr = aura->dmaseg.ds_addr; i < nelem; i++, addr += size)
22703ec638c9Svisa 		ogx_fpa3_free(aura, addr);
22713ec638c9Svisa }
22723ec638c9Svisa 
22733ec638c9Svisa int
ogx_node_load_firmware(struct ogx_node * node)22743ec638c9Svisa ogx_node_load_firmware(struct ogx_node *node)
22753ec638c9Svisa {
22763ec638c9Svisa 	struct ogx_fwhdr *fw;
22773ec638c9Svisa 	uint8_t *ucode = NULL;
22783ec638c9Svisa 	size_t size = 0;
22793ec638c9Svisa 	uint64_t *imem, val;
22803ec638c9Svisa 	int cl, error = 0, i;
22813ec638c9Svisa 
22823ec638c9Svisa 	rw_enter_write(&node->node_lock);
22833ec638c9Svisa 	if (node->node_flags & NODE_FWREADY)
22843ec638c9Svisa 		goto out;
22853ec638c9Svisa 
22863ec638c9Svisa 	error = loadfirmware("ogx-pki-cluster", &ucode, &size);
22873ec638c9Svisa 	if (error != 0) {
22883ec638c9Svisa 		printf("ogx node%llu: could not load firmware, error %d\n",
22893ec638c9Svisa 		    node->node_id, error);
22903ec638c9Svisa 		goto out;
22913ec638c9Svisa 	}
22923ec638c9Svisa 
22933ec638c9Svisa 	fw = (struct ogx_fwhdr *)ucode;
22943ec638c9Svisa 	if (size < sizeof(*fw) || fw->fw_size != size - sizeof(*fw)) {
22953ec638c9Svisa 		printf("ogx node%llu: invalid firmware\n", node->node_id);
22963ec638c9Svisa 		error = EINVAL;
22973ec638c9Svisa 		goto out;
22983ec638c9Svisa 	}
22993ec638c9Svisa 
23003ec638c9Svisa 	imem = (uint64_t *)(fw + 1);
23013ec638c9Svisa 	for (i = 0; i < fw->fw_size / sizeof(uint64_t); i++)
23023ec638c9Svisa 		PKI_WR_8(node, PKI_IMEM(i), imem[i]);
23033ec638c9Svisa 
23043ec638c9Svisa 	/* Enable the parsing clusters. */
23053ec638c9Svisa 	for (cl = 0; cl < node->node_cfg->cfg_nclusters; cl++) {
23063ec638c9Svisa 		val = PKI_RD_8(node, PKI_ICG_CFG(cl));
23073ec638c9Svisa 		val |= PKI_ICG_CFG_PENA;
23083ec638c9Svisa 		PKI_WR_8(node, PKI_ICG_CFG(cl), val);
23093ec638c9Svisa 	}
23103ec638c9Svisa 
23113ec638c9Svisa 	node->node_flags |= NODE_FWREADY;
23123ec638c9Svisa 
23133ec638c9Svisa out:
23143ec638c9Svisa 	free(ucode, M_DEVBUF, size);
23153ec638c9Svisa 	rw_exit_write(&node->node_lock);
23163ec638c9Svisa 	return error;
23173ec638c9Svisa }
2318