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