/* $OpenBSD: if_mvpp.c,v 1.53 2024/05/13 01:15:50 jsg Exp $ */ /* * Copyright (c) 2008, 2019 Mark Kettenis * Copyright (c) 2017, 2020 Patrick Wildt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copyright (C) 2016 Marvell International Ltd. * * Marvell BSD License Option * * If you received this File from Marvell, you may opt to use, redistribute * and/or modify this File under the following licensing terms. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Marvell nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include #include #include #include #include #include struct mvpp2_buf { bus_dmamap_t mb_map; struct mbuf *mb_m; }; #define MVPP2_NTXDESC 512 #define MVPP2_NTXSEGS 16 #define MVPP2_NRXDESC 512 struct mvpp2_bm_pool { struct mvpp2_dmamem *bm_mem; struct mvpp2_buf *rxbuf; uint32_t *freelist; int free_prod; int free_cons; }; #define MVPP2_BM_SIZE 64 #define MVPP2_BM_POOL_PTR_ALIGN 128 #define MVPP2_BM_POOLS_NUM 8 #define MVPP2_BM_ALIGN 32 struct mvpp2_tx_queue { uint8_t id; uint8_t log_id; struct mvpp2_dmamem *ring; struct mvpp2_buf *buf; struct mvpp2_tx_desc *descs; int prod; int cons; uint32_t done_pkts_coal; }; struct mvpp2_rx_queue { uint8_t id; struct mvpp2_dmamem *ring; struct mvpp2_rx_desc *descs; int prod; struct if_rxring rxring; int cons; uint32_t pkts_coal; uint32_t time_coal; }; struct mvpp2_dmamem { bus_dmamap_t mdm_map; bus_dma_segment_t mdm_seg; size_t mdm_size; caddr_t mdm_kva; }; #define MVPP2_DMA_MAP(_mdm) ((_mdm)->mdm_map) #define MVPP2_DMA_LEN(_mdm) ((_mdm)->mdm_size) #define MVPP2_DMA_DVA(_mdm) ((_mdm)->mdm_map->dm_segs[0].ds_addr) #define MVPP2_DMA_KVA(_mdm) ((void *)(_mdm)->mdm_kva) struct mvpp2_port; struct mvpp2_softc { struct device sc_dev; int sc_node; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh_base; bus_space_handle_t sc_ioh_iface; paddr_t sc_ioh_paddr; bus_size_t sc_iosize_base; bus_size_t sc_iosize_iface; bus_dma_tag_t sc_dmat; struct regmap *sc_rm; uint32_t sc_tclk; struct mvpp2_bm_pool *sc_bm_pools; int sc_npools; struct mvpp2_prs_shadow *sc_prs_shadow; uint8_t *sc_prs_double_vlans; int sc_aggr_ntxq; struct mvpp2_tx_queue *sc_aggr_txqs; struct mvpp2_port **sc_ports; }; struct mvpp2_port { struct device sc_dev; struct mvpp2_softc *sc; int sc_node; bus_dma_tag_t sc_dmat; int sc_id; int sc_gop_id; struct arpcom sc_ac; #define sc_lladdr sc_ac.ac_enaddr struct mii_data sc_mii; #define sc_media sc_mii.mii_media struct mii_bus *sc_mdio; enum { PHY_MODE_XAUI, PHY_MODE_10GBASER, PHY_MODE_2500BASEX, PHY_MODE_1000BASEX, PHY_MODE_SGMII, PHY_MODE_RGMII, PHY_MODE_RGMII_ID, PHY_MODE_RGMII_RXID, PHY_MODE_RGMII_TXID, } sc_phy_mode; int sc_fixed_link; int sc_inband_status; int sc_link; int sc_phyloc; int sc_sfp; int sc_ntxq; int sc_nrxq; struct mvpp2_tx_queue *sc_txqs; struct mvpp2_rx_queue *sc_rxqs; struct timeout sc_tick; uint32_t sc_tx_time_coal; }; #define MVPP2_MAX_PORTS 4 struct mvpp2_attach_args { int ma_node; bus_dma_tag_t ma_dmat; }; #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) static struct rwlock mvpp2_sff_lock = RWLOCK_INITIALIZER("mvpp2sff"); int mvpp2_match(struct device *, void *, void *); void mvpp2_attach(struct device *, struct device *, void *); void mvpp2_attach_deferred(struct device *); const struct cfattach mvppc_ca = { sizeof(struct mvpp2_softc), mvpp2_match, mvpp2_attach }; struct cfdriver mvppc_cd = { NULL, "mvppc", DV_DULL }; int mvpp2_port_match(struct device *, void *, void *); void mvpp2_port_attach(struct device *, struct device *, void *); const struct cfattach mvpp_ca = { sizeof(struct mvpp2_port), mvpp2_port_match, mvpp2_port_attach }; struct cfdriver mvpp_cd = { NULL, "mvpp", DV_IFNET }; void mvpp2_port_attach_sfp(struct device *); uint32_t mvpp2_read(struct mvpp2_softc *, bus_addr_t); void mvpp2_write(struct mvpp2_softc *, bus_addr_t, uint32_t); uint32_t mvpp2_gmac_read(struct mvpp2_port *, bus_addr_t); void mvpp2_gmac_write(struct mvpp2_port *, bus_addr_t, uint32_t); uint32_t mvpp2_xlg_read(struct mvpp2_port *, bus_addr_t); void mvpp2_xlg_write(struct mvpp2_port *, bus_addr_t, uint32_t); uint32_t mvpp2_xpcs_read(struct mvpp2_port *, bus_addr_t); void mvpp2_xpcs_write(struct mvpp2_port *, bus_addr_t, uint32_t); uint32_t mvpp2_mpcs_read(struct mvpp2_port *, bus_addr_t); void mvpp2_mpcs_write(struct mvpp2_port *, bus_addr_t, uint32_t); int mvpp2_ioctl(struct ifnet *, u_long, caddr_t); void mvpp2_start(struct ifnet *); int mvpp2_rxrinfo(struct mvpp2_port *, struct if_rxrinfo *); void mvpp2_watchdog(struct ifnet *); int mvpp2_media_change(struct ifnet *); void mvpp2_media_status(struct ifnet *, struct ifmediareq *); int mvpp2_mii_readreg(struct device *, int, int); void mvpp2_mii_writereg(struct device *, int, int, int); void mvpp2_mii_statchg(struct device *); void mvpp2_inband_statchg(struct mvpp2_port *); void mvpp2_port_change(struct mvpp2_port *); void mvpp2_tick(void *); int mvpp2_link_intr(void *); int mvpp2_intr(void *); void mvpp2_tx_proc(struct mvpp2_port *, uint8_t); void mvpp2_txq_proc(struct mvpp2_port *, struct mvpp2_tx_queue *); void mvpp2_rx_proc(struct mvpp2_port *, uint8_t); void mvpp2_rxq_proc(struct mvpp2_port *, struct mvpp2_rx_queue *); void mvpp2_rx_refill(struct mvpp2_port *); void mvpp2_up(struct mvpp2_port *); void mvpp2_down(struct mvpp2_port *); void mvpp2_iff(struct mvpp2_port *); void mvpp2_aggr_txq_hw_init(struct mvpp2_softc *, struct mvpp2_tx_queue *); void mvpp2_txq_hw_init(struct mvpp2_port *, struct mvpp2_tx_queue *); void mvpp2_rxq_hw_init(struct mvpp2_port *, struct mvpp2_rx_queue *); void mvpp2_txq_hw_deinit(struct mvpp2_port *, struct mvpp2_tx_queue *); void mvpp2_rxq_hw_drop(struct mvpp2_port *, struct mvpp2_rx_queue *); void mvpp2_rxq_hw_deinit(struct mvpp2_port *, struct mvpp2_rx_queue *); void mvpp2_rxq_long_pool_set(struct mvpp2_port *, int, int); void mvpp2_rxq_short_pool_set(struct mvpp2_port *, int, int); void mvpp2_mac_reset_assert(struct mvpp2_port *); void mvpp2_pcs_reset_assert(struct mvpp2_port *); void mvpp2_pcs_reset_deassert(struct mvpp2_port *); void mvpp2_mac_config(struct mvpp2_port *); void mvpp2_xlg_config(struct mvpp2_port *); void mvpp2_gmac_config(struct mvpp2_port *); void mvpp2_comphy_config(struct mvpp2_port *, int); void mvpp2_gop_config(struct mvpp2_port *); void mvpp2_gop_intr_mask(struct mvpp2_port *); void mvpp2_gop_intr_unmask(struct mvpp2_port *); struct mvpp2_dmamem * mvpp2_dmamem_alloc(struct mvpp2_softc *, bus_size_t, bus_size_t); void mvpp2_dmamem_free(struct mvpp2_softc *, struct mvpp2_dmamem *); struct mbuf *mvpp2_alloc_mbuf(struct mvpp2_softc *, bus_dmamap_t); void mvpp2_interrupts_enable(struct mvpp2_port *, int); void mvpp2_interrupts_disable(struct mvpp2_port *, int); int mvpp2_egress_port(struct mvpp2_port *); int mvpp2_txq_phys(int, int); void mvpp2_defaults_set(struct mvpp2_port *); void mvpp2_ingress_enable(struct mvpp2_port *); void mvpp2_ingress_disable(struct mvpp2_port *); void mvpp2_egress_enable(struct mvpp2_port *); void mvpp2_egress_disable(struct mvpp2_port *); void mvpp2_port_enable(struct mvpp2_port *); void mvpp2_port_disable(struct mvpp2_port *); void mvpp2_rxq_status_update(struct mvpp2_port *, int, int, int); int mvpp2_rxq_received(struct mvpp2_port *, int); void mvpp2_rxq_offset_set(struct mvpp2_port *, int, int); void mvpp2_txp_max_tx_size_set(struct mvpp2_port *); void mvpp2_rx_pkts_coal_set(struct mvpp2_port *, struct mvpp2_rx_queue *, uint32_t); void mvpp2_tx_pkts_coal_set(struct mvpp2_port *, struct mvpp2_tx_queue *, uint32_t); void mvpp2_rx_time_coal_set(struct mvpp2_port *, struct mvpp2_rx_queue *, uint32_t); void mvpp2_tx_time_coal_set(struct mvpp2_port *, uint32_t); void mvpp2_axi_config(struct mvpp2_softc *); void mvpp2_bm_pool_init(struct mvpp2_softc *); void mvpp2_rx_fifo_init(struct mvpp2_softc *); void mvpp2_tx_fifo_init(struct mvpp2_softc *); int mvpp2_prs_default_init(struct mvpp2_softc *); void mvpp2_prs_hw_inv(struct mvpp2_softc *, int); void mvpp2_prs_hw_port_init(struct mvpp2_softc *, int, int, int, int); void mvpp2_prs_def_flow_init(struct mvpp2_softc *); void mvpp2_prs_mh_init(struct mvpp2_softc *); void mvpp2_prs_mac_init(struct mvpp2_softc *); void mvpp2_prs_dsa_init(struct mvpp2_softc *); int mvpp2_prs_etype_init(struct mvpp2_softc *); int mvpp2_prs_vlan_init(struct mvpp2_softc *); int mvpp2_prs_pppoe_init(struct mvpp2_softc *); int mvpp2_prs_ip6_init(struct mvpp2_softc *); int mvpp2_prs_ip4_init(struct mvpp2_softc *); void mvpp2_prs_shadow_ri_set(struct mvpp2_softc *, int, uint32_t, uint32_t); void mvpp2_prs_tcam_lu_set(struct mvpp2_prs_entry *, uint32_t); void mvpp2_prs_tcam_port_set(struct mvpp2_prs_entry *, uint32_t, int); void mvpp2_prs_tcam_port_map_set(struct mvpp2_prs_entry *, uint32_t); uint32_t mvpp2_prs_tcam_port_map_get(struct mvpp2_prs_entry *); void mvpp2_prs_tcam_data_byte_set(struct mvpp2_prs_entry *, uint32_t, uint8_t, uint8_t); void mvpp2_prs_tcam_data_byte_get(struct mvpp2_prs_entry *, uint32_t, uint8_t *, uint8_t *); int mvpp2_prs_tcam_data_cmp(struct mvpp2_prs_entry *, int, uint16_t); void mvpp2_prs_tcam_ai_update(struct mvpp2_prs_entry *, uint32_t, uint32_t); int mvpp2_prs_sram_ai_get(struct mvpp2_prs_entry *); int mvpp2_prs_tcam_ai_get(struct mvpp2_prs_entry *); void mvpp2_prs_tcam_data_word_get(struct mvpp2_prs_entry *, uint32_t, uint32_t *, uint32_t *); void mvpp2_prs_match_etype(struct mvpp2_prs_entry *, uint32_t, uint16_t); int mvpp2_prs_sram_ri_get(struct mvpp2_prs_entry *); void mvpp2_prs_sram_ai_update(struct mvpp2_prs_entry *, uint32_t, uint32_t); void mvpp2_prs_sram_ri_update(struct mvpp2_prs_entry *, uint32_t, uint32_t); void mvpp2_prs_sram_bits_set(struct mvpp2_prs_entry *, uint32_t, uint32_t); void mvpp2_prs_sram_bits_clear(struct mvpp2_prs_entry *, uint32_t, uint32_t); void mvpp2_prs_sram_shift_set(struct mvpp2_prs_entry *, int, uint32_t); void mvpp2_prs_sram_offset_set(struct mvpp2_prs_entry *, uint32_t, int, uint32_t); void mvpp2_prs_sram_next_lu_set(struct mvpp2_prs_entry *, uint32_t); void mvpp2_prs_shadow_set(struct mvpp2_softc *, int, uint32_t); int mvpp2_prs_hw_write(struct mvpp2_softc *, struct mvpp2_prs_entry *); int mvpp2_prs_hw_read(struct mvpp2_softc *, struct mvpp2_prs_entry *, int); int mvpp2_prs_flow_find(struct mvpp2_softc *, int); int mvpp2_prs_tcam_first_free(struct mvpp2_softc *, uint8_t, uint8_t); void mvpp2_prs_mac_drop_all_set(struct mvpp2_softc *, uint32_t, int); void mvpp2_prs_mac_promisc_set(struct mvpp2_softc *, uint32_t, int, int); void mvpp2_prs_dsa_tag_set(struct mvpp2_softc *, uint32_t, int, int, int); void mvpp2_prs_dsa_tag_ethertype_set(struct mvpp2_softc *, uint32_t, int, int, int); struct mvpp2_prs_entry *mvpp2_prs_vlan_find(struct mvpp2_softc *, uint16_t, int); int mvpp2_prs_vlan_add(struct mvpp2_softc *, uint16_t, int, uint32_t); int mvpp2_prs_double_vlan_ai_free_get(struct mvpp2_softc *); struct mvpp2_prs_entry *mvpp2_prs_double_vlan_find(struct mvpp2_softc *, uint16_t, uint16_t); int mvpp2_prs_double_vlan_add(struct mvpp2_softc *, uint16_t, uint16_t, uint32_t); int mvpp2_prs_ip4_proto(struct mvpp2_softc *, uint16_t, uint32_t, uint32_t); int mvpp2_prs_ip4_cast(struct mvpp2_softc *, uint16_t); int mvpp2_prs_ip6_proto(struct mvpp2_softc *, uint16_t, uint32_t, uint32_t); int mvpp2_prs_ip6_cast(struct mvpp2_softc *, uint16_t); int mvpp2_prs_mac_da_range_find(struct mvpp2_softc *, int, const uint8_t *, uint8_t *, int); int mvpp2_prs_mac_range_equals(struct mvpp2_prs_entry *, const uint8_t *, uint8_t *); int mvpp2_prs_mac_da_accept(struct mvpp2_port *, const uint8_t *, int); void mvpp2_prs_mac_del_all(struct mvpp2_port *); int mvpp2_prs_tag_mode_set(struct mvpp2_softc *, int, int); int mvpp2_prs_def_flow(struct mvpp2_port *); void mvpp2_cls_flow_write(struct mvpp2_softc *, struct mvpp2_cls_flow_entry *); void mvpp2_cls_lookup_write(struct mvpp2_softc *, struct mvpp2_cls_lookup_entry *); void mvpp2_cls_init(struct mvpp2_softc *); void mvpp2_cls_port_config(struct mvpp2_port *); void mvpp2_cls_oversize_rxq_set(struct mvpp2_port *); int mvpp2_match(struct device *parent, void *cfdata, void *aux) { struct fdt_attach_args *faa = aux; return OF_is_compatible(faa->fa_node, "marvell,armada-7k-pp22"); } void mvpp2_attach(struct device *parent, struct device *self, void *aux) { struct mvpp2_softc *sc = (void *)self; struct fdt_attach_args *faa = aux; if (faa->fa_nreg < 2) { printf(": no registers\n"); return; } sc->sc_node = faa->fa_node; sc->sc_iot = faa->fa_iot; sc->sc_dmat = faa->fa_dmat; if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh_base)) { printf(": can't map registers\n"); return; } sc->sc_iosize_base = faa->fa_reg[0].size; sc->sc_ioh_paddr = bus_space_mmap(sc->sc_iot, faa->fa_reg[0].addr, 0, PROT_READ | PROT_WRITE, 0); KASSERT(sc->sc_ioh_paddr != -1); sc->sc_ioh_paddr &= PMAP_PA_MASK; if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, faa->fa_reg[1].size, 0, &sc->sc_ioh_iface)) { printf(": can't map registers\n"); bus_space_unmap(sc->sc_iot, sc->sc_ioh_base, sc->sc_iosize_base); return; } sc->sc_iosize_iface = faa->fa_reg[1].size; sc->sc_rm = regmap_byphandle(OF_getpropint(faa->fa_node, "marvell,system-controller", 0)); clock_enable_all(faa->fa_node); sc->sc_tclk = clock_get_frequency(faa->fa_node, "pp_clk"); printf("\n"); config_defer(self, mvpp2_attach_deferred); } void mvpp2_attach_deferred(struct device *self) { struct mvpp2_softc *sc = (void *)self; struct mvpp2_attach_args maa; struct mvpp2_tx_queue *txq; int i, node; mvpp2_axi_config(sc); bus_space_write_4(sc->sc_iot, sc->sc_ioh_iface, MVPP22_SMI_MISC_CFG_REG, bus_space_read_4(sc->sc_iot, sc->sc_ioh_iface, MVPP22_SMI_MISC_CFG_REG) & ~MVPP22_SMI_POLLING_EN); sc->sc_aggr_ntxq = 1; sc->sc_aggr_txqs = mallocarray(sc->sc_aggr_ntxq, sizeof(*sc->sc_aggr_txqs), M_DEVBUF, M_WAITOK | M_ZERO); for (i = 0; i < sc->sc_aggr_ntxq; i++) { txq = &sc->sc_aggr_txqs[i]; txq->id = i; mvpp2_aggr_txq_hw_init(sc, txq); } mvpp2_rx_fifo_init(sc); mvpp2_tx_fifo_init(sc); mvpp2_write(sc, MVPP2_TX_SNOOP_REG, 0x1); mvpp2_bm_pool_init(sc); sc->sc_prs_shadow = mallocarray(MVPP2_PRS_TCAM_SRAM_SIZE, sizeof(*sc->sc_prs_shadow), M_DEVBUF, M_WAITOK | M_ZERO); mvpp2_prs_default_init(sc); mvpp2_cls_init(sc); memset(&maa, 0, sizeof(maa)); for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) { maa.ma_node = node; maa.ma_dmat = sc->sc_dmat; config_found(self, &maa, NULL); } } void mvpp2_axi_config(struct mvpp2_softc *sc) { uint32_t reg; mvpp2_write(sc, MVPP22_BM_ADDR_HIGH_RLS_REG, 0); reg = (MVPP22_AXI_CODE_CACHE_WR_CACHE << MVPP22_AXI_ATTR_CACHE_OFFS) | (MVPP22_AXI_CODE_DOMAIN_OUTER_DOM << MVPP22_AXI_ATTR_DOMAIN_OFFS); mvpp2_write(sc, MVPP22_AXI_BM_WR_ATTR_REG, reg); mvpp2_write(sc, MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG, reg); mvpp2_write(sc, MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG, reg); mvpp2_write(sc, MVPP22_AXI_RX_DATA_WR_ATTR_REG, reg); reg = (MVPP22_AXI_CODE_CACHE_RD_CACHE << MVPP22_AXI_ATTR_CACHE_OFFS) | (MVPP22_AXI_CODE_DOMAIN_OUTER_DOM << MVPP22_AXI_ATTR_DOMAIN_OFFS); mvpp2_write(sc, MVPP22_AXI_BM_RD_ATTR_REG, reg); mvpp2_write(sc, MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG, reg); mvpp2_write(sc, MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG, reg); mvpp2_write(sc, MVPP22_AXI_TX_DATA_RD_ATTR_REG, reg); reg = (MVPP22_AXI_CODE_CACHE_NON_CACHE << MVPP22_AXI_CODE_CACHE_OFFS) | (MVPP22_AXI_CODE_DOMAIN_SYSTEM << MVPP22_AXI_CODE_DOMAIN_OFFS); mvpp2_write(sc, MVPP22_AXI_RD_NORMAL_CODE_REG, reg); mvpp2_write(sc, MVPP22_AXI_WR_NORMAL_CODE_REG, reg); reg = (MVPP22_AXI_CODE_CACHE_RD_CACHE << MVPP22_AXI_CODE_CACHE_OFFS) | (MVPP22_AXI_CODE_DOMAIN_OUTER_DOM << MVPP22_AXI_CODE_DOMAIN_OFFS); mvpp2_write(sc, MVPP22_AXI_RD_SNOOP_CODE_REG, reg); reg = (MVPP22_AXI_CODE_CACHE_WR_CACHE << MVPP22_AXI_CODE_CACHE_OFFS) | (MVPP22_AXI_CODE_DOMAIN_OUTER_DOM << MVPP22_AXI_CODE_DOMAIN_OFFS); mvpp2_write(sc, MVPP22_AXI_WR_SNOOP_CODE_REG, reg); } void mvpp2_bm_pool_init(struct mvpp2_softc *sc) { struct mvpp2_bm_pool *bm; struct mvpp2_buf *rxb; uint64_t phys, virt; int i, j, inuse; for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) { mvpp2_write(sc, MVPP2_BM_INTR_MASK_REG(i), 0); mvpp2_write(sc, MVPP2_BM_INTR_CAUSE_REG(i), 0); } sc->sc_npools = ncpus; sc->sc_npools = min(sc->sc_npools, MVPP2_BM_POOLS_NUM); sc->sc_bm_pools = mallocarray(sc->sc_npools, sizeof(*sc->sc_bm_pools), M_DEVBUF, M_WAITOK | M_ZERO); for (i = 0; i < sc->sc_npools; i++) { bm = &sc->sc_bm_pools[i]; bm->bm_mem = mvpp2_dmamem_alloc(sc, MVPP2_BM_SIZE * sizeof(uint64_t) * 2, MVPP2_BM_POOL_PTR_ALIGN); KASSERT(bm->bm_mem != NULL); bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(bm->bm_mem), 0, MVPP2_DMA_LEN(bm->bm_mem), BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); mvpp2_write(sc, MVPP2_BM_POOL_CTRL_REG(i), mvpp2_read(sc, MVPP2_BM_POOL_CTRL_REG(i)) | MVPP2_BM_STOP_MASK); mvpp2_write(sc, MVPP2_BM_POOL_BASE_REG(i), (uint64_t)MVPP2_DMA_DVA(bm->bm_mem) & 0xffffffff); mvpp2_write(sc, MVPP22_BM_POOL_BASE_HIGH_REG, ((uint64_t)MVPP2_DMA_DVA(bm->bm_mem) >> 32) & MVPP22_BM_POOL_BASE_HIGH_MASK); mvpp2_write(sc, MVPP2_BM_POOL_SIZE_REG(i), MVPP2_BM_SIZE); mvpp2_write(sc, MVPP2_BM_POOL_CTRL_REG(i), mvpp2_read(sc, MVPP2_BM_POOL_CTRL_REG(i)) | MVPP2_BM_START_MASK); /* * U-Boot might not have cleaned its pools. The pool needs * to be empty before we fill it, otherwise our packets are * written to wherever U-Boot allocated memory. Cleaning it * up ourselves is worrying as well, since the BM's pages are * probably in our own memory. Best we can do is stop the BM, * set new memory and drain the pool. */ inuse = mvpp2_read(sc, MVPP2_BM_POOL_PTRS_NUM_REG(i)) & MVPP2_BM_POOL_PTRS_NUM_MASK; inuse += mvpp2_read(sc, MVPP2_BM_BPPI_PTRS_NUM_REG(i)) & MVPP2_BM_BPPI_PTRS_NUM_MASK; if (inuse) inuse++; for (j = 0; j < inuse; j++) mvpp2_read(sc, MVPP2_BM_PHY_ALLOC_REG(i)); mvpp2_write(sc, MVPP2_POOL_BUF_SIZE_REG(i), roundup(MCLBYTES, 1 << MVPP2_POOL_BUF_SIZE_OFFSET)); bm->rxbuf = mallocarray(MVPP2_BM_SIZE, sizeof(struct mvpp2_buf), M_DEVBUF, M_WAITOK); bm->freelist = mallocarray(MVPP2_BM_SIZE, sizeof(*bm->freelist), M_DEVBUF, M_WAITOK | M_ZERO); for (j = 0; j < MVPP2_BM_SIZE; j++) { rxb = &bm->rxbuf[j]; bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0, BUS_DMA_WAITOK, &rxb->mb_map); rxb->mb_m = NULL; } /* Use pool-id and rxbuf index as cookie. */ for (j = 0; j < MVPP2_BM_SIZE; j++) bm->freelist[j] = (i << 16) | (j << 0); for (j = 0; j < MVPP2_BM_SIZE; j++) { rxb = &bm->rxbuf[j]; rxb->mb_m = mvpp2_alloc_mbuf(sc, rxb->mb_map); if (rxb->mb_m == NULL) break; KASSERT(bm->freelist[bm->free_cons] != -1); virt = bm->freelist[bm->free_cons]; bm->freelist[bm->free_cons] = -1; bm->free_cons = (bm->free_cons + 1) % MVPP2_BM_SIZE; phys = rxb->mb_map->dm_segs[0].ds_addr; mvpp2_write(sc, MVPP22_BM_ADDR_HIGH_RLS_REG, (((virt >> 32) & MVPP22_ADDR_HIGH_MASK) << MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) | ((phys >> 32) & MVPP22_ADDR_HIGH_MASK)); mvpp2_write(sc, MVPP2_BM_VIRT_RLS_REG, virt & 0xffffffff); mvpp2_write(sc, MVPP2_BM_PHY_RLS_REG(i), phys & 0xffffffff); } } } void mvpp2_rx_fifo_init(struct mvpp2_softc *sc) { int i; mvpp2_write(sc, MVPP2_RX_DATA_FIFO_SIZE_REG(0), MVPP2_RX_FIFO_PORT_DATA_SIZE_32KB); mvpp2_write(sc, MVPP2_RX_ATTR_FIFO_SIZE_REG(0), MVPP2_RX_FIFO_PORT_ATTR_SIZE_32KB); mvpp2_write(sc, MVPP2_RX_DATA_FIFO_SIZE_REG(1), MVPP2_RX_FIFO_PORT_DATA_SIZE_8KB); mvpp2_write(sc, MVPP2_RX_ATTR_FIFO_SIZE_REG(1), MVPP2_RX_FIFO_PORT_ATTR_SIZE_8KB); for (i = 2; i < MVPP2_MAX_PORTS; i++) { mvpp2_write(sc, MVPP2_RX_DATA_FIFO_SIZE_REG(i), MVPP2_RX_FIFO_PORT_DATA_SIZE_4KB); mvpp2_write(sc, MVPP2_RX_ATTR_FIFO_SIZE_REG(i), MVPP2_RX_FIFO_PORT_ATTR_SIZE_4KB); } mvpp2_write(sc, MVPP2_RX_MIN_PKT_SIZE_REG, MVPP2_RX_FIFO_PORT_MIN_PKT); mvpp2_write(sc, MVPP2_RX_FIFO_INIT_REG, 0x1); } void mvpp2_tx_fifo_init(struct mvpp2_softc *sc) { int i; mvpp2_write(sc, MVPP22_TX_FIFO_SIZE_REG(0), MVPP22_TX_FIFO_DATA_SIZE_10KB); mvpp2_write(sc, MVPP22_TX_FIFO_THRESH_REG(0), MVPP2_TX_FIFO_THRESHOLD_10KB); for (i = 1; i < MVPP2_MAX_PORTS; i++) { mvpp2_write(sc, MVPP22_TX_FIFO_SIZE_REG(i), MVPP22_TX_FIFO_DATA_SIZE_3KB); mvpp2_write(sc, MVPP22_TX_FIFO_THRESH_REG(i), MVPP2_TX_FIFO_THRESHOLD_3KB); } } int mvpp2_prs_default_init(struct mvpp2_softc *sc) { int i, j, ret; mvpp2_write(sc, MVPP2_PRS_TCAM_CTRL_REG, MVPP2_PRS_TCAM_EN_MASK); for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) { mvpp2_write(sc, MVPP2_PRS_TCAM_IDX_REG, i); for (j = 0; j < MVPP2_PRS_TCAM_WORDS; j++) mvpp2_write(sc, MVPP2_PRS_TCAM_DATA_REG(j), 0); mvpp2_write(sc, MVPP2_PRS_SRAM_IDX_REG, i); for (j = 0; j < MVPP2_PRS_SRAM_WORDS; j++) mvpp2_write(sc, MVPP2_PRS_SRAM_DATA_REG(j), 0); } for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) mvpp2_prs_hw_inv(sc, i); for (i = 0; i < MVPP2_MAX_PORTS; i++) mvpp2_prs_hw_port_init(sc, i, MVPP2_PRS_LU_MH, MVPP2_PRS_PORT_LU_MAX, 0); mvpp2_prs_def_flow_init(sc); mvpp2_prs_mh_init(sc); mvpp2_prs_mac_init(sc); mvpp2_prs_dsa_init(sc); ret = mvpp2_prs_etype_init(sc); if (ret) return ret; ret = mvpp2_prs_vlan_init(sc); if (ret) return ret; ret = mvpp2_prs_pppoe_init(sc); if (ret) return ret; ret = mvpp2_prs_ip6_init(sc); if (ret) return ret; ret = mvpp2_prs_ip4_init(sc); if (ret) return ret; return 0; } void mvpp2_prs_hw_inv(struct mvpp2_softc *sc, int index) { mvpp2_write(sc, MVPP2_PRS_TCAM_IDX_REG, index); mvpp2_write(sc, MVPP2_PRS_TCAM_DATA_REG(MVPP2_PRS_TCAM_INV_WORD), MVPP2_PRS_TCAM_INV_MASK); } void mvpp2_prs_hw_port_init(struct mvpp2_softc *sc, int port, int lu_first, int lu_max, int offset) { uint32_t reg; reg = mvpp2_read(sc, MVPP2_PRS_INIT_LOOKUP_REG); reg &= ~MVPP2_PRS_PORT_LU_MASK(port); reg |= MVPP2_PRS_PORT_LU_VAL(port, lu_first); mvpp2_write(sc, MVPP2_PRS_INIT_LOOKUP_REG, reg); reg = mvpp2_read(sc, MVPP2_PRS_MAX_LOOP_REG(port)); reg &= ~MVPP2_PRS_MAX_LOOP_MASK(port); reg |= MVPP2_PRS_MAX_LOOP_VAL(port, lu_max); mvpp2_write(sc, MVPP2_PRS_MAX_LOOP_REG(port), reg); reg = mvpp2_read(sc, MVPP2_PRS_INIT_OFFS_REG(port)); reg &= ~MVPP2_PRS_INIT_OFF_MASK(port); reg |= MVPP2_PRS_INIT_OFF_VAL(port, offset); mvpp2_write(sc, MVPP2_PRS_INIT_OFFS_REG(port), reg); } void mvpp2_prs_def_flow_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; int i; for (i = 0; i < MVPP2_MAX_PORTS; i++) { memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS); pe.index = MVPP2_PE_FIRST_DEFAULT_FLOW - i; mvpp2_prs_tcam_port_map_set(&pe, 0); mvpp2_prs_sram_ai_update(&pe, i, MVPP2_PRS_FLOW_ID_MASK); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_FLOWS); mvpp2_prs_hw_write(sc, &pe); } } void mvpp2_prs_mh_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; memset(&pe, 0, sizeof(pe)); pe.index = MVPP2_PE_MH_DEFAULT; mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MH); mvpp2_prs_sram_shift_set(&pe, MVPP2_MH_SIZE, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_MAC); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_MH); mvpp2_prs_hw_write(sc, &pe); } void mvpp2_prs_mac_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; memset(&pe, 0, sizeof(pe)); pe.index = MVPP2_PE_MAC_NON_PROMISCUOUS; mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, MVPP2_PRS_RI_DROP_MASK); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_MAC); mvpp2_prs_hw_write(sc, &pe); mvpp2_prs_mac_drop_all_set(sc, 0, 0); mvpp2_prs_mac_promisc_set(sc, 0, MVPP2_PRS_L2_UNI_CAST, 0); mvpp2_prs_mac_promisc_set(sc, 0, MVPP2_PRS_L2_MULTI_CAST, 0); } void mvpp2_prs_dsa_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; mvpp2_prs_dsa_tag_set(sc, 0, 0, MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_set(sc, 0, 0, MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_set(sc, 0, 0, MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_set(sc, 0, 0, MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_ethertype_set(sc, 0, 0, MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_ethertype_set(sc, 0, 0, MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_ethertype_set(sc, 0, 1, MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_ethertype_set(sc, 0, 1, MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); pe.index = MVPP2_PE_DSA_DEFAULT; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); mvpp2_prs_sram_shift_set(&pe, 0, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_MAC); mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_hw_write(sc, &pe); } int mvpp2_prs_etype_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; int tid; /* Ethertype: PPPoE */ tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, ETHERTYPE_PPPOE); mvpp2_prs_sram_shift_set(&pe, MVPP2_PPPOE_HDR_SIZE, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_PPPOE); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_PPPOE_MASK, MVPP2_PRS_RI_PPPOE_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_L2); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; sc->sc_prs_shadow[pe.index].finish = 0; mvpp2_prs_shadow_ri_set(sc, pe.index, MVPP2_PRS_RI_PPPOE_MASK, MVPP2_PRS_RI_PPPOE_MASK); mvpp2_prs_hw_write(sc, &pe); /* Ethertype: ARP */ tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, ETHERTYPE_ARP); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_ARP, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_L2); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; sc->sc_prs_shadow[pe.index].finish = 1; mvpp2_prs_shadow_ri_set(sc, pe.index, MVPP2_PRS_RI_L3_ARP, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_hw_write(sc, &pe); /* Ethertype: LBTD */ tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, MVPP2_IP_LBDT_TYPE); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | MVPP2_PRS_RI_UDF3_RX_SPECIAL, MVPP2_PRS_RI_CPU_CODE_MASK | MVPP2_PRS_RI_UDF3_MASK); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_L2); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; sc->sc_prs_shadow[pe.index].finish = 1; mvpp2_prs_shadow_ri_set(sc, pe.index, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | MVPP2_PRS_RI_UDF3_RX_SPECIAL, MVPP2_PRS_RI_CPU_CODE_MASK | MVPP2_PRS_RI_UDF3_MASK); mvpp2_prs_hw_write(sc, &pe); /* Ethertype: IPv4 without options */ tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, ETHERTYPE_IP); mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_IPV4_HEAD | MVPP2_PRS_IPV4_IHL, MVPP2_PRS_IPV4_HEAD_MASK | MVPP2_PRS_IPV4_IHL_MASK); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_L2); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; sc->sc_prs_shadow[pe.index].finish = 0; mvpp2_prs_shadow_ri_set(sc, pe.index, MVPP2_PRS_RI_L3_IP4, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_hw_write(sc, &pe); /* Ethertype: IPv4 with options */ tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; pe.index = tid; pe.tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE(MVPP2_ETH_TYPE_LEN)] = 0x0; pe.tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE_EN(MVPP2_ETH_TYPE_LEN)] = 0x0; mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_IPV4_HEAD, MVPP2_PRS_IPV4_HEAD_MASK); pe.sram.word[MVPP2_PRS_SRAM_RI_WORD] = 0x0; pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_L2); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; sc->sc_prs_shadow[pe.index].finish = 0; mvpp2_prs_shadow_ri_set(sc, pe.index, MVPP2_PRS_RI_L3_IP4_OPT, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_hw_write(sc, &pe); /* Ethertype: IPv6 without options */ tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, ETHERTYPE_IPV6); mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 8 + MVPP2_MAX_L3_ADDR_SIZE, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP6, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_L2); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; sc->sc_prs_shadow[pe.index].finish = 0; mvpp2_prs_shadow_ri_set(sc, pe.index, MVPP2_PRS_RI_L3_IP6, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_hw_write(sc, &pe); /* Default entry for MVPP2_PRS_LU_L2 - Unknown ethtype */ memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = MVPP2_PE_ETH_TYPE_UN; mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UN, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_L2); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; sc->sc_prs_shadow[pe.index].finish = 1; mvpp2_prs_shadow_ri_set(sc, pe.index, MVPP2_PRS_RI_L3_UN, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_vlan_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; int ret; sc->sc_prs_double_vlans = mallocarray(MVPP2_PRS_DBL_VLANS_MAX, sizeof(*sc->sc_prs_double_vlans), M_DEVBUF, M_WAITOK | M_ZERO); ret = mvpp2_prs_double_vlan_add(sc, ETHERTYPE_VLAN, ETHERTYPE_QINQ, MVPP2_PRS_PORT_MASK); if (ret) return ret; ret = mvpp2_prs_double_vlan_add(sc, ETHERTYPE_VLAN, ETHERTYPE_VLAN, MVPP2_PRS_PORT_MASK); if (ret) return ret; ret = mvpp2_prs_vlan_add(sc, ETHERTYPE_QINQ, MVPP2_PRS_SINGLE_VLAN_AI, MVPP2_PRS_PORT_MASK); if (ret) return ret; ret = mvpp2_prs_vlan_add(sc, ETHERTYPE_VLAN, MVPP2_PRS_SINGLE_VLAN_AI, MVPP2_PRS_PORT_MASK); if (ret) return ret; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); pe.index = MVPP2_PE_VLAN_DBL; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_DOUBLE, MVPP2_PRS_RI_VLAN_MASK); mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_DBL_VLAN_AI_BIT, MVPP2_PRS_DBL_VLAN_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_VLAN); mvpp2_prs_hw_write(sc, &pe); memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); pe.index = MVPP2_PE_VLAN_NONE; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE, MVPP2_PRS_RI_VLAN_MASK); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_VLAN); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_pppoe_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; int tid; tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, PPP_IP); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_PPPOE); mvpp2_prs_hw_write(sc, &pe); tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; pe.index = tid; mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_IPV4_HEAD | MVPP2_PRS_IPV4_IHL, MVPP2_PRS_IPV4_HEAD_MASK | MVPP2_PRS_IPV4_IHL_MASK); pe.sram.word[MVPP2_PRS_SRAM_RI_WORD] = 0x0; pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_PPPOE); mvpp2_prs_hw_write(sc, &pe); tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, PPP_IPV6); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP6, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_PPPOE); mvpp2_prs_hw_write(sc, &pe); tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); pe.index = tid; mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UN, MVPP2_PRS_RI_L3_PROTO_MASK); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, MVPP2_ETH_TYPE_LEN, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_PPPOE); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_ip6_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; int tid, ret; ret = mvpp2_prs_ip6_proto(sc, IPPROTO_TCP, MVPP2_PRS_RI_L4_TCP, MVPP2_PRS_RI_L4_PROTO_MASK); if (ret) return ret; ret = mvpp2_prs_ip6_proto(sc, IPPROTO_UDP, MVPP2_PRS_RI_L4_UDP, MVPP2_PRS_RI_L4_PROTO_MASK); if (ret) return ret; ret = mvpp2_prs_ip6_proto(sc, IPPROTO_ICMPV6, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | MVPP2_PRS_RI_UDF3_RX_SPECIAL, MVPP2_PRS_RI_CPU_CODE_MASK | MVPP2_PRS_RI_UDF3_MASK); if (ret) return ret; ret = mvpp2_prs_ip6_proto(sc, IPPROTO_IPIP, MVPP2_PRS_RI_UDF7_IP6_LITE, MVPP2_PRS_RI_UDF7_MASK); if (ret) return ret; ret = mvpp2_prs_ip6_cast(sc, MVPP2_PRS_L3_MULTI_CAST); if (ret) return ret; tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = tid; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UN | MVPP2_PRS_RI_DROP_MASK, MVPP2_PRS_RI_L3_PROTO_MASK | MVPP2_PRS_RI_DROP_MASK); mvpp2_prs_tcam_data_byte_set(&pe, 1, 0x00, MVPP2_PRS_IPV6_HOP_MASK); mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = MVPP2_PE_IP6_PROTO_UN; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER, MVPP2_PRS_RI_L4_PROTO_MASK); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, sizeof(struct ip6_hdr) - 6, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = MVPP2_PE_IP6_EXT_PROTO_UN; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER, MVPP2_PRS_RI_L4_PROTO_MASK); mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV6_EXT_AI_BIT, MVPP2_PRS_IPV6_EXT_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = MVPP2_PE_IP6_ADDR_UN; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UCAST, MVPP2_PRS_RI_L3_ADDR_MASK); mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); mvpp2_prs_sram_shift_set(&pe, -18, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP6); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_ip4_init(struct mvpp2_softc *sc) { struct mvpp2_prs_entry pe; int ret; ret = mvpp2_prs_ip4_proto(sc, IPPROTO_TCP, MVPP2_PRS_RI_L4_TCP, MVPP2_PRS_RI_L4_PROTO_MASK); if (ret) return ret; ret = mvpp2_prs_ip4_proto(sc, IPPROTO_UDP, MVPP2_PRS_RI_L4_UDP, MVPP2_PRS_RI_L4_PROTO_MASK); if (ret) return ret; ret = mvpp2_prs_ip4_proto(sc, IPPROTO_IGMP, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | MVPP2_PRS_RI_UDF3_RX_SPECIAL, MVPP2_PRS_RI_CPU_CODE_MASK | MVPP2_PRS_RI_UDF3_MASK); if (ret) return ret; ret = mvpp2_prs_ip4_cast(sc, MVPP2_PRS_L3_BROAD_CAST); if (ret) return ret; ret = mvpp2_prs_ip4_cast(sc, MVPP2_PRS_L3_MULTI_CAST); if (ret) return ret; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = MVPP2_PE_IP4_PROTO_UN; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); mvpp2_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, sizeof(struct ip) - 4, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER, MVPP2_PRS_RI_L4_PROTO_MASK); mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = MVPP2_PE_IP4_ADDR_UN; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UCAST, MVPP2_PRS_RI_L3_ADDR_MASK); mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_port_match(struct device *parent, void *cfdata, void *aux) { struct mvpp2_attach_args *maa = aux; char buf[32]; if (OF_getprop(maa->ma_node, "status", buf, sizeof(buf)) > 0 && strcmp(buf, "disabled") == 0) return 0; return 1; } void mvpp2_port_attach(struct device *parent, struct device *self, void *aux) { struct mvpp2_port *sc = (void *)self; struct mvpp2_attach_args *maa = aux; struct mvpp2_tx_queue *txq; struct mvpp2_rx_queue *rxq; struct ifnet *ifp; uint32_t phy, reg; int i, idx, len, node; int mii_flags = 0; char *phy_mode; char *managed; sc->sc = (void *)parent; sc->sc_node = maa->ma_node; sc->sc_dmat = maa->ma_dmat; sc->sc_id = OF_getpropint(sc->sc_node, "port-id", 0); sc->sc_gop_id = OF_getpropint(sc->sc_node, "gop-port-id", 0); sc->sc_sfp = OF_getpropint(sc->sc_node, "sfp", 0); len = OF_getproplen(sc->sc_node, "phy-mode"); if (len <= 0) { printf("%s: cannot extract phy-mode\n", self->dv_xname); return; } phy_mode = malloc(len, M_TEMP, M_WAITOK); OF_getprop(sc->sc_node, "phy-mode", phy_mode, len); if (!strncmp(phy_mode, "10gbase-r", strlen("10gbase-r"))) sc->sc_phy_mode = PHY_MODE_10GBASER; else if (!strncmp(phy_mode, "10gbase-kr", strlen("10gbase-kr"))) sc->sc_phy_mode = PHY_MODE_10GBASER; else if (!strncmp(phy_mode, "2500base-x", strlen("2500base-x"))) sc->sc_phy_mode = PHY_MODE_2500BASEX; else if (!strncmp(phy_mode, "1000base-x", strlen("1000base-x"))) sc->sc_phy_mode = PHY_MODE_1000BASEX; else if (!strncmp(phy_mode, "sgmii", strlen("sgmii"))) sc->sc_phy_mode = PHY_MODE_SGMII; else if (!strncmp(phy_mode, "rgmii-rxid", strlen("rgmii-rxid"))) sc->sc_phy_mode = PHY_MODE_RGMII_RXID; else if (!strncmp(phy_mode, "rgmii-txid", strlen("rgmii-txid"))) sc->sc_phy_mode = PHY_MODE_RGMII_TXID; else if (!strncmp(phy_mode, "rgmii-id", strlen("rgmii-id"))) sc->sc_phy_mode = PHY_MODE_RGMII_ID; else if (!strncmp(phy_mode, "rgmii", strlen("rgmii"))) sc->sc_phy_mode = PHY_MODE_RGMII; else { printf("%s: cannot use phy-mode %s\n", self->dv_xname, phy_mode); return; } free(phy_mode, M_TEMP, len); /* Lookup PHY. */ phy = OF_getpropint(sc->sc_node, "phy", 0); if (phy) { node = OF_getnodebyphandle(phy); if (!node) { printf(": no phy\n"); return; } sc->sc_mdio = mii_byphandle(phy); sc->sc_phyloc = OF_getpropint(node, "reg", MII_PHY_ANY); sc->sc_sfp = OF_getpropint(node, "sfp", sc->sc_sfp); sc->sc_mii.mii_node = node; } if (sc->sc_sfp) config_mountroot(self, mvpp2_port_attach_sfp); if ((len = OF_getproplen(sc->sc_node, "managed")) >= 0) { managed = malloc(len, M_TEMP, M_WAITOK); OF_getprop(sc->sc_node, "managed", managed, len); if (!strncmp(managed, "in-band-status", strlen("in-band-status"))) sc->sc_inband_status = 1; free(managed, M_TEMP, len); } if (OF_getprop(sc->sc_node, "local-mac-address", &sc->sc_lladdr, ETHER_ADDR_LEN) != ETHER_ADDR_LEN) memset(sc->sc_lladdr, 0xff, sizeof(sc->sc_lladdr)); printf(": address %s\n", ether_sprintf(sc->sc_lladdr)); sc->sc_ntxq = sc->sc_nrxq = 1; sc->sc_txqs = mallocarray(sc->sc_ntxq, sizeof(*sc->sc_txqs), M_DEVBUF, M_WAITOK | M_ZERO); sc->sc_rxqs = mallocarray(sc->sc_nrxq, sizeof(*sc->sc_rxqs), M_DEVBUF, M_WAITOK | M_ZERO); for (i = 0; i < sc->sc_ntxq; i++) { txq = &sc->sc_txqs[i]; txq->id = mvpp2_txq_phys(sc->sc_id, i); txq->log_id = i; txq->done_pkts_coal = MVPP2_TXDONE_COAL_PKTS_THRESH; } sc->sc_tx_time_coal = MVPP2_TXDONE_COAL_USEC; for (i = 0; i < sc->sc_nrxq; i++) { rxq = &sc->sc_rxqs[i]; rxq->id = sc->sc_id * 32 + i; rxq->pkts_coal = MVPP2_RX_COAL_PKTS; rxq->time_coal = MVPP2_RX_COAL_USEC; } mvpp2_egress_disable(sc); mvpp2_port_disable(sc); mvpp2_write(sc->sc, MVPP2_ISR_RXQ_GROUP_INDEX_REG, sc->sc_id << MVPP2_ISR_RXQ_GROUP_INDEX_GROUP_SHIFT | 0 /* queue vector id */); mvpp2_write(sc->sc, MVPP2_ISR_RXQ_SUB_GROUP_CONFIG_REG, sc->sc_nrxq << MVPP2_ISR_RXQ_SUB_GROUP_CONFIG_SIZE_SHIFT | 0 /* first rxq */); mvpp2_ingress_disable(sc); mvpp2_defaults_set(sc); mvpp2_cls_oversize_rxq_set(sc); mvpp2_cls_port_config(sc); /* * We have one pool per core, so all RX queues on a specific * core share that pool. Also long and short uses the same * pool. */ for (i = 0; i < sc->sc_nrxq; i++) { mvpp2_rxq_long_pool_set(sc, i, i); mvpp2_rxq_short_pool_set(sc, i, i); } mvpp2_mac_reset_assert(sc); mvpp2_pcs_reset_assert(sc); timeout_set(&sc->sc_tick, mvpp2_tick, sc); ifp = &sc->sc_ac.ac_if; ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = mvpp2_ioctl; ifp->if_start = mvpp2_start; ifp->if_watchdog = mvpp2_watchdog; ifq_init_maxlen(&ifp->if_snd, MVPP2_NTXDESC - 1); bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); ifp->if_capabilities = IFCAP_VLAN_MTU; sc->sc_mii.mii_ifp = ifp; sc->sc_mii.mii_readreg = mvpp2_mii_readreg; sc->sc_mii.mii_writereg = mvpp2_mii_writereg; sc->sc_mii.mii_statchg = mvpp2_mii_statchg; ifmedia_init(&sc->sc_media, 0, mvpp2_media_change, mvpp2_media_status); if (sc->sc_mdio) { switch (sc->sc_phy_mode) { case PHY_MODE_1000BASEX: mii_flags |= MIIF_IS_1000X; break; case PHY_MODE_SGMII: mii_flags |= MIIF_SGMII; break; case PHY_MODE_RGMII_ID: mii_flags |= MIIF_RXID | MIIF_TXID; break; case PHY_MODE_RGMII_RXID: mii_flags |= MIIF_RXID; break; case PHY_MODE_RGMII_TXID: mii_flags |= MIIF_TXID; break; default: break; } mii_attach(self, &sc->sc_mii, 0xffffffff, sc->sc_phyloc, (sc->sc_phyloc == MII_PHY_ANY) ? 0 : MII_OFFSET_ANY, mii_flags); if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) { printf("%s: no PHY found!\n", self->dv_xname); ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL, 0, NULL); ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL); } else ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); } else { ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO, 0, NULL); ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO); if (sc->sc_inband_status) { switch (sc->sc_phy_mode) { case PHY_MODE_1000BASEX: sc->sc_mii.mii_media_active = IFM_ETHER|IFM_1000_KX|IFM_FDX; break; case PHY_MODE_2500BASEX: sc->sc_mii.mii_media_active = IFM_ETHER|IFM_2500_KX|IFM_FDX; break; case PHY_MODE_10GBASER: sc->sc_mii.mii_media_active = IFM_ETHER|IFM_10G_KR|IFM_FDX; break; default: break; } mvpp2_inband_statchg(sc); } else { sc->sc_mii.mii_media_status = IFM_AVALID|IFM_ACTIVE; sc->sc_mii.mii_media_active = IFM_ETHER|IFM_1000_T|IFM_FDX; mvpp2_mii_statchg(self); } ifp->if_baudrate = ifmedia_baudrate(sc->sc_mii.mii_media_active); ifp->if_link_state = LINK_STATE_FULL_DUPLEX; } if_attach(ifp); ether_ifattach(ifp); if (sc->sc_phy_mode == PHY_MODE_2500BASEX || sc->sc_phy_mode == PHY_MODE_1000BASEX || sc->sc_phy_mode == PHY_MODE_SGMII || sc->sc_phy_mode == PHY_MODE_RGMII || sc->sc_phy_mode == PHY_MODE_RGMII_ID || sc->sc_phy_mode == PHY_MODE_RGMII_RXID || sc->sc_phy_mode == PHY_MODE_RGMII_TXID) { reg = mvpp2_gmac_read(sc, MVPP2_GMAC_INT_MASK_REG); reg |= MVPP2_GMAC_INT_CAUSE_LINK_CHANGE; mvpp2_gmac_write(sc, MVPP2_GMAC_INT_MASK_REG, reg); } if (sc->sc_gop_id == 0) { reg = mvpp2_xlg_read(sc, MV_XLG_INTERRUPT_MASK_REG); reg |= MV_XLG_INTERRUPT_LINK_CHANGE; mvpp2_xlg_write(sc, MV_XLG_INTERRUPT_MASK_REG, reg); } mvpp2_gop_intr_unmask(sc); idx = OF_getindex(sc->sc_node, "link", "interrupt-names"); if (idx >= 0) fdt_intr_establish_idx(sc->sc_node, idx, IPL_NET, mvpp2_link_intr, sc, sc->sc_dev.dv_xname); idx = OF_getindex(sc->sc_node, "hif0", "interrupt-names"); if (idx < 0) idx = OF_getindex(sc->sc_node, "tx-cpu0", "interrupt-names"); if (idx >= 0) fdt_intr_establish_idx(sc->sc_node, idx, IPL_NET, mvpp2_intr, sc, sc->sc_dev.dv_xname); } void mvpp2_port_attach_sfp(struct device *self) { struct mvpp2_port *sc = (struct mvpp2_port *)self; uint32_t reg; rw_enter(&mvpp2_sff_lock, RW_WRITE); sfp_disable(sc->sc_sfp); sfp_add_media(sc->sc_sfp, &sc->sc_mii); rw_exit(&mvpp2_sff_lock); switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) { case IFM_10G_SR: case IFM_10G_LR: case IFM_10G_LRM: case IFM_10G_ER: case IFM_10G_SFP_CU: sc->sc_phy_mode = PHY_MODE_10GBASER; sc->sc_mii.mii_media_status = IFM_AVALID; sc->sc_inband_status = 1; break; case IFM_2500_SX: sc->sc_phy_mode = PHY_MODE_2500BASEX; sc->sc_mii.mii_media_status = IFM_AVALID; sc->sc_inband_status = 1; break; case IFM_1000_CX: case IFM_1000_LX: case IFM_1000_SX: case IFM_1000_T: sc->sc_phy_mode = PHY_MODE_1000BASEX; sc->sc_mii.mii_media_status = IFM_AVALID; sc->sc_inband_status = 1; break; } if (sc->sc_inband_status) { reg = mvpp2_gmac_read(sc, MVPP2_GMAC_INT_MASK_REG); reg |= MVPP2_GMAC_INT_CAUSE_LINK_CHANGE; mvpp2_gmac_write(sc, MVPP2_GMAC_INT_MASK_REG, reg); } } uint32_t mvpp2_read(struct mvpp2_softc *sc, bus_addr_t addr) { return bus_space_read_4(sc->sc_iot, sc->sc_ioh_base, addr); } void mvpp2_write(struct mvpp2_softc *sc, bus_addr_t addr, uint32_t data) { bus_space_write_4(sc->sc_iot, sc->sc_ioh_base, addr, data); } uint32_t mvpp2_gmac_read(struct mvpp2_port *sc, bus_addr_t addr) { return bus_space_read_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_GMAC_OFFSET + sc->sc_gop_id * MVPP22_GMAC_REG_SIZE + addr); } void mvpp2_gmac_write(struct mvpp2_port *sc, bus_addr_t addr, uint32_t data) { bus_space_write_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_GMAC_OFFSET + sc->sc_gop_id * MVPP22_GMAC_REG_SIZE + addr, data); } uint32_t mvpp2_xlg_read(struct mvpp2_port *sc, bus_addr_t addr) { return bus_space_read_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_XLG_OFFSET + sc->sc_gop_id * MVPP22_XLG_REG_SIZE + addr); } void mvpp2_xlg_write(struct mvpp2_port *sc, bus_addr_t addr, uint32_t data) { bus_space_write_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_XLG_OFFSET + sc->sc_gop_id * MVPP22_XLG_REG_SIZE + addr, data); } uint32_t mvpp2_mpcs_read(struct mvpp2_port *sc, bus_addr_t addr) { return bus_space_read_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_MPCS_OFFSET + sc->sc_gop_id * MVPP22_MPCS_REG_SIZE + addr); } void mvpp2_mpcs_write(struct mvpp2_port *sc, bus_addr_t addr, uint32_t data) { bus_space_write_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_MPCS_OFFSET + sc->sc_gop_id * MVPP22_MPCS_REG_SIZE + addr, data); } uint32_t mvpp2_xpcs_read(struct mvpp2_port *sc, bus_addr_t addr) { return bus_space_read_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_XPCS_OFFSET + sc->sc_gop_id * MVPP22_XPCS_REG_SIZE + addr); } void mvpp2_xpcs_write(struct mvpp2_port *sc, bus_addr_t addr, uint32_t data) { bus_space_write_4(sc->sc->sc_iot, sc->sc->sc_ioh_iface, MVPP22_XPCS_OFFSET + sc->sc_gop_id * MVPP22_XPCS_REG_SIZE + addr, data); } static inline int mvpp2_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m) { int error; error = bus_dmamap_load_mbuf(dmat, map, m, BUS_DMA_NOWAIT); if (error != EFBIG) return (error); error = m_defrag(m, M_DONTWAIT); if (error != 0) return (error); return bus_dmamap_load_mbuf(dmat, map, m, BUS_DMA_NOWAIT); } void mvpp2_start(struct ifnet *ifp) { struct mvpp2_port *sc = ifp->if_softc; struct mvpp2_tx_queue *txq = &sc->sc->sc_aggr_txqs[0]; struct mvpp2_tx_desc *txd; struct mbuf *m; bus_dmamap_t map; uint32_t command; int i, current, first, last; int free, prod, used; if (!(ifp->if_flags & IFF_RUNNING)) return; if (ifq_is_oactive(&ifp->if_snd)) return; if (ifq_empty(&ifp->if_snd)) return; if (!sc->sc_link) return; used = 0; prod = txq->prod; free = txq->cons; if (free <= prod) free += MVPP2_AGGR_TXQ_SIZE; free -= prod; for (;;) { if (free <= MVPP2_NTXSEGS) { ifq_set_oactive(&ifp->if_snd); break; } m = ifq_dequeue(&ifp->if_snd); if (m == NULL) break; first = last = current = prod; map = txq->buf[current].mb_map; if (mvpp2_load_mbuf(sc->sc_dmat, map, m) != 0) { ifp->if_oerrors++; m_freem(m); continue; } bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize, BUS_DMASYNC_PREWRITE); command = MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE; for (i = 0; i < map->dm_nsegs; i++) { txd = &txq->descs[current]; memset(txd, 0, sizeof(*txd)); txd->buf_phys_addr_hw_cmd2 = map->dm_segs[i].ds_addr & ~0x1f; txd->packet_offset = map->dm_segs[i].ds_addr & 0x1f; txd->data_size = map->dm_segs[i].ds_len; txd->phys_txq = sc->sc_txqs[0].id; txd->command = command | MVPP2_TXD_PADDING_DISABLE; if (i == 0) txd->command |= MVPP2_TXD_F_DESC; if (i == (map->dm_nsegs - 1)) txd->command |= MVPP2_TXD_L_DESC; bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(txq->ring), current * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); last = current; current = (current + 1) % MVPP2_AGGR_TXQ_SIZE; KASSERT(current != txq->cons); } KASSERT(txq->buf[last].mb_m == NULL); txq->buf[first].mb_map = txq->buf[last].mb_map; txq->buf[last].mb_map = map; txq->buf[last].mb_m = m; #if NBPFILTER > 0 if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); #endif free -= map->dm_nsegs; used += map->dm_nsegs; prod = current; } if (used) mvpp2_write(sc->sc, MVPP2_AGGR_TXQ_UPDATE_REG, used); if (txq->prod != prod) txq->prod = prod; } int mvpp2_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) { struct mvpp2_port *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)addr; int error = 0, s; s = splnet(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; /* FALLTHROUGH */ case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING) error = ENETRESET; else mvpp2_up(sc); } else { if (ifp->if_flags & IFF_RUNNING) mvpp2_down(sc); } break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); break; case SIOCGIFRXR: error = mvpp2_rxrinfo(sc, (struct if_rxrinfo *)ifr->ifr_data); break; case SIOCGIFSFFPAGE: error = rw_enter(&mvpp2_sff_lock, RW_WRITE|RW_INTR); if (error != 0) break; error = sfp_get_sffpage(sc->sc_sfp, (struct if_sffpage *)addr); rw_exit(&mvpp2_sff_lock); break; default: error = ether_ioctl(ifp, &sc->sc_ac, cmd, addr); break; } if (error == ENETRESET) { if (ifp->if_flags & IFF_RUNNING) mvpp2_iff(sc); error = 0; } splx(s); return (error); } int mvpp2_rxrinfo(struct mvpp2_port *sc, struct if_rxrinfo *ifri) { struct mvpp2_rx_queue *rxq; struct if_rxring_info *ifrs, *ifr; unsigned int i; int error; ifrs = mallocarray(sc->sc_nrxq, sizeof(*ifrs), M_TEMP, M_WAITOK|M_ZERO|M_CANFAIL); if (ifrs == NULL) return (ENOMEM); for (i = 0; i < sc->sc_nrxq; i++) { rxq = &sc->sc_rxqs[i]; ifr = &ifrs[i]; snprintf(ifr->ifr_name, sizeof(ifr->ifr_name), "%u", i); ifr->ifr_size = MCLBYTES; ifr->ifr_info = rxq->rxring; } error = if_rxr_info_ioctl(ifri, i, ifrs); free(ifrs, M_TEMP, i * sizeof(*ifrs)); return (error); } void mvpp2_watchdog(struct ifnet *ifp) { printf("%s\n", __func__); } int mvpp2_media_change(struct ifnet *ifp) { struct mvpp2_port *sc = ifp->if_softc; if (LIST_FIRST(&sc->sc_mii.mii_phys)) mii_mediachg(&sc->sc_mii); return (0); } void mvpp2_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) { struct mvpp2_port *sc = ifp->if_softc; if (LIST_FIRST(&sc->sc_mii.mii_phys)) mii_pollstat(&sc->sc_mii); ifmr->ifm_active = sc->sc_mii.mii_media_active; ifmr->ifm_status = sc->sc_mii.mii_media_status; } int mvpp2_mii_readreg(struct device *self, int phy, int reg) { struct mvpp2_port *sc = (void *)self; return sc->sc_mdio->md_readreg(sc->sc_mdio->md_cookie, phy, reg); } void mvpp2_mii_writereg(struct device *self, int phy, int reg, int val) { struct mvpp2_port *sc = (void *)self; return sc->sc_mdio->md_writereg(sc->sc_mdio->md_cookie, phy, reg, val); } void mvpp2_mii_statchg(struct device *self) { struct mvpp2_port *sc = (void *)self; mvpp2_port_change(sc); } void mvpp2_inband_statchg(struct mvpp2_port *sc) { uint64_t subtype = IFM_SUBTYPE(sc->sc_mii.mii_media_active); uint32_t reg; sc->sc_mii.mii_media_status = IFM_AVALID; sc->sc_mii.mii_media_active = IFM_ETHER; if (sc->sc_gop_id == 0 && (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI)) { reg = mvpp2_xlg_read(sc, MV_XLG_MAC_PORT_STATUS_REG); if (reg & MV_XLG_MAC_PORT_STATUS_LINKSTATUS) sc->sc_mii.mii_media_status |= IFM_ACTIVE; sc->sc_mii.mii_media_active |= IFM_FDX; sc->sc_mii.mii_media_active |= subtype; } else { reg = mvpp2_gmac_read(sc, MVPP2_PORT_STATUS0_REG); if (reg & MVPP2_PORT_STATUS0_LINKUP) sc->sc_mii.mii_media_status |= IFM_ACTIVE; if (reg & MVPP2_PORT_STATUS0_FULLDX) sc->sc_mii.mii_media_active |= IFM_FDX; if (sc->sc_phy_mode == PHY_MODE_2500BASEX) sc->sc_mii.mii_media_active |= subtype; else if (sc->sc_phy_mode == PHY_MODE_1000BASEX) sc->sc_mii.mii_media_active |= subtype; else if (reg & MVPP2_PORT_STATUS0_GMIISPEED) sc->sc_mii.mii_media_active |= IFM_1000_T; else if (reg & MVPP2_PORT_STATUS0_MIISPEED) sc->sc_mii.mii_media_active |= IFM_100_TX; else sc->sc_mii.mii_media_active |= IFM_10_T; } mvpp2_port_change(sc); } void mvpp2_port_change(struct mvpp2_port *sc) { uint32_t reg; sc->sc_link = !!(sc->sc_mii.mii_media_status & IFM_ACTIVE); if (sc->sc_inband_status) return; if (sc->sc_link) { if (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI) { reg = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL0_REG); reg &= ~MV_XLG_MAC_CTRL0_FORCELINKDOWN; reg |= MV_XLG_MAC_CTRL0_FORCELINKPASS; mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL0_REG, reg); } else { reg = mvpp2_gmac_read(sc, MVPP2_GMAC_AUTONEG_CONFIG); reg &= ~MVPP2_GMAC_FORCE_LINK_DOWN; reg |= MVPP2_GMAC_FORCE_LINK_PASS; reg &= ~MVPP2_GMAC_CONFIG_MII_SPEED; reg &= ~MVPP2_GMAC_CONFIG_GMII_SPEED; reg &= ~MVPP2_GMAC_CONFIG_FULL_DUPLEX; if (IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_2500_KX || IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_2500_SX || IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_1000_CX || IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_1000_LX || IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_1000_KX || IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_1000_SX || IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_1000_T) reg |= MVPP2_GMAC_CONFIG_GMII_SPEED; if (IFM_SUBTYPE(sc->sc_mii.mii_media_active) == IFM_100_TX) reg |= MVPP2_GMAC_CONFIG_MII_SPEED; if ((sc->sc_mii.mii_media_active & IFM_GMASK) == IFM_FDX) reg |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; mvpp2_gmac_write(sc, MVPP2_GMAC_AUTONEG_CONFIG, reg); } } else { if (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI) { reg = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL0_REG); reg &= ~MV_XLG_MAC_CTRL0_FORCELINKPASS; reg |= MV_XLG_MAC_CTRL0_FORCELINKDOWN; mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL0_REG, reg); } else { reg = mvpp2_gmac_read(sc, MVPP2_GMAC_AUTONEG_CONFIG); reg &= ~MVPP2_GMAC_FORCE_LINK_PASS; reg |= MVPP2_GMAC_FORCE_LINK_DOWN; mvpp2_gmac_write(sc, MVPP2_GMAC_AUTONEG_CONFIG, reg); } } } void mvpp2_tick(void *arg) { struct mvpp2_port *sc = arg; int s; s = splnet(); mii_tick(&sc->sc_mii); splx(s); timeout_add_sec(&sc->sc_tick, 1); } int mvpp2_link_intr(void *arg) { struct mvpp2_port *sc = arg; uint32_t reg; int event = 0; if (sc->sc_gop_id == 0 && (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI)) { reg = mvpp2_xlg_read(sc, MV_XLG_INTERRUPT_CAUSE_REG); if (reg & MV_XLG_INTERRUPT_LINK_CHANGE) event = 1; } else if (sc->sc_phy_mode == PHY_MODE_2500BASEX || sc->sc_phy_mode == PHY_MODE_1000BASEX || sc->sc_phy_mode == PHY_MODE_SGMII || sc->sc_phy_mode == PHY_MODE_RGMII || sc->sc_phy_mode == PHY_MODE_RGMII_ID || sc->sc_phy_mode == PHY_MODE_RGMII_RXID || sc->sc_phy_mode == PHY_MODE_RGMII_TXID) { reg = mvpp2_gmac_read(sc, MVPP2_GMAC_INT_CAUSE_REG); if (reg & MVPP2_GMAC_INT_CAUSE_LINK_CHANGE) event = 1; } if (event && sc->sc_inband_status) mvpp2_inband_statchg(sc); return (1); } int mvpp2_intr(void *arg) { struct mvpp2_port *sc = arg; uint32_t reg; reg = mvpp2_read(sc->sc, MVPP2_ISR_RX_TX_CAUSE_REG(sc->sc_id)); if (reg & MVPP2_CAUSE_MISC_SUM_MASK) { mvpp2_write(sc->sc, MVPP2_ISR_MISC_CAUSE_REG, 0); mvpp2_write(sc->sc, MVPP2_ISR_RX_TX_CAUSE_REG(sc->sc_id), reg & ~MVPP2_CAUSE_MISC_SUM_MASK); } if (reg & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK) mvpp2_tx_proc(sc, (reg & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK) >> MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET); if (reg & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK) mvpp2_rx_proc(sc, reg & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK); return (1); } void mvpp2_tx_proc(struct mvpp2_port *sc, uint8_t queues) { struct mvpp2_tx_queue *txq; int i; for (i = 0; i < sc->sc_ntxq; i++) { txq = &sc->sc_txqs[i]; if ((queues & (1 << i)) == 0) continue; mvpp2_txq_proc(sc, txq); } } void mvpp2_txq_proc(struct mvpp2_port *sc, struct mvpp2_tx_queue *txq) { struct ifnet *ifp = &sc->sc_ac.ac_if; struct mvpp2_tx_queue *aggr_txq = &sc->sc->sc_aggr_txqs[0]; struct mvpp2_buf *txb; int i, idx, nsent; /* XXX: this is a percpu register! */ nsent = (mvpp2_read(sc->sc, MVPP2_TXQ_SENT_REG(txq->id)) & MVPP2_TRANSMITTED_COUNT_MASK) >> MVPP2_TRANSMITTED_COUNT_OFFSET; for (i = 0; i < nsent; i++) { idx = aggr_txq->cons; KASSERT(idx < MVPP2_AGGR_TXQ_SIZE); txb = &aggr_txq->buf[idx]; if (txb->mb_m) { bus_dmamap_sync(sc->sc_dmat, txb->mb_map, 0, txb->mb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, txb->mb_map); m_freem(txb->mb_m); txb->mb_m = NULL; } aggr_txq->cons = (aggr_txq->cons + 1) % MVPP2_AGGR_TXQ_SIZE; } if (ifq_is_oactive(&ifp->if_snd)) ifq_restart(&ifp->if_snd); } void mvpp2_rx_proc(struct mvpp2_port *sc, uint8_t queues) { struct mvpp2_rx_queue *rxq; int i; for (i = 0; i < sc->sc_nrxq; i++) { rxq = &sc->sc_rxqs[i]; if ((queues & (1 << i)) == 0) continue; mvpp2_rxq_proc(sc, rxq); } mvpp2_rx_refill(sc); } void mvpp2_rxq_proc(struct mvpp2_port *sc, struct mvpp2_rx_queue *rxq) { struct ifnet *ifp = &sc->sc_ac.ac_if; struct mbuf_list ml = MBUF_LIST_INITIALIZER(); struct mvpp2_rx_desc *rxd; struct mvpp2_bm_pool *bm; struct mvpp2_buf *rxb; struct mbuf *m; uint64_t virt; uint32_t i, nrecv, pool; nrecv = mvpp2_rxq_received(sc, rxq->id); if (!nrecv) return; pool = curcpu()->ci_cpuid; KASSERT(pool < sc->sc->sc_npools); bm = &sc->sc->sc_bm_pools[pool]; bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(rxq->ring), 0, MVPP2_DMA_LEN(rxq->ring), BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (i = 0; i < nrecv; i++) { rxd = &rxq->descs[rxq->cons]; virt = rxd->buf_cookie_bm_qset_cls_info; KASSERT(((virt >> 16) & 0xffff) == pool); KASSERT((virt & 0xffff) < MVPP2_BM_SIZE); rxb = &bm->rxbuf[virt & 0xffff]; KASSERT(rxb->mb_m != NULL); bus_dmamap_sync(sc->sc_dmat, rxb->mb_map, 0, rxd->data_size, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->sc_dmat, rxb->mb_map); m = rxb->mb_m; rxb->mb_m = NULL; m->m_pkthdr.len = m->m_len = rxd->data_size; m_adj(m, MVPP2_MH_SIZE); ml_enqueue(&ml, m); KASSERT(bm->freelist[bm->free_prod] == -1); bm->freelist[bm->free_prod] = virt & 0xffffffff; bm->free_prod = (bm->free_prod + 1) % MVPP2_BM_SIZE; rxq->cons = (rxq->cons + 1) % MVPP2_NRXDESC; } bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(rxq->ring), 0, MVPP2_DMA_LEN(rxq->ring), BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); mvpp2_rxq_status_update(sc, rxq->id, nrecv, nrecv); if_input(ifp, &ml); } /* * We have a pool per core, and since we should not assume that * RX buffers are always used in order, keep a list of rxbuf[] * indices that should be filled with an mbuf, if possible. */ void mvpp2_rx_refill(struct mvpp2_port *sc) { struct mvpp2_bm_pool *bm; struct mvpp2_buf *rxb; uint64_t phys, virt; int pool; pool = curcpu()->ci_cpuid; KASSERT(pool < sc->sc->sc_npools); bm = &sc->sc->sc_bm_pools[pool]; while (bm->freelist[bm->free_cons] != -1) { virt = bm->freelist[bm->free_cons]; KASSERT(((virt >> 16) & 0xffff) == pool); KASSERT((virt & 0xffff) < MVPP2_BM_SIZE); rxb = &bm->rxbuf[virt & 0xffff]; KASSERT(rxb->mb_m == NULL); rxb->mb_m = mvpp2_alloc_mbuf(sc->sc, rxb->mb_map); if (rxb->mb_m == NULL) break; bm->freelist[bm->free_cons] = -1; bm->free_cons = (bm->free_cons + 1) % MVPP2_BM_SIZE; phys = rxb->mb_map->dm_segs[0].ds_addr; mvpp2_write(sc->sc, MVPP22_BM_ADDR_HIGH_RLS_REG, (((virt >> 32) & MVPP22_ADDR_HIGH_MASK) << MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) | ((phys >> 32) & MVPP22_ADDR_HIGH_MASK)); mvpp2_write(sc->sc, MVPP2_BM_VIRT_RLS_REG, virt & 0xffffffff); mvpp2_write(sc->sc, MVPP2_BM_PHY_RLS_REG(pool), phys & 0xffffffff); } } void mvpp2_up(struct mvpp2_port *sc) { struct ifnet *ifp = &sc->sc_ac.ac_if; int i; if (sc->sc_sfp) { rw_enter(&mvpp2_sff_lock, RW_WRITE); sfp_enable(sc->sc_sfp); rw_exit(&mvpp2_sff_lock); } mvpp2_prs_mac_da_accept(sc, etherbroadcastaddr, 1); mvpp2_prs_mac_da_accept(sc, sc->sc_lladdr, 1); mvpp2_prs_tag_mode_set(sc->sc, sc->sc_id, MVPP2_TAG_TYPE_MH); mvpp2_prs_def_flow(sc); for (i = 0; i < sc->sc_ntxq; i++) mvpp2_txq_hw_init(sc, &sc->sc_txqs[i]); mvpp2_tx_time_coal_set(sc, sc->sc_tx_time_coal); for (i = 0; i < sc->sc_nrxq; i++) mvpp2_rxq_hw_init(sc, &sc->sc_rxqs[i]); /* FIXME: rx buffer fill */ /* Configure media. */ if (LIST_FIRST(&sc->sc_mii.mii_phys)) mii_mediachg(&sc->sc_mii); /* Program promiscuous mode and multicast filters. */ mvpp2_iff(sc); ifp->if_flags |= IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); mvpp2_txp_max_tx_size_set(sc); /* XXX: single vector */ mvpp2_write(sc->sc, MVPP2_ISR_RX_TX_MASK_REG(sc->sc_id), MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK | MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK | MVPP2_CAUSE_MISC_SUM_MASK); mvpp2_interrupts_enable(sc, (1 << 0)); mvpp2_mac_config(sc); mvpp2_egress_enable(sc); mvpp2_ingress_enable(sc); timeout_add_sec(&sc->sc_tick, 1); } void mvpp2_aggr_txq_hw_init(struct mvpp2_softc *sc, struct mvpp2_tx_queue *txq) { struct mvpp2_buf *txb; int i; txq->ring = mvpp2_dmamem_alloc(sc, MVPP2_AGGR_TXQ_SIZE * sizeof(struct mvpp2_tx_desc), 32); KASSERT(txq->ring != NULL); txq->descs = MVPP2_DMA_KVA(txq->ring); txq->buf = mallocarray(MVPP2_AGGR_TXQ_SIZE, sizeof(struct mvpp2_buf), M_DEVBUF, M_WAITOK); for (i = 0; i < MVPP2_AGGR_TXQ_SIZE; i++) { txb = &txq->buf[i]; bus_dmamap_create(sc->sc_dmat, MCLBYTES, MVPP2_NTXSEGS, MCLBYTES, 0, BUS_DMA_WAITOK, &txb->mb_map); txb->mb_m = NULL; } bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(txq->ring), 0, MVPP2_DMA_LEN(txq->ring), BUS_DMASYNC_PREWRITE); txq->prod = mvpp2_read(sc, MVPP2_AGGR_TXQ_INDEX_REG(txq->id)); mvpp2_write(sc, MVPP2_AGGR_TXQ_DESC_ADDR_REG(txq->id), MVPP2_DMA_DVA(txq->ring) >> MVPP22_DESC_ADDR_OFFS); mvpp2_write(sc, MVPP2_AGGR_TXQ_DESC_SIZE_REG(txq->id), MVPP2_AGGR_TXQ_SIZE); } void mvpp2_txq_hw_init(struct mvpp2_port *sc, struct mvpp2_tx_queue *txq) { struct mvpp2_buf *txb; int desc, desc_per_txq; uint32_t reg; int i; txq->prod = txq->cons = 0; // txq->last_desc = txq->size - 1; txq->ring = mvpp2_dmamem_alloc(sc->sc, MVPP2_NTXDESC * sizeof(struct mvpp2_tx_desc), 32); KASSERT(txq->ring != NULL); txq->descs = MVPP2_DMA_KVA(txq->ring); txq->buf = mallocarray(MVPP2_NTXDESC, sizeof(struct mvpp2_buf), M_DEVBUF, M_WAITOK); for (i = 0; i < MVPP2_NTXDESC; i++) { txb = &txq->buf[i]; bus_dmamap_create(sc->sc_dmat, MCLBYTES, MVPP2_NTXSEGS, MCLBYTES, 0, BUS_DMA_WAITOK, &txb->mb_map); txb->mb_m = NULL; } bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(txq->ring), 0, MVPP2_DMA_LEN(txq->ring), BUS_DMASYNC_PREWRITE); mvpp2_write(sc->sc, MVPP2_TXQ_NUM_REG, txq->id); mvpp2_write(sc->sc, MVPP2_TXQ_DESC_ADDR_REG, MVPP2_DMA_DVA(txq->ring)); mvpp2_write(sc->sc, MVPP2_TXQ_DESC_SIZE_REG, MVPP2_NTXDESC & MVPP2_TXQ_DESC_SIZE_MASK); mvpp2_write(sc->sc, MVPP2_TXQ_INDEX_REG, 0); mvpp2_write(sc->sc, MVPP2_TXQ_RSVD_CLR_REG, txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET); reg = mvpp2_read(sc->sc, MVPP2_TXQ_PENDING_REG); reg &= ~MVPP2_TXQ_PENDING_MASK; mvpp2_write(sc->sc, MVPP2_TXQ_PENDING_REG, reg); desc_per_txq = 16; desc = (sc->sc_id * MVPP2_MAX_TXQ * desc_per_txq) + (txq->log_id * desc_per_txq); mvpp2_write(sc->sc, MVPP2_TXQ_PREF_BUF_REG, MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 | MVPP2_PREF_BUF_THRESH(desc_per_txq / 2)); /* WRR / EJP configuration - indirect access */ mvpp2_write(sc->sc, MVPP2_TXP_SCHED_PORT_INDEX_REG, mvpp2_egress_port(sc)); reg = mvpp2_read(sc->sc, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id)); reg &= ~MVPP2_TXQ_REFILL_PERIOD_ALL_MASK; reg |= MVPP2_TXQ_REFILL_PERIOD_MASK(1); reg |= MVPP2_TXQ_REFILL_TOKENS_ALL_MASK; mvpp2_write(sc->sc, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id), reg); mvpp2_write(sc->sc, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq->log_id), MVPP2_TXQ_TOKEN_SIZE_MAX); mvpp2_tx_pkts_coal_set(sc, txq, txq->done_pkts_coal); mvpp2_read(sc->sc, MVPP2_TXQ_SENT_REG(txq->id)); } void mvpp2_rxq_hw_init(struct mvpp2_port *sc, struct mvpp2_rx_queue *rxq) { rxq->prod = rxq->cons = 0; rxq->ring = mvpp2_dmamem_alloc(sc->sc, MVPP2_NRXDESC * sizeof(struct mvpp2_rx_desc), 32); KASSERT(rxq->ring != NULL); rxq->descs = MVPP2_DMA_KVA(rxq->ring); bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(rxq->ring), 0, MVPP2_DMA_LEN(rxq->ring), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); mvpp2_write(sc->sc, MVPP2_RXQ_STATUS_REG(rxq->id), 0); mvpp2_write(sc->sc, MVPP2_RXQ_NUM_REG, rxq->id); mvpp2_write(sc->sc, MVPP2_RXQ_DESC_ADDR_REG, MVPP2_DMA_DVA(rxq->ring) >> MVPP22_DESC_ADDR_OFFS); mvpp2_write(sc->sc, MVPP2_RXQ_DESC_SIZE_REG, MVPP2_NRXDESC); mvpp2_write(sc->sc, MVPP2_RXQ_INDEX_REG, 0); mvpp2_rxq_offset_set(sc, rxq->id, 0); mvpp2_rx_pkts_coal_set(sc, rxq, rxq->pkts_coal); mvpp2_rx_time_coal_set(sc, rxq, rxq->time_coal); mvpp2_rxq_status_update(sc, rxq->id, 0, MVPP2_NRXDESC); } void mvpp2_mac_reset_assert(struct mvpp2_port *sc) { mvpp2_gmac_write(sc, MVPP2_PORT_CTRL2_REG, mvpp2_gmac_read(sc, MVPP2_PORT_CTRL2_REG) | MVPP2_PORT_CTRL2_PORTMACRESET); if (sc->sc_gop_id == 0) mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL0_REG, mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL0_REG) & ~MV_XLG_MAC_CTRL0_MACRESETN); } void mvpp2_pcs_reset_assert(struct mvpp2_port *sc) { uint32_t reg; if (sc->sc_gop_id != 0) return; reg = mvpp2_mpcs_read(sc, MVPP22_MPCS_CLOCK_RESET); reg |= MVPP22_MPCS_CLK_DIV_PHASE_SET; reg &= ~MVPP22_MPCS_TX_SD_CLK_RESET; reg &= ~MVPP22_MPCS_RX_SD_CLK_RESET; reg &= ~MVPP22_MPCS_MAC_CLK_RESET; mvpp2_mpcs_write(sc, MVPP22_MPCS_CLOCK_RESET, reg); reg = mvpp2_xpcs_read(sc, MVPP22_XPCS_GLOBAL_CFG_0_REG); reg &= ~MVPP22_XPCS_PCSRESET; mvpp2_xpcs_write(sc, MVPP22_XPCS_GLOBAL_CFG_0_REG, reg); } void mvpp2_pcs_reset_deassert(struct mvpp2_port *sc) { uint32_t reg; if (sc->sc_gop_id != 0) return; if (sc->sc_phy_mode == PHY_MODE_10GBASER) { reg = mvpp2_mpcs_read(sc, MVPP22_MPCS_CLOCK_RESET); reg &= ~MVPP22_MPCS_CLK_DIV_PHASE_SET; reg |= MVPP22_MPCS_TX_SD_CLK_RESET; reg |= MVPP22_MPCS_RX_SD_CLK_RESET; reg |= MVPP22_MPCS_MAC_CLK_RESET; mvpp2_mpcs_write(sc, MVPP22_MPCS_CLOCK_RESET, reg); } else if (sc->sc_phy_mode == PHY_MODE_XAUI) { reg = mvpp2_xpcs_read(sc, MVPP22_XPCS_GLOBAL_CFG_0_REG); reg |= MVPP22_XPCS_PCSRESET; mvpp2_xpcs_write(sc, MVPP22_XPCS_GLOBAL_CFG_0_REG, reg); } } void mvpp2_mac_config(struct mvpp2_port *sc) { uint32_t reg; reg = mvpp2_gmac_read(sc, MVPP2_GMAC_AUTONEG_CONFIG); reg &= ~MVPP2_GMAC_FORCE_LINK_PASS; reg |= MVPP2_GMAC_FORCE_LINK_DOWN; mvpp2_gmac_write(sc, MVPP2_GMAC_AUTONEG_CONFIG, reg); if (sc->sc_gop_id == 0) { reg = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL0_REG); reg &= ~MV_XLG_MAC_CTRL0_FORCELINKPASS; reg |= MV_XLG_MAC_CTRL0_FORCELINKDOWN; mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL0_REG, reg); } mvpp2_port_disable(sc); mvpp2_mac_reset_assert(sc); mvpp2_pcs_reset_assert(sc); mvpp2_gop_intr_mask(sc); mvpp2_comphy_config(sc, 0); if (sc->sc_gop_id == 0 && (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI)) mvpp2_xlg_config(sc); else mvpp2_gmac_config(sc); mvpp2_comphy_config(sc, 1); mvpp2_gop_config(sc); mvpp2_pcs_reset_deassert(sc); if (sc->sc_gop_id == 0) { reg = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL3_REG); reg &= ~MV_XLG_MAC_CTRL3_MACMODESELECT_MASK; if (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI) reg |= MV_XLG_MAC_CTRL3_MACMODESELECT_10G; else reg |= MV_XLG_MAC_CTRL3_MACMODESELECT_GMAC; mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL3_REG, reg); } if (sc->sc_gop_id == 0 && (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI)) { reg = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL1_REG); reg &= ~MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_MASK; reg |= ((MCLBYTES - MVPP2_MH_SIZE) / 2) << MV_XLG_MAC_CTRL1_FRAMESIZELIMIT_OFFS; mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL1_REG, reg); } else { reg = mvpp2_gmac_read(sc, MVPP2_GMAC_CTRL_0_REG); reg &= ~MVPP2_GMAC_MAX_RX_SIZE_MASK; reg |= ((MCLBYTES - MVPP2_MH_SIZE) / 2) << MVPP2_GMAC_MAX_RX_SIZE_OFFS; mvpp2_gmac_write(sc, MVPP2_GMAC_CTRL_0_REG, reg); } mvpp2_gop_intr_unmask(sc); if (!(sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI)) { mvpp2_gmac_write(sc, MVPP2_PORT_CTRL2_REG, mvpp2_gmac_read(sc, MVPP2_PORT_CTRL2_REG) & ~MVPP2_PORT_CTRL2_PORTMACRESET); while (mvpp2_gmac_read(sc, MVPP2_PORT_CTRL2_REG) & MVPP2_PORT_CTRL2_PORTMACRESET) ; } mvpp2_port_enable(sc); if (sc->sc_inband_status) { reg = mvpp2_gmac_read(sc, MVPP2_GMAC_AUTONEG_CONFIG); reg &= ~MVPP2_GMAC_FORCE_LINK_PASS; reg &= ~MVPP2_GMAC_FORCE_LINK_DOWN; mvpp2_gmac_write(sc, MVPP2_GMAC_AUTONEG_CONFIG, reg); if (sc->sc_gop_id == 0) { reg = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL0_REG); reg &= ~MV_XLG_MAC_CTRL0_FORCELINKPASS; reg &= ~MV_XLG_MAC_CTRL0_FORCELINKDOWN; mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL0_REG, reg); } } else mvpp2_port_change(sc); } void mvpp2_xlg_config(struct mvpp2_port *sc) { uint32_t ctl0, ctl4; ctl0 = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL0_REG); ctl4 = mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL4_REG); ctl0 |= MV_XLG_MAC_CTRL0_MACRESETN; ctl4 &= ~MV_XLG_MAC_CTRL4_EN_IDLE_CHECK_FOR_LINK; ctl4 |= MV_XLG_MAC_CTRL4_FORWARD_PFC_EN; ctl4 |= MV_XLG_MAC_CTRL4_FORWARD_802_3X_FC_EN; mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL0_REG, ctl0); mvpp2_xlg_write(sc, MV_XLG_PORT_MAC_CTRL4_REG, ctl4); /* Port reset */ while ((mvpp2_xlg_read(sc, MV_XLG_PORT_MAC_CTRL0_REG) & MV_XLG_MAC_CTRL0_MACRESETN) == 0) ; } void mvpp2_gmac_config(struct mvpp2_port *sc) { uint32_t ctl0, ctl2, ctl4, panc; /* Setup phy. */ ctl0 = mvpp2_gmac_read(sc, MVPP2_PORT_CTRL0_REG); ctl2 = mvpp2_gmac_read(sc, MVPP2_PORT_CTRL2_REG); ctl4 = mvpp2_gmac_read(sc, MVPP2_PORT_CTRL4_REG); panc = mvpp2_gmac_read(sc, MVPP2_GMAC_AUTONEG_CONFIG); ctl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK; ctl2 &= ~(MVPP2_GMAC_PORT_RESET_MASK | MVPP2_GMAC_PCS_ENABLE_MASK | MVPP2_GMAC_INBAND_AN_MASK); panc &= ~(MVPP2_GMAC_AN_DUPLEX_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG | MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FC_ADV_EN | MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS | MVPP2_GMAC_IN_BAND_AUTONEG); switch (sc->sc_phy_mode) { case PHY_MODE_XAUI: case PHY_MODE_10GBASER: break; case PHY_MODE_2500BASEX: case PHY_MODE_1000BASEX: ctl2 |= MVPP2_GMAC_PCS_ENABLE_MASK; ctl4 &= ~MVPP2_PORT_CTRL4_EXT_PIN_GMII_SEL; ctl4 |= MVPP2_PORT_CTRL4_SYNC_BYPASS; ctl4 |= MVPP2_PORT_CTRL4_DP_CLK_SEL; ctl4 |= MVPP2_PORT_CTRL4_QSGMII_BYPASS_ACTIVE; break; case PHY_MODE_SGMII: ctl2 |= MVPP2_GMAC_PCS_ENABLE_MASK; ctl2 |= MVPP2_GMAC_INBAND_AN_MASK; ctl4 &= ~MVPP2_PORT_CTRL4_EXT_PIN_GMII_SEL; ctl4 |= MVPP2_PORT_CTRL4_SYNC_BYPASS; ctl4 |= MVPP2_PORT_CTRL4_DP_CLK_SEL; ctl4 |= MVPP2_PORT_CTRL4_QSGMII_BYPASS_ACTIVE; break; case PHY_MODE_RGMII: case PHY_MODE_RGMII_ID: case PHY_MODE_RGMII_RXID: case PHY_MODE_RGMII_TXID: ctl4 &= ~MVPP2_PORT_CTRL4_DP_CLK_SEL; ctl4 |= MVPP2_PORT_CTRL4_EXT_PIN_GMII_SEL; ctl4 |= MVPP2_PORT_CTRL4_SYNC_BYPASS; ctl4 |= MVPP2_PORT_CTRL4_QSGMII_BYPASS_ACTIVE; break; } /* Use Auto-Negotiation for Inband Status only */ if (sc->sc_inband_status) { panc &= ~MVPP2_GMAC_CONFIG_MII_SPEED; panc &= ~MVPP2_GMAC_CONFIG_GMII_SPEED; panc &= ~MVPP2_GMAC_CONFIG_FULL_DUPLEX; panc |= MVPP2_GMAC_IN_BAND_AUTONEG; /* TODO: read mode from SFP */ if (sc->sc_phy_mode == PHY_MODE_SGMII) { /* SGMII */ panc |= MVPP2_GMAC_AN_SPEED_EN; panc |= MVPP2_GMAC_AN_DUPLEX_EN; } else { /* 802.3z */ ctl0 |= MVPP2_GMAC_PORT_TYPE_MASK; panc |= MVPP2_GMAC_CONFIG_GMII_SPEED; panc |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; } } mvpp2_gmac_write(sc, MVPP2_PORT_CTRL0_REG, ctl0); mvpp2_gmac_write(sc, MVPP2_PORT_CTRL2_REG, ctl2); mvpp2_gmac_write(sc, MVPP2_PORT_CTRL4_REG, ctl4); mvpp2_gmac_write(sc, MVPP2_GMAC_AUTONEG_CONFIG, panc); } #define COMPHY_BASE 0x120000 #define COMPHY_SIP_POWER_ON 0x82000001 #define COMPHY_SIP_POWER_OFF 0x82000002 #define COMPHY_SPEED(x) ((x) << 2) #define COMPHY_SPEED_1_25G 0 /* SGMII 1G */ #define COMPHY_SPEED_2_5G 1 #define COMPHY_SPEED_3_125G 2 /* SGMII 2.5G */ #define COMPHY_SPEED_5G 3 #define COMPHY_SPEED_5_15625G 4 /* XFI 5G */ #define COMPHY_SPEED_6G 5 #define COMPHY_SPEED_10_3125G 6 /* XFI 10G */ #define COMPHY_UNIT(x) ((x) << 8) #define COMPHY_MODE(x) ((x) << 12) #define COMPHY_MODE_SATA 1 #define COMPHY_MODE_SGMII 2 /* SGMII 1G */ #define COMPHY_MODE_HS_SGMII 3 /* SGMII 2.5G */ #define COMPHY_MODE_USB3H 4 #define COMPHY_MODE_USB3D 5 #define COMPHY_MODE_PCIE 6 #define COMPHY_MODE_RXAUI 7 #define COMPHY_MODE_XFI 8 #define COMPHY_MODE_SFI 9 #define COMPHY_MODE_USB3 10 #define COMPHY_MODE_AP 11 void mvpp2_comphy_config(struct mvpp2_port *sc, int on) { int node, phys[2], lane, unit; uint32_t mode; if (OF_getpropintarray(sc->sc_node, "phys", phys, sizeof(phys)) != sizeof(phys)) return; node = OF_getnodebyphandle(phys[0]); if (!node) return; lane = OF_getpropint(node, "reg", 0); unit = phys[1]; switch (sc->sc_phy_mode) { case PHY_MODE_XAUI: mode = COMPHY_MODE(COMPHY_MODE_RXAUI) | COMPHY_UNIT(unit); break; case PHY_MODE_10GBASER: mode = COMPHY_MODE(COMPHY_MODE_XFI) | COMPHY_SPEED(COMPHY_SPEED_10_3125G) | COMPHY_UNIT(unit); break; case PHY_MODE_2500BASEX: mode = COMPHY_MODE(COMPHY_MODE_HS_SGMII) | COMPHY_SPEED(COMPHY_SPEED_3_125G) | COMPHY_UNIT(unit); break; case PHY_MODE_1000BASEX: case PHY_MODE_SGMII: mode = COMPHY_MODE(COMPHY_MODE_SGMII) | COMPHY_SPEED(COMPHY_SPEED_1_25G) | COMPHY_UNIT(unit); break; default: return; } if (on) smc_call(COMPHY_SIP_POWER_ON, sc->sc->sc_ioh_paddr + COMPHY_BASE, lane, mode); else smc_call(COMPHY_SIP_POWER_OFF, sc->sc->sc_ioh_paddr + COMPHY_BASE, lane, 0); } void mvpp2_gop_config(struct mvpp2_port *sc) { uint32_t reg; if (sc->sc->sc_rm == NULL) return; if (sc->sc_phy_mode == PHY_MODE_RGMII || sc->sc_phy_mode == PHY_MODE_RGMII_ID || sc->sc_phy_mode == PHY_MODE_RGMII_RXID || sc->sc_phy_mode == PHY_MODE_RGMII_TXID) { if (sc->sc_gop_id == 0) return; reg = regmap_read_4(sc->sc->sc_rm, GENCONF_PORT_CTRL0); reg |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT; regmap_write_4(sc->sc->sc_rm, GENCONF_PORT_CTRL0, reg); reg = regmap_read_4(sc->sc->sc_rm, GENCONF_CTRL0); if (sc->sc_gop_id == 2) reg |= GENCONF_CTRL0_PORT0_RGMII | GENCONF_CTRL0_PORT1_RGMII; else if (sc->sc_gop_id == 3) reg |= GENCONF_CTRL0_PORT1_RGMII_MII; regmap_write_4(sc->sc->sc_rm, GENCONF_CTRL0, reg); } else if (sc->sc_phy_mode == PHY_MODE_2500BASEX || sc->sc_phy_mode == PHY_MODE_1000BASEX || sc->sc_phy_mode == PHY_MODE_SGMII) { reg = regmap_read_4(sc->sc->sc_rm, GENCONF_PORT_CTRL0); reg |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT | GENCONF_PORT_CTRL0_RX_DATA_SAMPLE; regmap_write_4(sc->sc->sc_rm, GENCONF_PORT_CTRL0, reg); if (sc->sc_gop_id > 1) { reg = regmap_read_4(sc->sc->sc_rm, GENCONF_CTRL0); if (sc->sc_gop_id == 2) reg &= ~GENCONF_CTRL0_PORT0_RGMII; else if (sc->sc_gop_id == 3) reg &= ~GENCONF_CTRL0_PORT1_RGMII_MII; regmap_write_4(sc->sc->sc_rm, GENCONF_CTRL0, reg); } } else if (sc->sc_phy_mode == PHY_MODE_10GBASER) { if (sc->sc_gop_id != 0) return; reg = mvpp2_xpcs_read(sc, MVPP22_XPCS_GLOBAL_CFG_0_REG); reg &= ~MVPP22_XPCS_PCSMODE_MASK; reg &= ~MVPP22_XPCS_LANEACTIVE_MASK; reg |= 2 << MVPP22_XPCS_LANEACTIVE_OFFS; mvpp2_xpcs_write(sc, MVPP22_XPCS_GLOBAL_CFG_0_REG, reg); reg = mvpp2_mpcs_read(sc, MVPP22_MPCS40G_COMMON_CONTROL); reg &= ~MVPP22_MPCS_FORWARD_ERROR_CORRECTION_MASK; mvpp2_mpcs_write(sc, MVPP22_MPCS40G_COMMON_CONTROL, reg); reg = mvpp2_mpcs_read(sc, MVPP22_MPCS_CLOCK_RESET); reg &= ~MVPP22_MPCS_CLK_DIVISION_RATIO_MASK; reg |= MVPP22_MPCS_CLK_DIVISION_RATIO_DEFAULT; mvpp2_mpcs_write(sc, MVPP22_MPCS_CLOCK_RESET, reg); } else return; reg = regmap_read_4(sc->sc->sc_rm, GENCONF_PORT_CTRL1); reg |= GENCONF_PORT_CTRL1_RESET(sc->sc_gop_id) | GENCONF_PORT_CTRL1_EN(sc->sc_gop_id); regmap_write_4(sc->sc->sc_rm, GENCONF_PORT_CTRL1, reg); reg = regmap_read_4(sc->sc->sc_rm, GENCONF_PORT_CTRL0); reg |= GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR; regmap_write_4(sc->sc->sc_rm, GENCONF_PORT_CTRL0, reg); reg = regmap_read_4(sc->sc->sc_rm, GENCONF_SOFT_RESET1); reg |= GENCONF_SOFT_RESET1_GOP; regmap_write_4(sc->sc->sc_rm, GENCONF_SOFT_RESET1, reg); } void mvpp2_gop_intr_mask(struct mvpp2_port *sc) { uint32_t reg; if (sc->sc_gop_id == 0) { reg = mvpp2_xlg_read(sc, MV_XLG_EXTERNAL_INTERRUPT_MASK_REG); reg &= ~MV_XLG_EXTERNAL_INTERRUPT_LINK_CHANGE_XLG; reg &= ~MV_XLG_EXTERNAL_INTERRUPT_LINK_CHANGE_GIG; mvpp2_xlg_write(sc, MV_XLG_EXTERNAL_INTERRUPT_MASK_REG, reg); } reg = mvpp2_gmac_read(sc, MVPP2_GMAC_INT_SUM_MASK_REG); reg &= ~MVPP2_GMAC_INT_SUM_CAUSE_LINK_CHANGE; mvpp2_gmac_write(sc, MVPP2_GMAC_INT_SUM_MASK_REG, reg); } void mvpp2_gop_intr_unmask(struct mvpp2_port *sc) { uint32_t reg; reg = mvpp2_gmac_read(sc, MVPP2_GMAC_INT_SUM_MASK_REG); reg |= MVPP2_GMAC_INT_SUM_CAUSE_LINK_CHANGE; mvpp2_gmac_write(sc, MVPP2_GMAC_INT_SUM_MASK_REG, reg); if (sc->sc_gop_id == 0) { reg = mvpp2_xlg_read(sc, MV_XLG_EXTERNAL_INTERRUPT_MASK_REG); if (sc->sc_phy_mode == PHY_MODE_10GBASER || sc->sc_phy_mode == PHY_MODE_XAUI) reg |= MV_XLG_EXTERNAL_INTERRUPT_LINK_CHANGE_XLG; else reg |= MV_XLG_EXTERNAL_INTERRUPT_LINK_CHANGE_GIG; mvpp2_xlg_write(sc, MV_XLG_EXTERNAL_INTERRUPT_MASK_REG, reg); } } void mvpp2_down(struct mvpp2_port *sc) { struct ifnet *ifp = &sc->sc_ac.ac_if; uint32_t reg; int i; timeout_del(&sc->sc_tick); ifp->if_flags &= ~IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); mvpp2_egress_disable(sc); mvpp2_ingress_disable(sc); mvpp2_mac_reset_assert(sc); mvpp2_pcs_reset_assert(sc); /* XXX: single vector */ mvpp2_interrupts_disable(sc, (1 << 0)); mvpp2_write(sc->sc, MVPP2_ISR_RX_TX_MASK_REG(sc->sc_id), 0); reg = mvpp2_read(sc->sc, MVPP2_TX_PORT_FLUSH_REG); reg |= MVPP2_TX_PORT_FLUSH_MASK(sc->sc_id); mvpp2_write(sc->sc, MVPP2_TX_PORT_FLUSH_REG, reg); for (i = 0; i < sc->sc_ntxq; i++) mvpp2_txq_hw_deinit(sc, &sc->sc_txqs[i]); reg &= ~MVPP2_TX_PORT_FLUSH_MASK(sc->sc_id); mvpp2_write(sc->sc, MVPP2_TX_PORT_FLUSH_REG, reg); for (i = 0; i < sc->sc_nrxq; i++) mvpp2_rxq_hw_deinit(sc, &sc->sc_rxqs[i]); if (sc->sc_sfp) { rw_enter(&mvpp2_sff_lock, RW_WRITE); sfp_disable(sc->sc_sfp); rw_exit(&mvpp2_sff_lock); } } void mvpp2_txq_hw_deinit(struct mvpp2_port *sc, struct mvpp2_tx_queue *txq) { struct mvpp2_buf *txb; int i, pending; uint32_t reg; mvpp2_write(sc->sc, MVPP2_TXQ_NUM_REG, txq->id); reg = mvpp2_read(sc->sc, MVPP2_TXQ_PREF_BUF_REG); reg |= MVPP2_TXQ_DRAIN_EN_MASK; mvpp2_write(sc->sc, MVPP2_TXQ_PREF_BUF_REG, reg); /* * the queue has been stopped so wait for all packets * to be transmitted. */ i = 0; do { if (i >= MVPP2_TX_PENDING_TIMEOUT_MSEC) { printf("%s: port %d: cleaning queue %d timed out\n", sc->sc_dev.dv_xname, sc->sc_id, txq->log_id); break; } delay(1000); i++; pending = mvpp2_read(sc->sc, MVPP2_TXQ_PENDING_REG) & MVPP2_TXQ_PENDING_MASK; } while (pending); reg &= ~MVPP2_TXQ_DRAIN_EN_MASK; mvpp2_write(sc->sc, MVPP2_TXQ_PREF_BUF_REG, reg); mvpp2_write(sc->sc, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->log_id), 0); mvpp2_write(sc->sc, MVPP2_TXQ_NUM_REG, txq->id); mvpp2_write(sc->sc, MVPP2_TXQ_DESC_ADDR_REG, 0); mvpp2_write(sc->sc, MVPP2_TXQ_DESC_SIZE_REG, 0); mvpp2_read(sc->sc, MVPP2_TXQ_SENT_REG(txq->id)); for (i = 0; i < MVPP2_NTXDESC; i++) { txb = &txq->buf[i]; if (txb->mb_m) { bus_dmamap_sync(sc->sc_dmat, txb->mb_map, 0, txb->mb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sc_dmat, txb->mb_map); m_freem(txb->mb_m); } bus_dmamap_destroy(sc->sc_dmat, txb->mb_map); } mvpp2_dmamem_free(sc->sc, txq->ring); free(txq->buf, M_DEVBUF, sizeof(struct mvpp2_buf) * MVPP2_NTXDESC); } void mvpp2_rxq_hw_drop(struct mvpp2_port *sc, struct mvpp2_rx_queue *rxq) { struct mvpp2_rx_desc *rxd; struct mvpp2_bm_pool *bm; uint64_t phys, virt; uint32_t i, nrecv, pool; struct mvpp2_buf *rxb; nrecv = mvpp2_rxq_received(sc, rxq->id); if (!nrecv) return; bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(rxq->ring), 0, MVPP2_DMA_LEN(rxq->ring), BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (i = 0; i < nrecv; i++) { rxd = &rxq->descs[rxq->cons]; virt = rxd->buf_cookie_bm_qset_cls_info; pool = (virt >> 16) & 0xffff; KASSERT(pool < sc->sc->sc_npools); bm = &sc->sc->sc_bm_pools[pool]; KASSERT((virt & 0xffff) < MVPP2_BM_SIZE); rxb = &bm->rxbuf[virt & 0xffff]; KASSERT(rxb->mb_m != NULL); virt &= 0xffffffff; phys = rxb->mb_map->dm_segs[0].ds_addr; mvpp2_write(sc->sc, MVPP22_BM_ADDR_HIGH_RLS_REG, (((virt >> 32) & MVPP22_ADDR_HIGH_MASK) << MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) | ((phys >> 32) & MVPP22_ADDR_HIGH_MASK)); mvpp2_write(sc->sc, MVPP2_BM_VIRT_RLS_REG, virt & 0xffffffff); mvpp2_write(sc->sc, MVPP2_BM_PHY_RLS_REG(pool), phys & 0xffffffff); rxq->cons = (rxq->cons + 1) % MVPP2_NRXDESC; } bus_dmamap_sync(sc->sc_dmat, MVPP2_DMA_MAP(rxq->ring), 0, MVPP2_DMA_LEN(rxq->ring), BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); mvpp2_rxq_status_update(sc, rxq->id, nrecv, nrecv); } void mvpp2_rxq_hw_deinit(struct mvpp2_port *sc, struct mvpp2_rx_queue *rxq) { mvpp2_rxq_hw_drop(sc, rxq); mvpp2_write(sc->sc, MVPP2_RXQ_STATUS_REG(rxq->id), 0); mvpp2_write(sc->sc, MVPP2_RXQ_NUM_REG, rxq->id); mvpp2_write(sc->sc, MVPP2_RXQ_DESC_ADDR_REG, 0); mvpp2_write(sc->sc, MVPP2_RXQ_DESC_SIZE_REG, 0); mvpp2_dmamem_free(sc->sc, rxq->ring); } void mvpp2_rxq_long_pool_set(struct mvpp2_port *port, int lrxq, int pool) { uint32_t val; int prxq; /* get queue physical ID */ prxq = port->sc_rxqs[lrxq].id; val = mvpp2_read(port->sc, MVPP2_RXQ_CONFIG_REG(prxq)); val &= ~MVPP2_RXQ_POOL_LONG_MASK; val |= ((pool << MVPP2_RXQ_POOL_LONG_OFFS) & MVPP2_RXQ_POOL_LONG_MASK); mvpp2_write(port->sc, MVPP2_RXQ_CONFIG_REG(prxq), val); } void mvpp2_rxq_short_pool_set(struct mvpp2_port *port, int lrxq, int pool) { uint32_t val; int prxq; /* get queue physical ID */ prxq = port->sc_rxqs[lrxq].id; val = mvpp2_read(port->sc, MVPP2_RXQ_CONFIG_REG(prxq)); val &= ~MVPP2_RXQ_POOL_SHORT_MASK; val |= ((pool << MVPP2_RXQ_POOL_SHORT_OFFS) & MVPP2_RXQ_POOL_SHORT_MASK); mvpp2_write(port->sc, MVPP2_RXQ_CONFIG_REG(prxq), val); } void mvpp2_iff(struct mvpp2_port *sc) { struct arpcom *ac = &sc->sc_ac; struct ifnet *ifp = &sc->sc_ac.ac_if; struct ether_multi *enm; struct ether_multistep step; ifp->if_flags &= ~IFF_ALLMULTI; /* Removes all but broadcast and (new) lladdr */ mvpp2_prs_mac_del_all(sc); if (ifp->if_flags & IFF_PROMISC) { mvpp2_prs_mac_promisc_set(sc->sc, sc->sc_id, MVPP2_PRS_L2_UNI_CAST, 1); mvpp2_prs_mac_promisc_set(sc->sc, sc->sc_id, MVPP2_PRS_L2_MULTI_CAST, 1); return; } mvpp2_prs_mac_promisc_set(sc->sc, sc->sc_id, MVPP2_PRS_L2_UNI_CAST, 0); mvpp2_prs_mac_promisc_set(sc->sc, sc->sc_id, MVPP2_PRS_L2_MULTI_CAST, 0); if (ac->ac_multirangecnt > 0 || ac->ac_multicnt > MVPP2_PRS_MAC_MC_FILT_MAX) { ifp->if_flags |= IFF_ALLMULTI; mvpp2_prs_mac_promisc_set(sc->sc, sc->sc_id, MVPP2_PRS_L2_MULTI_CAST, 1); } else { ETHER_FIRST_MULTI(step, ac, enm); while (enm != NULL) { mvpp2_prs_mac_da_accept(sc, enm->enm_addrlo, 1); ETHER_NEXT_MULTI(step, enm); } } } struct mvpp2_dmamem * mvpp2_dmamem_alloc(struct mvpp2_softc *sc, bus_size_t size, bus_size_t align) { struct mvpp2_dmamem *mdm; int nsegs; mdm = malloc(sizeof(*mdm), M_DEVBUF, M_WAITOK | M_ZERO); mdm->mdm_size = size; if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &mdm->mdm_map) != 0) goto mdmfree; if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &mdm->mdm_seg, 1, &nsegs, BUS_DMA_WAITOK) != 0) goto destroy; if (bus_dmamem_map(sc->sc_dmat, &mdm->mdm_seg, nsegs, size, &mdm->mdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0) goto free; if (bus_dmamap_load(sc->sc_dmat, mdm->mdm_map, mdm->mdm_kva, size, NULL, BUS_DMA_WAITOK) != 0) goto unmap; bzero(mdm->mdm_kva, size); return (mdm); unmap: bus_dmamem_unmap(sc->sc_dmat, mdm->mdm_kva, size); free: bus_dmamem_free(sc->sc_dmat, &mdm->mdm_seg, 1); destroy: bus_dmamap_destroy(sc->sc_dmat, mdm->mdm_map); mdmfree: free(mdm, M_DEVBUF, 0); return (NULL); } void mvpp2_dmamem_free(struct mvpp2_softc *sc, struct mvpp2_dmamem *mdm) { bus_dmamem_unmap(sc->sc_dmat, mdm->mdm_kva, mdm->mdm_size); bus_dmamem_free(sc->sc_dmat, &mdm->mdm_seg, 1); bus_dmamap_destroy(sc->sc_dmat, mdm->mdm_map); free(mdm, M_DEVBUF, 0); } struct mbuf * mvpp2_alloc_mbuf(struct mvpp2_softc *sc, bus_dmamap_t map) { struct mbuf *m = NULL; m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES); if (!m) return (NULL); m->m_len = m->m_pkthdr.len = MCLBYTES; if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT) != 0) { printf("%s: could not load mbuf DMA map", DEVNAME(sc)); m_freem(m); return (NULL); } bus_dmamap_sync(sc->sc_dmat, map, 0, m->m_pkthdr.len, BUS_DMASYNC_PREREAD); return (m); } void mvpp2_interrupts_enable(struct mvpp2_port *port, int cpu_mask) { mvpp2_write(port->sc, MVPP2_ISR_ENABLE_REG(port->sc_id), MVPP2_ISR_ENABLE_INTERRUPT(cpu_mask)); } void mvpp2_interrupts_disable(struct mvpp2_port *port, int cpu_mask) { mvpp2_write(port->sc, MVPP2_ISR_ENABLE_REG(port->sc_id), MVPP2_ISR_DISABLE_INTERRUPT(cpu_mask)); } int mvpp2_egress_port(struct mvpp2_port *port) { return MVPP2_MAX_TCONT + port->sc_id; } int mvpp2_txq_phys(int port, int txq) { return (MVPP2_MAX_TCONT + port) * MVPP2_MAX_TXQ + txq; } void mvpp2_defaults_set(struct mvpp2_port *port) { int val, queue; mvpp2_write(port->sc, MVPP2_TXP_SCHED_PORT_INDEX_REG, mvpp2_egress_port(port)); mvpp2_write(port->sc, MVPP2_TXP_SCHED_CMD_1_REG, 0); for (queue = 0; queue < MVPP2_MAX_TXQ; queue++) mvpp2_write(port->sc, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(queue), 0); mvpp2_write(port->sc, MVPP2_TXP_SCHED_PERIOD_REG, port->sc->sc_tclk / (1000 * 1000)); val = mvpp2_read(port->sc, MVPP2_TXP_SCHED_REFILL_REG); val &= ~MVPP2_TXP_REFILL_PERIOD_ALL_MASK; val |= MVPP2_TXP_REFILL_PERIOD_MASK(1); val |= MVPP2_TXP_REFILL_TOKENS_ALL_MASK; mvpp2_write(port->sc, MVPP2_TXP_SCHED_REFILL_REG, val); val = MVPP2_TXP_TOKEN_SIZE_MAX; mvpp2_write(port->sc, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); /* set maximum_low_latency_packet_size value to 256 */ mvpp2_write(port->sc, MVPP2_RX_CTRL_REG(port->sc_id), MVPP2_RX_USE_PSEUDO_FOR_CSUM_MASK | MVPP2_RX_LOW_LATENCY_PKT_SIZE(256)); /* mask all interrupts to all present cpus */ mvpp2_interrupts_disable(port, (0xf << 0)); } void mvpp2_ingress_enable(struct mvpp2_port *port) { uint32_t val; int lrxq, queue; for (lrxq = 0; lrxq < port->sc_nrxq; lrxq++) { queue = port->sc_rxqs[lrxq].id; val = mvpp2_read(port->sc, MVPP2_RXQ_CONFIG_REG(queue)); val &= ~MVPP2_RXQ_DISABLE_MASK; mvpp2_write(port->sc, MVPP2_RXQ_CONFIG_REG(queue), val); } } void mvpp2_ingress_disable(struct mvpp2_port *port) { uint32_t val; int lrxq, queue; for (lrxq = 0; lrxq < port->sc_nrxq; lrxq++) { queue = port->sc_rxqs[lrxq].id; val = mvpp2_read(port->sc, MVPP2_RXQ_CONFIG_REG(queue)); val |= MVPP2_RXQ_DISABLE_MASK; mvpp2_write(port->sc, MVPP2_RXQ_CONFIG_REG(queue), val); } } void mvpp2_egress_enable(struct mvpp2_port *port) { struct mvpp2_tx_queue *txq; uint32_t qmap; int queue; qmap = 0; for (queue = 0; queue < port->sc_ntxq; queue++) { txq = &port->sc_txqs[queue]; if (txq->descs != NULL) { qmap |= (1 << queue); } } mvpp2_write(port->sc, MVPP2_TXP_SCHED_PORT_INDEX_REG, mvpp2_egress_port(port)); mvpp2_write(port->sc, MVPP2_TXP_SCHED_Q_CMD_REG, qmap); } void mvpp2_egress_disable(struct mvpp2_port *port) { uint32_t reg_data; int i; mvpp2_write(port->sc, MVPP2_TXP_SCHED_PORT_INDEX_REG, mvpp2_egress_port(port)); reg_data = (mvpp2_read(port->sc, MVPP2_TXP_SCHED_Q_CMD_REG)) & MVPP2_TXP_SCHED_ENQ_MASK; if (reg_data) mvpp2_write(port->sc, MVPP2_TXP_SCHED_Q_CMD_REG, reg_data << MVPP2_TXP_SCHED_DISQ_OFFSET); i = 0; do { if (i >= MVPP2_TX_DISABLE_TIMEOUT_MSEC) { printf("%s: tx stop timed out, status=0x%08x\n", port->sc_dev.dv_xname, reg_data); break; } delay(1000); i++; reg_data = mvpp2_read(port->sc, MVPP2_TXP_SCHED_Q_CMD_REG); } while (reg_data & MVPP2_TXP_SCHED_ENQ_MASK); } void mvpp2_port_enable(struct mvpp2_port *port) { uint32_t val; if (port->sc_gop_id == 0 && (port->sc_phy_mode == PHY_MODE_10GBASER || port->sc_phy_mode == PHY_MODE_XAUI)) { val = mvpp2_xlg_read(port, MV_XLG_PORT_MAC_CTRL0_REG); val |= MV_XLG_MAC_CTRL0_PORTEN; val &= ~MV_XLG_MAC_CTRL0_MIBCNTDIS; mvpp2_xlg_write(port, MV_XLG_PORT_MAC_CTRL0_REG, val); } else { val = mvpp2_gmac_read(port, MVPP2_GMAC_CTRL_0_REG); val |= MVPP2_GMAC_PORT_EN_MASK; val |= MVPP2_GMAC_MIB_CNTR_EN_MASK; mvpp2_gmac_write(port, MVPP2_GMAC_CTRL_0_REG, val); } } void mvpp2_port_disable(struct mvpp2_port *port) { uint32_t val; if (port->sc_gop_id == 0 && (port->sc_phy_mode == PHY_MODE_10GBASER || port->sc_phy_mode == PHY_MODE_XAUI)) { val = mvpp2_xlg_read(port, MV_XLG_PORT_MAC_CTRL0_REG); val &= ~MV_XLG_MAC_CTRL0_PORTEN; mvpp2_xlg_write(port, MV_XLG_PORT_MAC_CTRL0_REG, val); } val = mvpp2_gmac_read(port, MVPP2_GMAC_CTRL_0_REG); val &= ~MVPP2_GMAC_PORT_EN_MASK; mvpp2_gmac_write(port, MVPP2_GMAC_CTRL_0_REG, val); } int mvpp2_rxq_received(struct mvpp2_port *port, int rxq_id) { uint32_t val = mvpp2_read(port->sc, MVPP2_RXQ_STATUS_REG(rxq_id)); return val & MVPP2_RXQ_OCCUPIED_MASK; } void mvpp2_rxq_status_update(struct mvpp2_port *port, int rxq_id, int used_count, int free_count) { uint32_t val = used_count | (free_count << MVPP2_RXQ_NUM_NEW_OFFSET); mvpp2_write(port->sc, MVPP2_RXQ_STATUS_UPDATE_REG(rxq_id), val); } void mvpp2_rxq_offset_set(struct mvpp2_port *port, int prxq, int offset) { uint32_t val; offset = offset >> 5; val = mvpp2_read(port->sc, MVPP2_RXQ_CONFIG_REG(prxq)); val &= ~MVPP2_RXQ_PACKET_OFFSET_MASK; val |= ((offset << MVPP2_RXQ_PACKET_OFFSET_OFFS) & MVPP2_RXQ_PACKET_OFFSET_MASK); mvpp2_write(port->sc, MVPP2_RXQ_CONFIG_REG(prxq), val); } void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port) { uint32_t val, size, mtu; int txq; mtu = MCLBYTES * 8; if (mtu > MVPP2_TXP_MTU_MAX) mtu = MVPP2_TXP_MTU_MAX; /* WA for wrong token bucket update: set MTU value = 3*real MTU value */ mtu = 3 * mtu; /* indirect access to reg_valisters */ mvpp2_write(port->sc, MVPP2_TXP_SCHED_PORT_INDEX_REG, mvpp2_egress_port(port)); /* set MTU */ val = mvpp2_read(port->sc, MVPP2_TXP_SCHED_MTU_REG); val &= ~MVPP2_TXP_MTU_MAX; val |= mtu; mvpp2_write(port->sc, MVPP2_TXP_SCHED_MTU_REG, val); /* TXP token size and all TXqs token size must be larger that MTU */ val = mvpp2_read(port->sc, MVPP2_TXP_SCHED_TOKEN_SIZE_REG); size = val & MVPP2_TXP_TOKEN_SIZE_MAX; if (size < mtu) { size = mtu; val &= ~MVPP2_TXP_TOKEN_SIZE_MAX; val |= size; mvpp2_write(port->sc, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); } for (txq = 0; txq < port->sc_ntxq; txq++) { val = mvpp2_read(port->sc, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq)); size = val & MVPP2_TXQ_TOKEN_SIZE_MAX; if (size < mtu) { size = mtu; val &= ~MVPP2_TXQ_TOKEN_SIZE_MAX; val |= size; mvpp2_write(port->sc, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq), val); } } } void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq, uint32_t pkts) { rxq->pkts_coal = pkts <= MVPP2_OCCUPIED_THRESH_MASK ? pkts : MVPP2_OCCUPIED_THRESH_MASK; mvpp2_write(port->sc, MVPP2_RXQ_NUM_REG, rxq->id); mvpp2_write(port->sc, MVPP2_RXQ_THRESH_REG, rxq->pkts_coal); } void mvpp2_tx_pkts_coal_set(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, uint32_t pkts) { txq->done_pkts_coal = pkts <= MVPP2_TRANSMITTED_THRESH_MASK ? pkts : MVPP2_TRANSMITTED_THRESH_MASK; mvpp2_write(port->sc, MVPP2_TXQ_NUM_REG, txq->id); mvpp2_write(port->sc, MVPP2_TXQ_THRESH_REG, txq->done_pkts_coal << MVPP2_TRANSMITTED_THRESH_OFFSET); } void mvpp2_rx_time_coal_set(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq, uint32_t usec) { uint32_t val; val = (port->sc->sc_tclk / (1000 * 1000)) * usec; mvpp2_write(port->sc, MVPP2_ISR_RX_THRESHOLD_REG(rxq->id), val); rxq->time_coal = usec; } void mvpp2_tx_time_coal_set(struct mvpp2_port *port, uint32_t usec) { uint32_t val; val = (port->sc->sc_tclk / (1000 * 1000)) * usec; mvpp2_write(port->sc, MVPP2_ISR_TX_THRESHOLD_REG(port->sc_id), val); port->sc_tx_time_coal = usec; } void mvpp2_prs_shadow_ri_set(struct mvpp2_softc *sc, int index, uint32_t ri, uint32_t ri_mask) { sc->sc_prs_shadow[index].ri_mask = ri_mask; sc->sc_prs_shadow[index].ri = ri; } void mvpp2_prs_tcam_lu_set(struct mvpp2_prs_entry *pe, uint32_t lu) { int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_LU_BYTE); pe->tcam.byte[MVPP2_PRS_TCAM_LU_BYTE] = lu; pe->tcam.byte[enable_off] = MVPP2_PRS_LU_MASK; } void mvpp2_prs_tcam_port_set(struct mvpp2_prs_entry *pe, uint32_t port, int add) { int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE); if (add) pe->tcam.byte[enable_off] &= ~(1 << port); else pe->tcam.byte[enable_off] |= (1 << port); } void mvpp2_prs_tcam_port_map_set(struct mvpp2_prs_entry *pe, uint32_t port_mask) { int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE); uint8_t mask = MVPP2_PRS_PORT_MASK; pe->tcam.byte[MVPP2_PRS_TCAM_PORT_BYTE] = 0; pe->tcam.byte[enable_off] &= ~mask; pe->tcam.byte[enable_off] |= ~port_mask & MVPP2_PRS_PORT_MASK; } uint32_t mvpp2_prs_tcam_port_map_get(struct mvpp2_prs_entry *pe) { int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE); return ~(pe->tcam.byte[enable_off]) & MVPP2_PRS_PORT_MASK; } void mvpp2_prs_tcam_data_byte_set(struct mvpp2_prs_entry *pe, uint32_t offs, uint8_t byte, uint8_t enable) { pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE(offs)] = byte; pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE_EN(offs)] = enable; } void mvpp2_prs_tcam_data_byte_get(struct mvpp2_prs_entry *pe, uint32_t offs, uint8_t *byte, uint8_t *enable) { *byte = pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE(offs)]; *enable = pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE_EN(offs)]; } int mvpp2_prs_tcam_data_cmp(struct mvpp2_prs_entry *pe, int offset, uint16_t data) { int byte_offset = MVPP2_PRS_TCAM_DATA_BYTE(offset); uint16_t tcam_data; tcam_data = (pe->tcam.byte[byte_offset + 1] << 8) | pe->tcam.byte[byte_offset]; return tcam_data == data; } void mvpp2_prs_tcam_ai_update(struct mvpp2_prs_entry *pe, uint32_t bits, uint32_t enable) { int i, ai_idx = MVPP2_PRS_TCAM_AI_BYTE; for (i = 0; i < MVPP2_PRS_AI_BITS; i++) { if (!(enable & BIT(i))) continue; if (bits & BIT(i)) pe->tcam.byte[ai_idx] |= BIT(i); else pe->tcam.byte[ai_idx] &= ~BIT(i); } pe->tcam.byte[MVPP2_PRS_TCAM_EN_OFFS(ai_idx)] |= enable; } int mvpp2_prs_tcam_ai_get(struct mvpp2_prs_entry *pe) { return pe->tcam.byte[MVPP2_PRS_TCAM_AI_BYTE]; } void mvpp2_prs_tcam_data_word_get(struct mvpp2_prs_entry *pe, uint32_t data_offset, uint32_t *word, uint32_t *enable) { int index, position; uint8_t byte, mask; for (index = 0; index < 4; index++) { position = (data_offset * sizeof(int)) + index; mvpp2_prs_tcam_data_byte_get(pe, position, &byte, &mask); ((uint8_t *)word)[index] = byte; ((uint8_t *)enable)[index] = mask; } } void mvpp2_prs_match_etype(struct mvpp2_prs_entry *pe, uint32_t offs, uint16_t ether_type) { mvpp2_prs_tcam_data_byte_set(pe, offs + 0, ether_type >> 8, 0xff); mvpp2_prs_tcam_data_byte_set(pe, offs + 1, ether_type & 0xff, 0xff); } void mvpp2_prs_sram_bits_set(struct mvpp2_prs_entry *pe, uint32_t bit, uint32_t val) { pe->sram.byte[bit / 8] |= (val << (bit % 8)); } void mvpp2_prs_sram_bits_clear(struct mvpp2_prs_entry *pe, uint32_t bit, uint32_t val) { pe->sram.byte[bit / 8] &= ~(val << (bit % 8)); } void mvpp2_prs_sram_ri_update(struct mvpp2_prs_entry *pe, uint32_t bits, uint32_t mask) { int i; for (i = 0; i < MVPP2_PRS_SRAM_RI_CTRL_BITS; i++) { if (!(mask & BIT(i))) continue; if (bits & BIT(i)) mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_RI_OFFS + i, 1); else mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_RI_OFFS + i, 1); mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_RI_CTRL_OFFS + i, 1); } } int mvpp2_prs_sram_ri_get(struct mvpp2_prs_entry *pe) { return pe->sram.word[MVPP2_PRS_SRAM_RI_WORD]; } void mvpp2_prs_sram_ai_update(struct mvpp2_prs_entry *pe, uint32_t bits, uint32_t mask) { int i; for (i = 0; i < MVPP2_PRS_SRAM_AI_CTRL_BITS; i++) { if (!(mask & BIT(i))) continue; if (bits & BIT(i)) mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_AI_OFFS + i, 1); else mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_AI_OFFS + i, 1); mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_AI_CTRL_OFFS + i, 1); } } int mvpp2_prs_sram_ai_get(struct mvpp2_prs_entry *pe) { uint8_t bits; int ai_off = MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_AI_OFFS); int ai_en_off = ai_off + 1; int ai_shift = MVPP2_PRS_SRAM_AI_OFFS % 8; bits = (pe->sram.byte[ai_off] >> ai_shift) | (pe->sram.byte[ai_en_off] << (8 - ai_shift)); return bits; } void mvpp2_prs_sram_shift_set(struct mvpp2_prs_entry *pe, int shift, uint32_t op) { if (shift < 0) { mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_SHIFT_SIGN_BIT, 1); shift = -shift; } else { mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_SHIFT_SIGN_BIT, 1); } pe->sram.byte[MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_SHIFT_OFFS)] |= shift & MVPP2_PRS_SRAM_SHIFT_MASK; mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS, MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK); mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS, op); mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS, 1); } void mvpp2_prs_sram_offset_set(struct mvpp2_prs_entry *pe, uint32_t type, int offset, uint32_t op) { uint8_t udf_byte, udf_byte_offset; uint8_t op_sel_udf_byte, op_sel_udf_byte_offset; udf_byte = MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_UDF_OFFS + MVPP2_PRS_SRAM_UDF_BITS); udf_byte_offset = (8 - (MVPP2_PRS_SRAM_UDF_OFFS % 8)); op_sel_udf_byte = MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS + MVPP2_PRS_SRAM_OP_SEL_UDF_BITS); op_sel_udf_byte_offset = (8 - (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8)); if (offset < 0) { mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_SIGN_BIT, 1); offset = -offset; } else { mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_SIGN_BIT, 1); } mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_OFFS, MVPP2_PRS_SRAM_UDF_MASK); mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_OFFS, offset); pe->sram.byte[udf_byte] &= ~(MVPP2_PRS_SRAM_UDF_MASK >> udf_byte_offset); pe->sram.byte[udf_byte] |= (offset >> udf_byte_offset); mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_TYPE_OFFS, MVPP2_PRS_SRAM_UDF_TYPE_MASK); mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_TYPE_OFFS, type); mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS, MVPP2_PRS_SRAM_OP_SEL_UDF_MASK); mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS, op); pe->sram.byte[op_sel_udf_byte] &= ~(MVPP2_PRS_SRAM_OP_SEL_UDF_MASK >> op_sel_udf_byte_offset); pe->sram.byte[op_sel_udf_byte] |= (op >> op_sel_udf_byte_offset); mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS, 1); } void mvpp2_prs_sram_next_lu_set(struct mvpp2_prs_entry *pe, uint32_t lu) { int sram_next_off = MVPP2_PRS_SRAM_NEXT_LU_OFFS; mvpp2_prs_sram_bits_clear(pe, sram_next_off, MVPP2_PRS_SRAM_NEXT_LU_MASK); mvpp2_prs_sram_bits_set(pe, sram_next_off, lu); } void mvpp2_prs_shadow_set(struct mvpp2_softc *sc, int index, uint32_t lu) { sc->sc_prs_shadow[index].valid = 1; sc->sc_prs_shadow[index].lu = lu; } int mvpp2_prs_hw_write(struct mvpp2_softc *sc, struct mvpp2_prs_entry *pe) { int i; if (pe->index > MVPP2_PRS_TCAM_SRAM_SIZE - 1) return EINVAL; pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] &= ~MVPP2_PRS_TCAM_INV_MASK; mvpp2_write(sc, MVPP2_PRS_TCAM_IDX_REG, pe->index); for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) mvpp2_write(sc, MVPP2_PRS_TCAM_DATA_REG(i), pe->tcam.word[i]); mvpp2_write(sc, MVPP2_PRS_SRAM_IDX_REG, pe->index); for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) mvpp2_write(sc, MVPP2_PRS_SRAM_DATA_REG(i), pe->sram.word[i]); return 0; } int mvpp2_prs_hw_read(struct mvpp2_softc *sc, struct mvpp2_prs_entry *pe, int tid) { int i; if (tid > MVPP2_PRS_TCAM_SRAM_SIZE - 1) return EINVAL; memset(pe, 0, sizeof(*pe)); pe->index = tid; mvpp2_write(sc, MVPP2_PRS_TCAM_IDX_REG, pe->index); pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] = mvpp2_read(sc, MVPP2_PRS_TCAM_DATA_REG(MVPP2_PRS_TCAM_INV_WORD)); if (pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] & MVPP2_PRS_TCAM_INV_MASK) return EINVAL; for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) pe->tcam.word[i] = mvpp2_read(sc, MVPP2_PRS_TCAM_DATA_REG(i)); mvpp2_write(sc, MVPP2_PRS_SRAM_IDX_REG, pe->index); for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) pe->sram.word[i] = mvpp2_read(sc, MVPP2_PRS_SRAM_DATA_REG(i)); return 0; } int mvpp2_prs_flow_find(struct mvpp2_softc *sc, int flow) { struct mvpp2_prs_entry pe; uint8_t bits; int tid; for (tid = MVPP2_PRS_TCAM_SRAM_SIZE - 1; tid >= 0; tid--) { if (!sc->sc_prs_shadow[tid].valid || sc->sc_prs_shadow[tid].lu != MVPP2_PRS_LU_FLOWS) continue; mvpp2_prs_hw_read(sc, &pe, tid); bits = mvpp2_prs_sram_ai_get(&pe); if ((bits & MVPP2_PRS_FLOW_ID_MASK) == flow) return tid; } return -1; } int mvpp2_prs_tcam_first_free(struct mvpp2_softc *sc, uint8_t start, uint8_t end) { uint8_t tmp; int tid; if (start > end) { tmp = end; end = start; start = tmp; } for (tid = start; tid <= end; tid++) { if (!sc->sc_prs_shadow[tid].valid) return tid; } return -1; } void mvpp2_prs_mac_drop_all_set(struct mvpp2_softc *sc, uint32_t port, int add) { struct mvpp2_prs_entry pe; if (sc->sc_prs_shadow[MVPP2_PE_DROP_ALL].valid) { mvpp2_prs_hw_read(sc, &pe, MVPP2_PE_DROP_ALL); } else { memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); pe.index = MVPP2_PE_DROP_ALL; mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, MVPP2_PRS_RI_DROP_MASK); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_MAC); mvpp2_prs_tcam_port_map_set(&pe, 0); } mvpp2_prs_tcam_port_set(&pe, port, add); mvpp2_prs_hw_write(sc, &pe); } void mvpp2_prs_mac_promisc_set(struct mvpp2_softc *sc, uint32_t port, int l2_cast, int add) { struct mvpp2_prs_entry pe; uint8_t cast_match; uint32_t ri; int tid; if (l2_cast == MVPP2_PRS_L2_UNI_CAST) { cast_match = MVPP2_PRS_UCAST_VAL; tid = MVPP2_PE_MAC_UC_PROMISCUOUS; ri = MVPP2_PRS_RI_L2_UCAST; } else { cast_match = MVPP2_PRS_MCAST_VAL; tid = MVPP2_PE_MAC_MC_PROMISCUOUS; ri = MVPP2_PRS_RI_L2_MCAST; } if (sc->sc_prs_shadow[tid].valid) { mvpp2_prs_hw_read(sc, &pe, tid); } else { memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); pe.index = tid; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); mvpp2_prs_sram_ri_update(&pe, ri, MVPP2_PRS_RI_L2_CAST_MASK); mvpp2_prs_tcam_data_byte_set(&pe, 0, cast_match, MVPP2_PRS_CAST_MASK); mvpp2_prs_sram_shift_set(&pe, 2 * ETHER_ADDR_LEN, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_tcam_port_map_set(&pe, 0); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_MAC); } mvpp2_prs_tcam_port_set(&pe, port, add); mvpp2_prs_hw_write(sc, &pe); } void mvpp2_prs_dsa_tag_set(struct mvpp2_softc *sc, uint32_t port, int add, int tagged, int extend) { struct mvpp2_prs_entry pe; int32_t tid, shift; if (extend) { tid = tagged ? MVPP2_PE_EDSA_TAGGED : MVPP2_PE_EDSA_UNTAGGED; shift = 8; } else { tid = tagged ? MVPP2_PE_DSA_TAGGED : MVPP2_PE_DSA_UNTAGGED; shift = 4; } if (sc->sc_prs_shadow[tid].valid) { mvpp2_prs_hw_read(sc, &pe, tid); } else { memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); pe.index = tid; mvpp2_prs_sram_shift_set(&pe, shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_DSA); if (tagged) { mvpp2_prs_tcam_data_byte_set(&pe, 0, MVPP2_PRS_TCAM_DSA_TAGGED_BIT, MVPP2_PRS_TCAM_DSA_TAGGED_BIT); mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); } else { mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE, MVPP2_PRS_RI_VLAN_MASK); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); } mvpp2_prs_tcam_port_map_set(&pe, 0); } mvpp2_prs_tcam_port_set(&pe, port, add); mvpp2_prs_hw_write(sc, &pe); } void mvpp2_prs_dsa_tag_ethertype_set(struct mvpp2_softc *sc, uint32_t port, int add, int tagged, int extend) { struct mvpp2_prs_entry pe; int32_t tid, shift, port_mask; if (extend) { tid = tagged ? MVPP2_PE_EDSA_TAGGED : MVPP2_PE_EDSA_UNTAGGED; port_mask = 0; shift = 8; } else { tid = tagged ? MVPP2_PE_DSA_TAGGED : MVPP2_PE_DSA_UNTAGGED; port_mask = MVPP2_PRS_PORT_MASK; shift = 4; } if (sc->sc_prs_shadow[tid].valid) { mvpp2_prs_hw_read(sc, &pe, tid); } else { memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); pe.index = tid; mvpp2_prs_match_etype(&pe, 0, 0xdada); mvpp2_prs_match_etype(&pe, 2, 0); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DSA_MASK, MVPP2_PRS_RI_DSA_MASK); mvpp2_prs_sram_shift_set(&pe, 2 * ETHER_ADDR_LEN + shift, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_DSA); if (tagged) { mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN + 2 + 3, MVPP2_PRS_TCAM_DSA_TAGGED_BIT, MVPP2_PRS_TCAM_DSA_TAGGED_BIT); mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_SRAM_AI_MASK); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); } else { mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_VLAN_NONE, MVPP2_PRS_RI_VLAN_MASK); mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_L2); } mvpp2_prs_tcam_port_map_set(&pe, port_mask); } mvpp2_prs_tcam_port_set(&pe, port, add); mvpp2_prs_hw_write(sc, &pe); } struct mvpp2_prs_entry * mvpp2_prs_vlan_find(struct mvpp2_softc *sc, uint16_t tpid, int ai) { struct mvpp2_prs_entry *pe; uint32_t ri_bits, ai_bits; int match, tid; pe = malloc(sizeof(*pe), M_TEMP, M_NOWAIT); if (pe == NULL) return NULL; mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); for (tid = MVPP2_PE_FIRST_FREE_TID; tid <= MVPP2_PE_LAST_FREE_TID; tid++) { if (!sc->sc_prs_shadow[tid].valid || sc->sc_prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN) continue; mvpp2_prs_hw_read(sc, pe, tid); match = mvpp2_prs_tcam_data_cmp(pe, 0, swap16(tpid)); if (!match) continue; ri_bits = mvpp2_prs_sram_ri_get(pe); ri_bits &= MVPP2_PRS_RI_VLAN_MASK; ai_bits = mvpp2_prs_tcam_ai_get(pe); ai_bits &= ~MVPP2_PRS_DBL_VLAN_AI_BIT; if (ai != ai_bits) continue; if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE || ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE) return pe; } free(pe, M_TEMP, sizeof(*pe)); return NULL; } int mvpp2_prs_vlan_add(struct mvpp2_softc *sc, uint16_t tpid, int ai, uint32_t port_map) { struct mvpp2_prs_entry *pe; uint32_t ri_bits; int tid_aux, tid; int ret = 0; pe = mvpp2_prs_vlan_find(sc, tpid, ai); if (pe == NULL) { tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_LAST_FREE_TID, MVPP2_PE_FIRST_FREE_TID); if (tid < 0) return tid; pe = malloc(sizeof(*pe), M_TEMP, M_NOWAIT); if (pe == NULL) return ENOMEM; /* get last double vlan tid */ for (tid_aux = MVPP2_PE_LAST_FREE_TID; tid_aux >= MVPP2_PE_FIRST_FREE_TID; tid_aux--) { if (!sc->sc_prs_shadow[tid_aux].valid || sc->sc_prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN) continue; mvpp2_prs_hw_read(sc, pe, tid_aux); ri_bits = mvpp2_prs_sram_ri_get(pe); if ((ri_bits & MVPP2_PRS_RI_VLAN_MASK) == MVPP2_PRS_RI_VLAN_DOUBLE) break; } if (tid <= tid_aux) { ret = EINVAL; goto error; } memset(pe, 0, sizeof(*pe)); mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); pe->index = tid; mvpp2_prs_match_etype(pe, 0, tpid); mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_L2); mvpp2_prs_sram_shift_set(pe, MVPP2_VLAN_TAG_LEN, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_ai_update(pe, 0, MVPP2_PRS_SRAM_AI_MASK); if (ai == MVPP2_PRS_SINGLE_VLAN_AI) { mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_SINGLE, MVPP2_PRS_RI_VLAN_MASK); } else { ai |= MVPP2_PRS_DBL_VLAN_AI_BIT; mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_TRIPLE, MVPP2_PRS_RI_VLAN_MASK); } mvpp2_prs_tcam_ai_update(pe, ai, MVPP2_PRS_SRAM_AI_MASK); mvpp2_prs_shadow_set(sc, pe->index, MVPP2_PRS_LU_VLAN); } mvpp2_prs_tcam_port_map_set(pe, port_map); mvpp2_prs_hw_write(sc, pe); error: free(pe, M_TEMP, sizeof(*pe)); return ret; } int mvpp2_prs_double_vlan_ai_free_get(struct mvpp2_softc *sc) { int i; for (i = 1; i < MVPP2_PRS_DBL_VLANS_MAX; i++) if (!sc->sc_prs_double_vlans[i]) return i; return -1; } struct mvpp2_prs_entry * mvpp2_prs_double_vlan_find(struct mvpp2_softc *sc, uint16_t tpid1, uint16_t tpid2) { struct mvpp2_prs_entry *pe; uint32_t ri_mask; int match, tid; pe = malloc(sizeof(*pe), M_TEMP, M_NOWAIT); if (pe == NULL) return NULL; mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); for (tid = MVPP2_PE_FIRST_FREE_TID; tid <= MVPP2_PE_LAST_FREE_TID; tid++) { if (!sc->sc_prs_shadow[tid].valid || sc->sc_prs_shadow[tid].lu != MVPP2_PRS_LU_VLAN) continue; mvpp2_prs_hw_read(sc, pe, tid); match = mvpp2_prs_tcam_data_cmp(pe, 0, swap16(tpid1)) && mvpp2_prs_tcam_data_cmp(pe, 4, swap16(tpid2)); if (!match) continue; ri_mask = mvpp2_prs_sram_ri_get(pe) & MVPP2_PRS_RI_VLAN_MASK; if (ri_mask == MVPP2_PRS_RI_VLAN_DOUBLE) return pe; } free(pe, M_TEMP, sizeof(*pe)); return NULL; } int mvpp2_prs_double_vlan_add(struct mvpp2_softc *sc, uint16_t tpid1, uint16_t tpid2, uint32_t port_map) { struct mvpp2_prs_entry *pe; int tid_aux, tid, ai, ret = 0; uint32_t ri_bits; pe = mvpp2_prs_double_vlan_find(sc, tpid1, tpid2); if (pe == NULL) { tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; pe = malloc(sizeof(*pe), M_TEMP, M_NOWAIT); if (pe == NULL) return ENOMEM; ai = mvpp2_prs_double_vlan_ai_free_get(sc); if (ai < 0) { ret = ai; goto error; } for (tid_aux = MVPP2_PE_FIRST_FREE_TID; tid_aux <= MVPP2_PE_LAST_FREE_TID; tid_aux++) { if (!sc->sc_prs_shadow[tid_aux].valid || sc->sc_prs_shadow[tid_aux].lu != MVPP2_PRS_LU_VLAN) continue; mvpp2_prs_hw_read(sc, pe, tid_aux); ri_bits = mvpp2_prs_sram_ri_get(pe); ri_bits &= MVPP2_PRS_RI_VLAN_MASK; if (ri_bits == MVPP2_PRS_RI_VLAN_SINGLE || ri_bits == MVPP2_PRS_RI_VLAN_TRIPLE) break; } if (tid >= tid_aux) { ret = ERANGE; goto error; } memset(pe, 0, sizeof(*pe)); mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); pe->index = tid; sc->sc_prs_double_vlans[ai] = 1; mvpp2_prs_match_etype(pe, 0, tpid1); mvpp2_prs_match_etype(pe, 4, tpid2); mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_VLAN); mvpp2_prs_sram_shift_set(pe, 2 * MVPP2_VLAN_TAG_LEN, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_ri_update(pe, MVPP2_PRS_RI_VLAN_DOUBLE, MVPP2_PRS_RI_VLAN_MASK); mvpp2_prs_sram_ai_update(pe, ai | MVPP2_PRS_DBL_VLAN_AI_BIT, MVPP2_PRS_SRAM_AI_MASK); mvpp2_prs_shadow_set(sc, pe->index, MVPP2_PRS_LU_VLAN); } mvpp2_prs_tcam_port_map_set(pe, port_map); mvpp2_prs_hw_write(sc, pe); error: free(pe, M_TEMP, sizeof(*pe)); return ret; } int mvpp2_prs_ip4_proto(struct mvpp2_softc *sc, uint16_t proto, uint32_t ri, uint32_t ri_mask) { struct mvpp2_prs_entry pe; int tid; if ((proto != IPPROTO_TCP) && (proto != IPPROTO_UDP) && (proto != IPPROTO_IGMP)) return EINVAL; tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = tid; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); mvpp2_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, sizeof(struct ip) - 4, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_sram_ri_update(&pe, ri, ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); mvpp2_prs_tcam_data_byte_set(&pe, 2, 0x00, MVPP2_PRS_TCAM_PROTO_MASK_L); mvpp2_prs_tcam_data_byte_set(&pe, 3, 0x00, MVPP2_PRS_TCAM_PROTO_MASK); mvpp2_prs_tcam_data_byte_set(&pe, 5, proto, MVPP2_PRS_TCAM_PROTO_MASK); mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; pe.index = tid; pe.sram.word[MVPP2_PRS_SRAM_RI_WORD] = 0x0; pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; mvpp2_prs_sram_ri_update(&pe, ri, ri_mask); mvpp2_prs_sram_ri_update(&pe, ri | MVPP2_PRS_RI_IP_FRAG_MASK, ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK); mvpp2_prs_tcam_data_byte_set(&pe, 2, 0x00, 0x0); mvpp2_prs_tcam_data_byte_set(&pe, 3, 0x00, 0x0); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_ip4_cast(struct mvpp2_softc *sc, uint16_t l3_cast) { struct mvpp2_prs_entry pe; int mask, tid; tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = tid; switch (l3_cast) { case MVPP2_PRS_L3_MULTI_CAST: mvpp2_prs_tcam_data_byte_set(&pe, 0, MVPP2_PRS_IPV4_MC, MVPP2_PRS_IPV4_MC_MASK); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_MCAST, MVPP2_PRS_RI_L3_ADDR_MASK); break; case MVPP2_PRS_L3_BROAD_CAST: mask = MVPP2_PRS_IPV4_BC_MASK; mvpp2_prs_tcam_data_byte_set(&pe, 0, mask, mask); mvpp2_prs_tcam_data_byte_set(&pe, 1, mask, mask); mvpp2_prs_tcam_data_byte_set(&pe, 2, mask, mask); mvpp2_prs_tcam_data_byte_set(&pe, 3, mask, mask); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_BCAST, MVPP2_PRS_RI_L3_ADDR_MASK); break; default: return EINVAL; } mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT, MVPP2_PRS_IPV4_DIP_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP4); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_ip6_proto(struct mvpp2_softc *sc, uint16_t proto, uint32_t ri, uint32_t ri_mask) { struct mvpp2_prs_entry pe; int tid; if ((proto != IPPROTO_TCP) && (proto != IPPROTO_UDP) && (proto != IPPROTO_ICMPV6) && (proto != IPPROTO_IPIP)) return EINVAL; tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = tid; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); mvpp2_prs_sram_ri_update(&pe, ri, ri_mask); mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4, sizeof(struct ip6_hdr) - 6, MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); mvpp2_prs_tcam_data_byte_set(&pe, 0, proto, MVPP2_PRS_TCAM_PROTO_MASK); mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP6); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_ip6_cast(struct mvpp2_softc *sc, uint16_t l3_cast) { struct mvpp2_prs_entry pe; int tid; if (l3_cast != MVPP2_PRS_L3_MULTI_CAST) return EINVAL; tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_FIRST_FREE_TID, MVPP2_PE_LAST_FREE_TID); if (tid < 0) return tid; memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = tid; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_MCAST, MVPP2_PRS_RI_L3_ADDR_MASK); mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV6_NO_EXT_AI_BIT, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); mvpp2_prs_sram_shift_set(&pe, -18, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); mvpp2_prs_tcam_data_byte_set(&pe, 0, MVPP2_PRS_IPV6_MC, MVPP2_PRS_IPV6_MC_MASK); mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV6_NO_EXT_AI_BIT); mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_IP6); mvpp2_prs_hw_write(sc, &pe); return 0; } int mvpp2_prs_mac_range_equals(struct mvpp2_prs_entry *pe, const uint8_t *da, uint8_t *mask) { uint8_t tcam_byte, tcam_mask; int index; for (index = 0; index < ETHER_ADDR_LEN; index++) { mvpp2_prs_tcam_data_byte_get(pe, index, &tcam_byte, &tcam_mask); if (tcam_mask != mask[index]) return 0; if ((tcam_mask & tcam_byte) != (da[index] & mask[index])) return 0; } return 1; } int mvpp2_prs_mac_da_range_find(struct mvpp2_softc *sc, int pmap, const uint8_t *da, uint8_t *mask, int udf_type) { struct mvpp2_prs_entry pe; int tid; for (tid = MVPP2_PE_MAC_RANGE_START; tid <= MVPP2_PE_MAC_RANGE_END; tid++) { uint32_t entry_pmap; if (!sc->sc_prs_shadow[tid].valid || (sc->sc_prs_shadow[tid].lu != MVPP2_PRS_LU_MAC) || (sc->sc_prs_shadow[tid].udf != udf_type)) continue; mvpp2_prs_hw_read(sc, &pe, tid); entry_pmap = mvpp2_prs_tcam_port_map_get(&pe); if (mvpp2_prs_mac_range_equals(&pe, da, mask) && entry_pmap == pmap) return tid; } return -1; } int mvpp2_prs_mac_da_accept(struct mvpp2_port *port, const uint8_t *da, int add) { struct mvpp2_softc *sc = port->sc; struct mvpp2_prs_entry pe; uint32_t pmap, len, ri; uint8_t mask[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int tid; memset(&pe, 0, sizeof(pe)); tid = mvpp2_prs_mac_da_range_find(sc, BIT(port->sc_id), da, mask, MVPP2_PRS_UDF_MAC_DEF); if (tid < 0) { if (!add) return 0; tid = mvpp2_prs_tcam_first_free(sc, MVPP2_PE_MAC_RANGE_START, MVPP2_PE_MAC_RANGE_END); if (tid < 0) return tid; pe.index = tid; mvpp2_prs_tcam_port_map_set(&pe, 0); } else { mvpp2_prs_hw_read(sc, &pe, tid); } mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); mvpp2_prs_tcam_port_set(&pe, port->sc_id, add); /* invalidate the entry if no ports are left enabled */ pmap = mvpp2_prs_tcam_port_map_get(&pe); if (pmap == 0) { if (add) return -1; mvpp2_prs_hw_inv(sc, pe.index); sc->sc_prs_shadow[pe.index].valid = 0; return 0; } mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); len = ETHER_ADDR_LEN; while (len--) mvpp2_prs_tcam_data_byte_set(&pe, len, da[len], 0xff); if (ETHER_IS_BROADCAST(da)) ri = MVPP2_PRS_RI_L2_BCAST; else if (ETHER_IS_MULTICAST(da)) ri = MVPP2_PRS_RI_L2_MCAST; else ri = MVPP2_PRS_RI_L2_UCAST | MVPP2_PRS_RI_MAC_ME_MASK; mvpp2_prs_sram_ri_update(&pe, ri, MVPP2_PRS_RI_L2_CAST_MASK | MVPP2_PRS_RI_MAC_ME_MASK); mvpp2_prs_shadow_ri_set(sc, pe.index, ri, MVPP2_PRS_RI_L2_CAST_MASK | MVPP2_PRS_RI_MAC_ME_MASK); mvpp2_prs_sram_shift_set(&pe, 2 * ETHER_ADDR_LEN, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); sc->sc_prs_shadow[pe.index].udf = MVPP2_PRS_UDF_MAC_DEF; mvpp2_prs_shadow_set(sc, pe.index, MVPP2_PRS_LU_MAC); mvpp2_prs_hw_write(sc, &pe); return 0; } void mvpp2_prs_mac_del_all(struct mvpp2_port *port) { struct mvpp2_softc *sc = port->sc; struct mvpp2_prs_entry pe; uint32_t pmap; int index, tid; for (tid = MVPP2_PE_MAC_RANGE_START; tid <= MVPP2_PE_MAC_RANGE_END; tid++) { uint8_t da[ETHER_ADDR_LEN], da_mask[ETHER_ADDR_LEN]; if (!sc->sc_prs_shadow[tid].valid || (sc->sc_prs_shadow[tid].lu != MVPP2_PRS_LU_MAC) || (sc->sc_prs_shadow[tid].udf != MVPP2_PRS_UDF_MAC_DEF)) continue; mvpp2_prs_hw_read(sc, &pe, tid); pmap = mvpp2_prs_tcam_port_map_get(&pe); if (!(pmap & (1 << port->sc_id))) continue; for (index = 0; index < ETHER_ADDR_LEN; index++) mvpp2_prs_tcam_data_byte_get(&pe, index, &da[index], &da_mask[index]); if (ETHER_IS_BROADCAST(da) || ETHER_IS_EQ(da, port->sc_lladdr)) continue; mvpp2_prs_mac_da_accept(port, da, 0); } } int mvpp2_prs_tag_mode_set(struct mvpp2_softc *sc, int port_id, int type) { switch (type) { case MVPP2_TAG_TYPE_EDSA: mvpp2_prs_dsa_tag_set(sc, port_id, 1, MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_set(sc, port_id, 1, MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); break; case MVPP2_TAG_TYPE_DSA: mvpp2_prs_dsa_tag_set(sc, port_id, 1, MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_set(sc, port_id, 1, MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); break; case MVPP2_TAG_TYPE_MH: case MVPP2_TAG_TYPE_NONE: mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_UNTAGGED, MVPP2_PRS_DSA); mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_TAGGED, MVPP2_PRS_EDSA); mvpp2_prs_dsa_tag_set(sc, port_id, 0, MVPP2_PRS_UNTAGGED, MVPP2_PRS_EDSA); break; default: if ((type < 0) || (type > MVPP2_TAG_TYPE_EDSA)) return EINVAL; break; } return 0; } int mvpp2_prs_def_flow(struct mvpp2_port *port) { struct mvpp2_prs_entry pe; int tid; memset(&pe, 0, sizeof(pe)); tid = mvpp2_prs_flow_find(port->sc, port->sc_id); if (tid < 0) { tid = mvpp2_prs_tcam_first_free(port->sc, MVPP2_PE_LAST_FREE_TID, MVPP2_PE_FIRST_FREE_TID); if (tid < 0) return tid; pe.index = tid; mvpp2_prs_sram_ai_update(&pe, port->sc_id, MVPP2_PRS_FLOW_ID_MASK); mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); mvpp2_prs_shadow_set(port->sc, pe.index, MVPP2_PRS_LU_FLOWS); } else { mvpp2_prs_hw_read(port->sc, &pe, tid); } mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS); mvpp2_prs_tcam_port_map_set(&pe, (1 << port->sc_id)); mvpp2_prs_hw_write(port->sc, &pe); return 0; } void mvpp2_cls_flow_write(struct mvpp2_softc *sc, struct mvpp2_cls_flow_entry *fe) { mvpp2_write(sc, MVPP2_CLS_FLOW_INDEX_REG, fe->index); mvpp2_write(sc, MVPP2_CLS_FLOW_TBL0_REG, fe->data[0]); mvpp2_write(sc, MVPP2_CLS_FLOW_TBL1_REG, fe->data[1]); mvpp2_write(sc, MVPP2_CLS_FLOW_TBL2_REG, fe->data[2]); } void mvpp2_cls_lookup_write(struct mvpp2_softc *sc, struct mvpp2_cls_lookup_entry *le) { uint32_t val; val = (le->way << MVPP2_CLS_LKP_INDEX_WAY_OFFS) | le->lkpid; mvpp2_write(sc, MVPP2_CLS_LKP_INDEX_REG, val); mvpp2_write(sc, MVPP2_CLS_LKP_TBL_REG, le->data); } void mvpp2_cls_init(struct mvpp2_softc *sc) { struct mvpp2_cls_lookup_entry le; struct mvpp2_cls_flow_entry fe; int index; mvpp2_write(sc, MVPP2_CLS_MODE_REG, MVPP2_CLS_MODE_ACTIVE_MASK); memset(&fe.data, 0, sizeof(fe.data)); for (index = 0; index < MVPP2_CLS_FLOWS_TBL_SIZE; index++) { fe.index = index; mvpp2_cls_flow_write(sc, &fe); } le.data = 0; for (index = 0; index < MVPP2_CLS_LKP_TBL_SIZE; index++) { le.lkpid = index; le.way = 0; mvpp2_cls_lookup_write(sc, &le); le.way = 1; mvpp2_cls_lookup_write(sc, &le); } } void mvpp2_cls_port_config(struct mvpp2_port *port) { struct mvpp2_cls_lookup_entry le; uint32_t val; /* set way for the port */ val = mvpp2_read(port->sc, MVPP2_CLS_PORT_WAY_REG); val &= ~MVPP2_CLS_PORT_WAY_MASK(port->sc_id); mvpp2_write(port->sc, MVPP2_CLS_PORT_WAY_REG, val); /* * pick the entry to be accessed in lookup ID decoding table * according to the way and lkpid. */ le.lkpid = port->sc_id; le.way = 0; le.data = 0; /* set initial CPU queue for receiving packets */ le.data &= ~MVPP2_CLS_LKP_TBL_RXQ_MASK; le.data |= (port->sc_id * 32); /* disable classification engines */ le.data &= ~MVPP2_CLS_LKP_TBL_LOOKUP_EN_MASK; /* update lookup ID table entry */ mvpp2_cls_lookup_write(port->sc, &le); } void mvpp2_cls_oversize_rxq_set(struct mvpp2_port *port) { uint32_t val; mvpp2_write(port->sc, MVPP2_CLS_OVERSIZE_RXQ_LOW_REG(port->sc_id), (port->sc_id * 32) & MVPP2_CLS_OVERSIZE_RXQ_LOW_MASK); mvpp2_write(port->sc, MVPP2_CLS_SWFWD_P2HQ_REG(port->sc_id), (port->sc_id * 32) >> MVPP2_CLS_OVERSIZE_RXQ_LOW_BITS); val = mvpp2_read(port->sc, MVPP2_CLS_SWFWD_PCTRL_REG); val &= ~MVPP2_CLS_SWFWD_PCTRL_MASK(port->sc_id); mvpp2_write(port->sc, MVPP2_CLS_SWFWD_PCTRL_REG, val); }