#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/timeout.h>
#include <uvm/uvm_extern.h>
#include <machine/cpufunc.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ppp_defs.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_gpio.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/ofw_regulator.h>
#include <dev/ofw/fdt.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#if NBPFILTER > 0
#include <net/bpf.h>
#endif
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <dev/fdt/if_mvppreg.h>
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);
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;
}
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;
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);
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);
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);
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);
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);
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);
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;
return OF_is_enabled(maa->ma_node);
}
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);
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 );
mvpp2_write(sc->sc, MVPP2_ISR_RXQ_SUB_GROUP_CONFIG_REG,
sc->sc_nrxq << MVPP2_ISR_RXQ_SUB_GROUP_CONFIG_SIZE_SHIFT |
0 );
mvpp2_ingress_disable(sc);
mvpp2_defaults_set(sc);
mvpp2_cls_oversize_rxq_set(sc);
mvpp2_cls_port_config(sc);
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;
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;
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);
}
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]);
if (LIST_FIRST(&sc->sc_mii.mii_phys))
mii_mediachg(&sc->sc_mii);
mvpp2_iff(sc);
ifp->if_flags |= IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
mvpp2_txp_max_tx_size_set(sc);
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->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));
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);
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;
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;
}
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;
if (sc->sc_phy_mode == PHY_MODE_SGMII) {
panc |= MVPP2_GMAC_AN_SPEED_EN;
panc |= MVPP2_GMAC_AN_DUPLEX_EN;
} else {
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
#define COMPHY_SPEED_2_5G 1
#define COMPHY_SPEED_3_125G 2
#define COMPHY_SPEED_5G 3
#define COMPHY_SPEED_5_15625G 4
#define COMPHY_SPEED_6G 5
#define COMPHY_SPEED_10_3125G 6
#define COMPHY_UNIT(x) ((x) << 8)
#define COMPHY_MODE(x) ((x) << 12)
#define COMPHY_MODE_SATA 1
#define COMPHY_MODE_SGMII 2
#define COMPHY_MODE_HS_SGMII 3
#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);
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);
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;
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;
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;
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);
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));
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;
mtu = 3 * mtu;
mvpp2_write(port->sc, MVPP2_TXP_SCHED_PORT_INDEX_REG,
mvpp2_egress_port(port));
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);
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;
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);
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;
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);
le.lkpid = port->sc_id;
le.way = 0;
le.data = 0;
le.data &= ~MVPP2_CLS_LKP_TBL_RXQ_MASK;
le.data |= (port->sc_id * 32);
le.data &= ~MVPP2_CLS_LKP_TBL_LOOKUP_EN_MASK;
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);
}