diff options
Diffstat (limited to 'drivers/net/ethernet/microchip')
57 files changed, 21619 insertions, 186 deletions
diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index ed7a35c3ceac..24c994baad13 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -57,5 +57,6 @@ config LAN743X source "drivers/net/ethernet/microchip/lan966x/Kconfig" source "drivers/net/ethernet/microchip/sparx5/Kconfig" +source "drivers/net/ethernet/microchip/vcap/Kconfig" endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index 9faa41436198..bbd349264e6f 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -11,3 +11,4 @@ lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o obj-$(CONFIG_LAN966X_SWITCH) += lan966x/ obj-$(CONFIG_SPARX5_SWITCH) += sparx5/ +obj-$(CONFIG_VCAP) += vcap/ diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.c b/drivers/net/ethernet/microchip/lan743x_ethtool.c index c739d60ee17d..2db5949b4c7e 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.c +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.c @@ -1190,14 +1190,12 @@ static int lan743x_ethtool_set_wol(struct net_device *netdev, } #endif /* CONFIG_PM */ -static void lan743x_common_regs(struct net_device *dev, - struct ethtool_regs *regs, void *p) - +static void lan743x_common_regs(struct net_device *dev, void *p) { struct lan743x_adapter *adapter = netdev_priv(dev); u32 *rb = p; - memset(p, 0, (MAX_LAN743X_ETH_REGS * sizeof(u32))); + memset(p, 0, (MAX_LAN743X_ETH_COMMON_REGS * sizeof(u32))); rb[ETH_PRIV_FLAGS] = adapter->flags; rb[ETH_ID_REV] = lan743x_csr_read(adapter, ID_REV); @@ -1220,17 +1218,164 @@ static void lan743x_common_regs(struct net_device *dev, rb[ETH_WK_SRC] = lan743x_csr_read(adapter, MAC_WK_SRC); } +static void lan743x_sgmii_regs(struct net_device *dev, void *p) +{ + struct lan743x_adapter *adp = netdev_priv(dev); + u32 *rb = p; + u16 idx; + int val; + struct { + u8 id; + u8 dev; + u16 addr; + } regs[] = { + { ETH_SR_VSMMD_DEV_ID1, MDIO_MMD_VEND1, 0x0002}, + { ETH_SR_VSMMD_DEV_ID2, MDIO_MMD_VEND1, 0x0003}, + { ETH_SR_VSMMD_PCS_ID1, MDIO_MMD_VEND1, 0x0004}, + { ETH_SR_VSMMD_PCS_ID2, MDIO_MMD_VEND1, 0x0005}, + { ETH_SR_VSMMD_STS, MDIO_MMD_VEND1, 0x0008}, + { ETH_SR_VSMMD_CTRL, MDIO_MMD_VEND1, 0x0009}, + { ETH_SR_MII_CTRL, MDIO_MMD_VEND2, 0x0000}, + { ETH_SR_MII_STS, MDIO_MMD_VEND2, 0x0001}, + { ETH_SR_MII_DEV_ID1, MDIO_MMD_VEND2, 0x0002}, + { ETH_SR_MII_DEV_ID2, MDIO_MMD_VEND2, 0x0003}, + { ETH_SR_MII_AN_ADV, MDIO_MMD_VEND2, 0x0004}, + { ETH_SR_MII_LP_BABL, MDIO_MMD_VEND2, 0x0005}, + { ETH_SR_MII_EXPN, MDIO_MMD_VEND2, 0x0006}, + { ETH_SR_MII_EXT_STS, MDIO_MMD_VEND2, 0x000F}, + { ETH_SR_MII_TIME_SYNC_ABL, MDIO_MMD_VEND2, 0x0708}, + { ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_LWR, MDIO_MMD_VEND2, 0x0709}, + { ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_UPR, MDIO_MMD_VEND2, 0x070A}, + { ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_LWR, MDIO_MMD_VEND2, 0x070B}, + { ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_UPR, MDIO_MMD_VEND2, 0x070C}, + { ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_LWR, MDIO_MMD_VEND2, 0x070D}, + { ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_UPR, MDIO_MMD_VEND2, 0x070E}, + { ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_LWR, MDIO_MMD_VEND2, 0x070F}, + { ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_UPR, MDIO_MMD_VEND2, 0x0710}, + { ETH_VR_MII_DIG_CTRL1, MDIO_MMD_VEND2, 0x8000}, + { ETH_VR_MII_AN_CTRL, MDIO_MMD_VEND2, 0x8001}, + { ETH_VR_MII_AN_INTR_STS, MDIO_MMD_VEND2, 0x8002}, + { ETH_VR_MII_TC, MDIO_MMD_VEND2, 0x8003}, + { ETH_VR_MII_DBG_CTRL, MDIO_MMD_VEND2, 0x8005}, + { ETH_VR_MII_EEE_MCTRL0, MDIO_MMD_VEND2, 0x8006}, + { ETH_VR_MII_EEE_TXTIMER, MDIO_MMD_VEND2, 0x8008}, + { ETH_VR_MII_EEE_RXTIMER, MDIO_MMD_VEND2, 0x8009}, + { ETH_VR_MII_LINK_TIMER_CTRL, MDIO_MMD_VEND2, 0x800A}, + { ETH_VR_MII_EEE_MCTRL1, MDIO_MMD_VEND2, 0x800B}, + { ETH_VR_MII_DIG_STS, MDIO_MMD_VEND2, 0x8010}, + { ETH_VR_MII_ICG_ERRCNT1, MDIO_MMD_VEND2, 0x8011}, + { ETH_VR_MII_GPIO, MDIO_MMD_VEND2, 0x8015}, + { ETH_VR_MII_EEE_LPI_STATUS, MDIO_MMD_VEND2, 0x8016}, + { ETH_VR_MII_EEE_WKERR, MDIO_MMD_VEND2, 0x8017}, + { ETH_VR_MII_MISC_STS, MDIO_MMD_VEND2, 0x8018}, + { ETH_VR_MII_RX_LSTS, MDIO_MMD_VEND2, 0x8020}, + { ETH_VR_MII_GEN2_GEN4_TX_BSTCTRL0, MDIO_MMD_VEND2, 0x8038}, + { ETH_VR_MII_GEN2_GEN4_TX_LVLCTRL0, MDIO_MMD_VEND2, 0x803A}, + { ETH_VR_MII_GEN2_GEN4_TXGENCTRL0, MDIO_MMD_VEND2, 0x803C}, + { ETH_VR_MII_GEN2_GEN4_TXGENCTRL1, MDIO_MMD_VEND2, 0x803D}, + { ETH_VR_MII_GEN4_TXGENCTRL2, MDIO_MMD_VEND2, 0x803E}, + { ETH_VR_MII_GEN2_GEN4_TX_STS, MDIO_MMD_VEND2, 0x8048}, + { ETH_VR_MII_GEN2_GEN4_RXGENCTRL0, MDIO_MMD_VEND2, 0x8058}, + { ETH_VR_MII_GEN2_GEN4_RXGENCTRL1, MDIO_MMD_VEND2, 0x8059}, + { ETH_VR_MII_GEN4_RXEQ_CTRL, MDIO_MMD_VEND2, 0x805B}, + { ETH_VR_MII_GEN4_RXLOS_CTRL0, MDIO_MMD_VEND2, 0x805D}, + { ETH_VR_MII_GEN2_GEN4_MPLL_CTRL0, MDIO_MMD_VEND2, 0x8078}, + { ETH_VR_MII_GEN2_GEN4_MPLL_CTRL1, MDIO_MMD_VEND2, 0x8079}, + { ETH_VR_MII_GEN2_GEN4_MPLL_STS, MDIO_MMD_VEND2, 0x8088}, + { ETH_VR_MII_GEN2_GEN4_LVL_CTRL, MDIO_MMD_VEND2, 0x8090}, + { ETH_VR_MII_GEN4_MISC_CTRL2, MDIO_MMD_VEND2, 0x8093}, + { ETH_VR_MII_GEN2_GEN4_MISC_CTRL0, MDIO_MMD_VEND2, 0x8099}, + { ETH_VR_MII_GEN2_GEN4_MISC_CTRL1, MDIO_MMD_VEND2, 0x809A}, + { ETH_VR_MII_SNPS_CR_CTRL, MDIO_MMD_VEND2, 0x80A0}, + { ETH_VR_MII_SNPS_CR_ADDR, MDIO_MMD_VEND2, 0x80A1}, + { ETH_VR_MII_SNPS_CR_DATA, MDIO_MMD_VEND2, 0x80A2}, + { ETH_VR_MII_DIG_CTRL2, MDIO_MMD_VEND2, 0x80E1}, + { ETH_VR_MII_DIG_ERRCNT, MDIO_MMD_VEND2, 0x80E2}, + }; + + for (idx = 0; idx < ARRAY_SIZE(regs); idx++) { + val = lan743x_sgmii_read(adp, regs[idx].dev, regs[idx].addr); + if (val < 0) + rb[regs[idx].id] = 0xFFFF; + else + rb[regs[idx].id] = val; + } +} + static int lan743x_get_regs_len(struct net_device *dev) { - return MAX_LAN743X_ETH_REGS * sizeof(u32); + struct lan743x_adapter *adapter = netdev_priv(dev); + u32 num_regs = MAX_LAN743X_ETH_COMMON_REGS; + + if (adapter->is_sgmii_en) + num_regs += MAX_LAN743X_ETH_SGMII_REGS; + + return num_regs * sizeof(u32); } static void lan743x_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { + struct lan743x_adapter *adapter = netdev_priv(dev); + int regs_len; + + regs_len = lan743x_get_regs_len(dev); + memset(p, 0, regs_len); + regs->version = LAN743X_ETH_REG_VERSION; + regs->len = regs_len; + + lan743x_common_regs(dev, p); + p = (u32 *)p + MAX_LAN743X_ETH_COMMON_REGS; - lan743x_common_regs(dev, regs, p); + if (adapter->is_sgmii_en) { + lan743x_sgmii_regs(dev, p); + p = (u32 *)p + MAX_LAN743X_ETH_SGMII_REGS; + } +} + +static void lan743x_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + struct lan743x_adapter *adapter = netdev_priv(dev); + struct lan743x_phy *phy = &adapter->phy; + + if (phy->fc_request_control & FLOW_CTRL_TX) + pause->tx_pause = 1; + if (phy->fc_request_control & FLOW_CTRL_RX) + pause->rx_pause = 1; + pause->autoneg = phy->fc_autoneg; +} + +static int lan743x_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + struct lan743x_adapter *adapter = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; + struct lan743x_phy *phy = &adapter->phy; + + if (!phydev) + return -ENODEV; + + if (!phy_validate_pause(phydev, pause)) + return -EINVAL; + + phy->fc_request_control = 0; + if (pause->rx_pause) + phy->fc_request_control |= FLOW_CTRL_RX; + + if (pause->tx_pause) + phy->fc_request_control |= FLOW_CTRL_TX; + + phy->fc_autoneg = pause->autoneg; + + if (pause->autoneg == AUTONEG_DISABLE) + lan743x_mac_flow_ctrl_set_enables(adapter, pause->tx_pause, + pause->rx_pause); + else + phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_pause); + + return 0; } const struct ethtool_ops lan743x_ethtool_ops = { @@ -1259,6 +1404,8 @@ const struct ethtool_ops lan743x_ethtool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, .get_regs_len = lan743x_get_regs_len, .get_regs = lan743x_get_regs, + .get_pauseparam = lan743x_get_pauseparam, + .set_pauseparam = lan743x_set_pauseparam, #ifdef CONFIG_PM .get_wol = lan743x_ethtool_get_wol, .set_wol = lan743x_ethtool_set_wol, diff --git a/drivers/net/ethernet/microchip/lan743x_ethtool.h b/drivers/net/ethernet/microchip/lan743x_ethtool.h index 7f5996a52488..267d5035b8ad 100644 --- a/drivers/net/ethernet/microchip/lan743x_ethtool.h +++ b/drivers/net/ethernet/microchip/lan743x_ethtool.h @@ -29,7 +29,76 @@ enum { ETH_WK_SRC, /* Add new registers above */ - MAX_LAN743X_ETH_REGS + MAX_LAN743X_ETH_COMMON_REGS +}; + +enum { + /* SGMII Register */ + ETH_SR_VSMMD_DEV_ID1, + ETH_SR_VSMMD_DEV_ID2, + ETH_SR_VSMMD_PCS_ID1, + ETH_SR_VSMMD_PCS_ID2, + ETH_SR_VSMMD_STS, + ETH_SR_VSMMD_CTRL, + ETH_SR_MII_CTRL, + ETH_SR_MII_STS, + ETH_SR_MII_DEV_ID1, + ETH_SR_MII_DEV_ID2, + ETH_SR_MII_AN_ADV, + ETH_SR_MII_LP_BABL, + ETH_SR_MII_EXPN, + ETH_SR_MII_EXT_STS, + ETH_SR_MII_TIME_SYNC_ABL, + ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_LWR, + ETH_SR_MII_TIME_SYNC_TX_MAX_DLY_UPR, + ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_LWR, + ETH_SR_MII_TIME_SYNC_TX_MIN_DLY_UPR, + ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_LWR, + ETH_SR_MII_TIME_SYNC_RX_MAX_DLY_UPR, + ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_LWR, + ETH_SR_MII_TIME_SYNC_RX_MIN_DLY_UPR, + ETH_VR_MII_DIG_CTRL1, + ETH_VR_MII_AN_CTRL, + ETH_VR_MII_AN_INTR_STS, + ETH_VR_MII_TC, + ETH_VR_MII_DBG_CTRL, + ETH_VR_MII_EEE_MCTRL0, + ETH_VR_MII_EEE_TXTIMER, + ETH_VR_MII_EEE_RXTIMER, + ETH_VR_MII_LINK_TIMER_CTRL, + ETH_VR_MII_EEE_MCTRL1, + ETH_VR_MII_DIG_STS, + ETH_VR_MII_ICG_ERRCNT1, + ETH_VR_MII_GPIO, + ETH_VR_MII_EEE_LPI_STATUS, + ETH_VR_MII_EEE_WKERR, + ETH_VR_MII_MISC_STS, + ETH_VR_MII_RX_LSTS, + ETH_VR_MII_GEN2_GEN4_TX_BSTCTRL0, + ETH_VR_MII_GEN2_GEN4_TX_LVLCTRL0, + ETH_VR_MII_GEN2_GEN4_TXGENCTRL0, + ETH_VR_MII_GEN2_GEN4_TXGENCTRL1, + ETH_VR_MII_GEN4_TXGENCTRL2, + ETH_VR_MII_GEN2_GEN4_TX_STS, + ETH_VR_MII_GEN2_GEN4_RXGENCTRL0, + ETH_VR_MII_GEN2_GEN4_RXGENCTRL1, + ETH_VR_MII_GEN4_RXEQ_CTRL, + ETH_VR_MII_GEN4_RXLOS_CTRL0, + ETH_VR_MII_GEN2_GEN4_MPLL_CTRL0, + ETH_VR_MII_GEN2_GEN4_MPLL_CTRL1, + ETH_VR_MII_GEN2_GEN4_MPLL_STS, + ETH_VR_MII_GEN2_GEN4_LVL_CTRL, + ETH_VR_MII_GEN4_MISC_CTRL2, + ETH_VR_MII_GEN2_GEN4_MISC_CTRL0, + ETH_VR_MII_GEN2_GEN4_MISC_CTRL1, + ETH_VR_MII_SNPS_CR_CTRL, + ETH_VR_MII_SNPS_CR_ADDR, + ETH_VR_MII_SNPS_CR_DATA, + ETH_VR_MII_DIG_CTRL2, + ETH_VR_MII_DIG_ERRCNT, + + /* Add new registers above */ + MAX_LAN743X_ETH_SGMII_REGS }; extern const struct ethtool_ops lan743x_ethtool_ops; diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index 50eeecba1f18..534840f9a7ca 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -939,7 +939,7 @@ static int lan743x_sgmii_wait_till_not_busy(struct lan743x_adapter *adapter) return ret; } -static int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr) +int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr) { u32 mmd_access; int ret; @@ -1326,8 +1326,8 @@ static void lan743x_mac_close(struct lan743x_adapter *adapter) 1, 1000, 20000, 100); } -static void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, - bool tx_enable, bool rx_enable) +void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, + bool tx_enable, bool rx_enable) { u32 flow_setting = 0; diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h index 67877d3b6dd9..8438c3dbcf36 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.h +++ b/drivers/net/ethernet/microchip/lan743x_main.h @@ -1159,5 +1159,8 @@ u32 lan743x_csr_read(struct lan743x_adapter *adapter, int offset); void lan743x_csr_write(struct lan743x_adapter *adapter, int offset, u32 data); int lan743x_hs_syslock_acquire(struct lan743x_adapter *adapter, u16 timeout); void lan743x_hs_syslock_release(struct lan743x_adapter *adapter); +void lan743x_mac_flow_ctrl_set_enables(struct lan743x_adapter *adapter, + bool tx_enable, bool rx_enable); +int lan743x_sgmii_read(struct lan743x_adapter *adapter, u8 mmd, u16 addr); #endif /* _LAN743X_H */ diff --git a/drivers/net/ethernet/microchip/lan743x_ptp.c b/drivers/net/ethernet/microchip/lan743x_ptp.c index da3ea905adbb..39e1066ecd5f 100644 --- a/drivers/net/ethernet/microchip/lan743x_ptp.c +++ b/drivers/net/ethernet/microchip/lan743x_ptp.c @@ -339,59 +339,18 @@ static int lan743x_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm) struct lan743x_adapter *adapter = container_of(ptp, struct lan743x_adapter, ptp); u32 lan743x_rate_adj = 0; - bool positive = true; - u64 u64_delta = 0; + u64 u64_delta; if ((scaled_ppm < (-LAN743X_PTP_MAX_FINE_ADJ_IN_SCALED_PPM)) || scaled_ppm > LAN743X_PTP_MAX_FINE_ADJ_IN_SCALED_PPM) { return -EINVAL; } - if (scaled_ppm > 0) { - u64_delta = (u64)scaled_ppm; - positive = true; - } else { - u64_delta = (u64)(-scaled_ppm); - positive = false; - } - u64_delta = (u64_delta << 19); - lan743x_rate_adj = div_u64(u64_delta, 1000000); - - if (positive) - lan743x_rate_adj |= PTP_CLOCK_RATE_ADJ_DIR_; - - lan743x_csr_write(adapter, PTP_CLOCK_RATE_ADJ, - lan743x_rate_adj); - - return 0; -} - -static int lan743x_ptpci_adjfreq(struct ptp_clock_info *ptpci, s32 delta_ppb) -{ - struct lan743x_ptp *ptp = - container_of(ptpci, struct lan743x_ptp, ptp_clock_info); - struct lan743x_adapter *adapter = - container_of(ptp, struct lan743x_adapter, ptp); - u32 lan743x_rate_adj = 0; - bool positive = true; - u32 u32_delta = 0; - u64 u64_delta = 0; - if ((delta_ppb < (-LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB)) || - delta_ppb > LAN743X_PTP_MAX_FREQ_ADJ_IN_PPB) { - return -EINVAL; - } - if (delta_ppb > 0) { - u32_delta = (u32)delta_ppb; - positive = true; - } else { - u32_delta = (u32)(-delta_ppb); - positive = false; - } - u64_delta = (((u64)u32_delta) << 35); - lan743x_rate_adj = div_u64(u64_delta, 1000000000); - - if (positive) - lan743x_rate_adj |= PTP_CLOCK_RATE_ADJ_DIR_; + /* diff_by_scaled_ppm returns true if the difference is negative */ + if (diff_by_scaled_ppm(1ULL << 35, scaled_ppm, &u64_delta)) + lan743x_rate_adj = (u32)u64_delta; + else + lan743x_rate_adj = (u32)u64_delta | PTP_CLOCK_RATE_ADJ_DIR_; lan743x_csr_write(adapter, PTP_CLOCK_RATE_ADJ, lan743x_rate_adj); @@ -1583,7 +1542,6 @@ int lan743x_ptp_open(struct lan743x_adapter *adapter) ptp->ptp_clock_info.pps = LAN743X_PTP_N_PPS; ptp->ptp_clock_info.pin_config = ptp->pin_config; ptp->ptp_clock_info.adjfine = lan743x_ptpci_adjfine; - ptp->ptp_clock_info.adjfreq = lan743x_ptpci_adjfreq; ptp->ptp_clock_info.adjtime = lan743x_ptpci_adjtime; ptp->ptp_clock_info.gettime64 = lan743x_ptpci_gettime64; ptp->ptp_clock_info.getcrosststamp = NULL; diff --git a/drivers/net/ethernet/microchip/lan966x/Kconfig b/drivers/net/ethernet/microchip/lan966x/Kconfig index 49e1464a4313..8bcd60f17d6d 100644 --- a/drivers/net/ethernet/microchip/lan966x/Kconfig +++ b/drivers/net/ethernet/microchip/lan966x/Kconfig @@ -7,5 +7,7 @@ config LAN966X_SWITCH depends on BRIDGE || BRIDGE=n select PHYLINK select PACKING + select PAGE_POOL + select VCAP help This driver supports the Lan966x network switch device. diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 962f7c5f9e7d..56afd694f3c7 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -11,4 +11,9 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_ptp.o lan966x_fdma.o lan966x_lag.o \ lan966x_tc.o lan966x_mqprio.o lan966x_taprio.o \ lan966x_tbf.o lan966x_cbs.o lan966x_ets.o \ - lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o + lan966x_tc_matchall.o lan966x_police.o lan966x_mirror.o \ + lan966x_xdp.o lan966x_vcap_impl.o lan966x_vcap_ag_api.o \ + lan966x_tc_flower.o lan966x_goto.o + +# Provide include files +ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index e6948939ccc2..5314c064ceae 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ +#include <linux/bpf.h> +#include <linux/filter.h> + #include "lan966x_main.h" static int lan966x_fdma_channel_active(struct lan966x *lan966x) @@ -10,50 +13,39 @@ static int lan966x_fdma_channel_active(struct lan966x *lan966x) static struct page *lan966x_fdma_rx_alloc_page(struct lan966x_rx *rx, struct lan966x_db *db) { - struct lan966x *lan966x = rx->lan966x; - dma_addr_t dma_addr; struct page *page; - page = dev_alloc_pages(rx->page_order); + page = page_pool_dev_alloc_pages(rx->page_pool); if (unlikely(!page)) return NULL; - dma_addr = dma_map_page(lan966x->dev, page, 0, - PAGE_SIZE << rx->page_order, - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(lan966x->dev, dma_addr))) - goto free_page; - - db->dataptr = dma_addr; + db->dataptr = page_pool_get_dma_addr(page) + XDP_PACKET_HEADROOM; return page; - -free_page: - __free_pages(page, rx->page_order); - return NULL; } static void lan966x_fdma_rx_free_pages(struct lan966x_rx *rx) { - struct lan966x *lan966x = rx->lan966x; - struct lan966x_rx_dcb *dcb; - struct lan966x_db *db; int i, j; for (i = 0; i < FDMA_DCB_MAX; ++i) { - dcb = &rx->dcbs[i]; - - for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j) { - db = &dcb->db[j]; - dma_unmap_single(lan966x->dev, - (dma_addr_t)db->dataptr, - PAGE_SIZE << rx->page_order, - DMA_FROM_DEVICE); - __free_pages(rx->page[i][j], rx->page_order); - } + for (j = 0; j < FDMA_RX_DCB_MAX_DBS; ++j) + page_pool_put_full_page(rx->page_pool, + rx->page[i][j], false); } } +static void lan966x_fdma_rx_free_page(struct lan966x_rx *rx) +{ + struct page *page; + + page = rx->page[rx->dcb_index][rx->db_index]; + if (unlikely(!page)) + return; + + page_pool_recycle_direct(rx->page_pool, page); +} + static void lan966x_fdma_rx_add_dcb(struct lan966x_rx *rx, struct lan966x_rx_dcb *dcb, u64 nextptr) @@ -73,6 +65,41 @@ static void lan966x_fdma_rx_add_dcb(struct lan966x_rx *rx, rx->last_entry = dcb; } +static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx) +{ + struct lan966x *lan966x = rx->lan966x; + struct page_pool_params pp_params = { + .order = rx->page_order, + .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .pool_size = FDMA_DCB_MAX, + .nid = NUMA_NO_NODE, + .dev = lan966x->dev, + .dma_dir = DMA_FROM_DEVICE, + .offset = XDP_PACKET_HEADROOM, + .max_len = rx->max_mtu - + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), + }; + + if (lan966x_xdp_present(lan966x)) + pp_params.dma_dir = DMA_BIDIRECTIONAL; + + rx->page_pool = page_pool_create(&pp_params); + + for (int i = 0; i < lan966x->num_phys_ports; ++i) { + struct lan966x_port *port; + + if (!lan966x->ports[i]) + continue; + + port = lan966x->ports[i]; + xdp_rxq_info_unreg_mem_model(&port->xdp_rxq); + xdp_rxq_info_reg_mem_model(&port->xdp_rxq, MEM_TYPE_PAGE_POOL, + rx->page_pool); + } + + return PTR_ERR_OR_ZERO(rx->page_pool); +} + static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; @@ -82,6 +109,9 @@ static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) int i, j; int size; + if (lan966x_fdma_rx_alloc_page_pool(rx)) + return PTR_ERR(rx->page_pool); + /* calculate how many pages are needed to allocate the dcbs */ size = sizeof(struct lan966x_rx_dcb) * FDMA_DCB_MAX; size = ALIGN(size, PAGE_SIZE); @@ -116,6 +146,12 @@ static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx) return 0; } +static void lan966x_fdma_rx_advance_dcb(struct lan966x_rx *rx) +{ + rx->dcb_index++; + rx->dcb_index &= FDMA_DCB_MAX - 1; +} + static void lan966x_fdma_rx_free(struct lan966x_rx *rx) { struct lan966x *lan966x = rx->lan966x; @@ -355,11 +391,14 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) { struct lan966x_tx *tx = &lan966x->tx; struct lan966x_tx_dcb_buf *dcb_buf; + struct xdp_frame_bulk bq; struct lan966x_db *db; unsigned long flags; bool clear = false; int i; + xdp_frame_bulk_init(&bq); + spin_lock_irqsave(&lan966x->tx_lock, flags); for (i = 0; i < FDMA_DCB_MAX; ++i) { dcb_buf = &tx->dcbs_buf[i]; @@ -372,19 +411,35 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) continue; dcb_buf->dev->stats.tx_packets++; - dcb_buf->dev->stats.tx_bytes += dcb_buf->skb->len; + dcb_buf->dev->stats.tx_bytes += dcb_buf->len; dcb_buf->used = false; - dma_unmap_single(lan966x->dev, - dcb_buf->dma_addr, - dcb_buf->skb->len, - DMA_TO_DEVICE); - if (!dcb_buf->ptp) - dev_kfree_skb_any(dcb_buf->skb); + if (dcb_buf->use_skb) { + dma_unmap_single(lan966x->dev, + dcb_buf->dma_addr, + dcb_buf->len, + DMA_TO_DEVICE); + + if (!dcb_buf->ptp) + napi_consume_skb(dcb_buf->data.skb, weight); + } else { + if (dcb_buf->xdp_ndo) + dma_unmap_single(lan966x->dev, + dcb_buf->dma_addr, + dcb_buf->len, + DMA_TO_DEVICE); + + if (dcb_buf->xdp_ndo) + xdp_return_frame_bulk(dcb_buf->data.xdpf, &bq); + else + xdp_return_frame_rx_napi(dcb_buf->data.xdpf); + } clear = true; } + xdp_flush_frame_bulk(&bq); + if (clear) lan966x_fdma_wakeup_netdev(lan966x); @@ -403,40 +458,61 @@ static bool lan966x_fdma_rx_more_frames(struct lan966x_rx *rx) return true; } -static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) +static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) { struct lan966x *lan966x = rx->lan966x; - u64 src_port, timestamp; + struct lan966x_port *port; struct lan966x_db *db; - struct sk_buff *skb; struct page *page; - /* Get the received frame and unmap it */ db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; page = rx->page[rx->dcb_index][rx->db_index]; + if (unlikely(!page)) + return FDMA_ERROR; - dma_sync_single_for_cpu(lan966x->dev, (dma_addr_t)db->dataptr, + dma_sync_single_for_cpu(lan966x->dev, + (dma_addr_t)db->dataptr + XDP_PACKET_HEADROOM, FDMA_DCB_STATUS_BLOCKL(db->status), DMA_FROM_DEVICE); + lan966x_ifh_get_src_port(page_address(page) + XDP_PACKET_HEADROOM, + src_port); + if (WARN_ON(*src_port >= lan966x->num_phys_ports)) + return FDMA_ERROR; + + port = lan966x->ports[*src_port]; + if (!lan966x_xdp_port_present(port)) + return FDMA_PASS; + + return lan966x_xdp_run(port, page, FDMA_DCB_STATUS_BLOCKL(db->status)); +} + +static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx, + u64 src_port) +{ + struct lan966x *lan966x = rx->lan966x; + struct lan966x_db *db; + struct sk_buff *skb; + struct page *page; + u64 timestamp; + + /* Get the received frame and unmap it */ + db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; + page = rx->page[rx->dcb_index][rx->db_index]; + skb = build_skb(page_address(page), PAGE_SIZE << rx->page_order); if (unlikely(!skb)) - goto unmap_page; + goto free_page; + + skb_mark_for_recycle(skb); + skb_reserve(skb, XDP_PACKET_HEADROOM); skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status)); - lan966x_ifh_get_src_port(skb->data, &src_port); lan966x_ifh_get_timestamp(skb->data, ×tamp); - if (WARN_ON(src_port >= lan966x->num_phys_ports)) - goto free_skb; - - dma_unmap_single_attrs(lan966x->dev, (dma_addr_t)db->dataptr, - PAGE_SIZE << rx->page_order, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); - skb->dev = lan966x->ports[src_port]->dev; - skb_pull(skb, IFH_LEN * sizeof(u32)); + skb_pull(skb, IFH_LEN_BYTES); if (likely(!(skb->dev->features & NETIF_F_RXFCS))) skb_trim(skb, skb->len - ETH_FCS_LEN); @@ -457,13 +533,8 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) return skb; -free_skb: - kfree_skb(skb); -unmap_page: - dma_unmap_single_attrs(lan966x->dev, (dma_addr_t)db->dataptr, - PAGE_SIZE << rx->page_order, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); - __free_pages(page, rx->page_order); +free_page: + page_pool_recycle_direct(rx->page_pool, page); return NULL; } @@ -475,9 +546,11 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) int dcb_reload = rx->dcb_index; struct lan966x_rx_dcb *old_dcb; struct lan966x_db *db; + bool redirect = false; struct sk_buff *skb; struct page *page; int counter = 0; + u64 src_port; u64 nextptr; lan966x_fdma_tx_clear_buf(lan966x, weight); @@ -487,19 +560,36 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) if (!lan966x_fdma_rx_more_frames(rx)) break; - skb = lan966x_fdma_rx_get_frame(rx); + counter++; - rx->page[rx->dcb_index][rx->db_index] = NULL; - rx->dcb_index++; - rx->dcb_index &= FDMA_DCB_MAX - 1; + switch (lan966x_fdma_rx_check_frame(rx, &src_port)) { + case FDMA_PASS: + break; + case FDMA_ERROR: + lan966x_fdma_rx_free_page(rx); + lan966x_fdma_rx_advance_dcb(rx); + goto allocate_new; + case FDMA_REDIRECT: + redirect = true; + fallthrough; + case FDMA_TX: + lan966x_fdma_rx_advance_dcb(rx); + continue; + case FDMA_DROP: + lan966x_fdma_rx_free_page(rx); + lan966x_fdma_rx_advance_dcb(rx); + continue; + } + skb = lan966x_fdma_rx_get_frame(rx, src_port); + lan966x_fdma_rx_advance_dcb(rx); if (!skb) - break; + goto allocate_new; napi_gro_receive(&lan966x->napi, skb); - counter++; } +allocate_new: /* Allocate new pages and map them */ while (dcb_reload != rx->dcb_index) { db = &rx->dcbs[dcb_reload].db[rx->db_index]; @@ -521,6 +611,9 @@ static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight) if (counter < weight && napi_complete_done(napi, counter)) lan_wr(0xff, lan966x, FDMA_INTR_DB_ENA); + if (redirect) + xdp_do_flush(); + return counter; } @@ -565,14 +658,139 @@ static int lan966x_fdma_get_next_dcb(struct lan966x_tx *tx) return -1; } +static void lan966x_fdma_tx_setup_dcb(struct lan966x_tx *tx, + int next_to_use, int len, + dma_addr_t dma_addr) +{ + struct lan966x_tx_dcb *next_dcb; + struct lan966x_db *next_db; + + next_dcb = &tx->dcbs[next_to_use]; + next_dcb->nextptr = FDMA_DCB_INVALID_DATA; + + next_db = &next_dcb->db[0]; + next_db->dataptr = dma_addr; + next_db->status = FDMA_DCB_STATUS_SOF | + FDMA_DCB_STATUS_EOF | + FDMA_DCB_STATUS_INTR | + FDMA_DCB_STATUS_BLOCKO(0) | + FDMA_DCB_STATUS_BLOCKL(len); +} + +static void lan966x_fdma_tx_start(struct lan966x_tx *tx, int next_to_use) +{ + struct lan966x *lan966x = tx->lan966x; + struct lan966x_tx_dcb *dcb; + + if (likely(lan966x->tx.activated)) { + /* Connect current dcb to the next db */ + dcb = &tx->dcbs[tx->last_in_use]; + dcb->nextptr = tx->dma + (next_to_use * + sizeof(struct lan966x_tx_dcb)); + + lan966x_fdma_tx_reload(tx); + } else { + /* Because it is first time, then just activate */ + lan966x->tx.activated = true; + lan966x_fdma_tx_activate(tx); + } + + /* Move to next dcb because this last in use */ + tx->last_in_use = next_to_use; +} + +int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, + struct xdp_frame *xdpf, + struct page *page, + bool dma_map) +{ + struct lan966x *lan966x = port->lan966x; + struct lan966x_tx_dcb_buf *next_dcb_buf; + struct lan966x_tx *tx = &lan966x->tx; + dma_addr_t dma_addr; + int next_to_use; + __be32 *ifh; + int ret = 0; + + spin_lock(&lan966x->tx_lock); + + /* Get next index */ + next_to_use = lan966x_fdma_get_next_dcb(tx); + if (next_to_use < 0) { + netif_stop_queue(port->dev); + ret = NETDEV_TX_BUSY; + goto out; + } + + /* Generate new IFH */ + if (dma_map) { + if (xdpf->headroom < IFH_LEN_BYTES) { + ret = NETDEV_TX_OK; + goto out; + } + + ifh = xdpf->data - IFH_LEN_BYTES; + memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); + lan966x_ifh_set_bypass(ifh, 1); + lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port)); + + dma_addr = dma_map_single(lan966x->dev, + xdpf->data - IFH_LEN_BYTES, + xdpf->len + IFH_LEN_BYTES, + DMA_TO_DEVICE); + if (dma_mapping_error(lan966x->dev, dma_addr)) { + ret = NETDEV_TX_OK; + goto out; + } + + /* Setup next dcb */ + lan966x_fdma_tx_setup_dcb(tx, next_to_use, + xdpf->len + IFH_LEN_BYTES, + dma_addr); + } else { + ifh = page_address(page) + XDP_PACKET_HEADROOM; + memset(ifh, 0x0, sizeof(__be32) * IFH_LEN); + lan966x_ifh_set_bypass(ifh, 1); + lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port)); + + dma_addr = page_pool_get_dma_addr(page); + dma_sync_single_for_device(lan966x->dev, + dma_addr + XDP_PACKET_HEADROOM, + xdpf->len + IFH_LEN_BYTES, + DMA_TO_DEVICE); + + /* Setup next dcb */ + lan966x_fdma_tx_setup_dcb(tx, next_to_use, + xdpf->len + IFH_LEN_BYTES, + dma_addr + XDP_PACKET_HEADROOM); + } + + /* Fill up the buffer */ + next_dcb_buf = &tx->dcbs_buf[next_to_use]; + next_dcb_buf->use_skb = false; + next_dcb_buf->data.xdpf = xdpf; + next_dcb_buf->xdp_ndo = dma_map; + next_dcb_buf->len = xdpf->len + IFH_LEN_BYTES; + next_dcb_buf->dma_addr = dma_addr; + next_dcb_buf->used = true; + next_dcb_buf->ptp = false; + next_dcb_buf->dev = port->dev; + + /* Start the transmission */ + lan966x_fdma_tx_start(tx, next_to_use); + +out: + spin_unlock(&lan966x->tx_lock); + + return ret; +} + int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) { struct lan966x_port *port = netdev_priv(dev); struct lan966x *lan966x = port->lan966x; struct lan966x_tx_dcb_buf *next_dcb_buf; - struct lan966x_tx_dcb *next_dcb, *dcb; struct lan966x_tx *tx = &lan966x->tx; - struct lan966x_db *next_db; int needed_headroom; int needed_tailroom; dma_addr_t dma_addr; @@ -592,7 +810,7 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) } /* skb processing */ - needed_headroom = max_t(int, IFH_LEN * sizeof(u32) - skb_headroom(skb), 0); + needed_headroom = max_t(int, IFH_LEN_BYTES - skb_headroom(skb), 0); needed_tailroom = max_t(int, ETH_FCS_LEN - skb_tailroom(skb), 0); if (needed_headroom || needed_tailroom || skb_header_cloned(skb)) { err = pskb_expand_head(skb, needed_headroom, needed_tailroom, @@ -605,8 +823,8 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) } skb_tx_timestamp(skb); - skb_push(skb, IFH_LEN * sizeof(u32)); - memcpy(skb->data, ifh, IFH_LEN * sizeof(u32)); + skb_push(skb, IFH_LEN_BYTES); + memcpy(skb->data, ifh, IFH_LEN_BYTES); skb_put(skb, 4); dma_addr = dma_map_single(lan966x->dev, skb->data, skb->len, @@ -618,20 +836,14 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) } /* Setup next dcb */ - next_dcb = &tx->dcbs[next_to_use]; - next_dcb->nextptr = FDMA_DCB_INVALID_DATA; - - next_db = &next_dcb->db[0]; - next_db->dataptr = dma_addr; - next_db->status = FDMA_DCB_STATUS_SOF | - FDMA_DCB_STATUS_EOF | - FDMA_DCB_STATUS_INTR | - FDMA_DCB_STATUS_BLOCKO(0) | - FDMA_DCB_STATUS_BLOCKL(skb->len); + lan966x_fdma_tx_setup_dcb(tx, next_to_use, skb->len, dma_addr); /* Fill up the buffer */ next_dcb_buf = &tx->dcbs_buf[next_to_use]; - next_dcb_buf->skb = skb; + next_dcb_buf->use_skb = true; + next_dcb_buf->data.skb = skb; + next_dcb_buf->xdp_ndo = false; + next_dcb_buf->len = skb->len; next_dcb_buf->dma_addr = dma_addr; next_dcb_buf->used = true; next_dcb_buf->ptp = false; @@ -641,21 +853,8 @@ int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev) LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP) next_dcb_buf->ptp = true; - if (likely(lan966x->tx.activated)) { - /* Connect current dcb to the next db */ - dcb = &tx->dcbs[tx->last_in_use]; - dcb->nextptr = tx->dma + (next_to_use * - sizeof(struct lan966x_tx_dcb)); - - lan966x_fdma_tx_reload(tx); - } else { - /* Because it is first time, then just activate */ - lan966x->tx.activated = true; - lan966x_fdma_tx_activate(tx); - } - - /* Move to next dcb because this last in use */ - tx->last_in_use = next_to_use; + /* Start the transmission */ + lan966x_fdma_tx_start(tx, next_to_use); return NETDEV_TX_OK; @@ -696,6 +895,7 @@ static int lan966x_qsys_sw_status(struct lan966x *lan966x) static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) { + struct page_pool *page_pool; dma_addr_t rx_dma; void *rx_dcbs; u32 size; @@ -704,6 +904,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) /* Store these for later to free them */ rx_dma = lan966x->rx.dma; rx_dcbs = lan966x->rx.dcbs; + page_pool = lan966x->rx.page_pool; napi_synchronize(&lan966x->napi); napi_disable(&lan966x->napi); @@ -712,6 +913,7 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) lan966x_fdma_rx_disable(&lan966x->rx); lan966x_fdma_rx_free_pages(&lan966x->rx); lan966x->rx.page_order = round_up(new_mtu, PAGE_SIZE) / PAGE_SIZE - 1; + lan966x->rx.max_mtu = new_mtu; err = lan966x_fdma_rx_alloc(&lan966x->rx); if (err) goto restore; @@ -721,11 +923,14 @@ static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu) size = ALIGN(size, PAGE_SIZE); dma_free_coherent(lan966x->dev, size, rx_dcbs, rx_dma); + page_pool_destroy(page_pool); + lan966x_fdma_wakeup_netdev(lan966x); napi_enable(&lan966x->napi); return err; restore: + lan966x->rx.page_pool = page_pool; lan966x->rx.dma = rx_dma; lan966x->rx.dcbs = rx_dcbs; lan966x_fdma_rx_start(&lan966x->rx); @@ -733,21 +938,20 @@ restore: return err; } -int lan966x_fdma_change_mtu(struct lan966x *lan966x) +static int lan966x_fdma_get_max_frame(struct lan966x *lan966x) +{ + return lan966x_fdma_get_max_mtu(lan966x) + + IFH_LEN_BYTES + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + VLAN_HLEN * 2 + + XDP_PACKET_HEADROOM; +} + +static int __lan966x_fdma_reload(struct lan966x *lan966x, int max_mtu) { - int max_mtu; int err; u32 val; - max_mtu = lan966x_fdma_get_max_mtu(lan966x); - max_mtu += IFH_LEN * sizeof(u32); - max_mtu += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - max_mtu += VLAN_HLEN * 2; - - if (round_up(max_mtu, PAGE_SIZE) / PAGE_SIZE - 1 == - lan966x->rx.page_order) - return 0; - /* Disable the CPU port */ lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(0), QSYS_SW_PORT_MODE_PORT_ENA, @@ -773,6 +977,25 @@ int lan966x_fdma_change_mtu(struct lan966x *lan966x) return err; } +int lan966x_fdma_change_mtu(struct lan966x *lan966x) +{ + int max_mtu; + + max_mtu = lan966x_fdma_get_max_frame(lan966x); + if (max_mtu == lan966x->rx.max_mtu) + return 0; + + return __lan966x_fdma_reload(lan966x, max_mtu); +} + +int lan966x_fdma_reload_page_pool(struct lan966x *lan966x) +{ + int max_mtu; + + max_mtu = lan966x_fdma_get_max_frame(lan966x); + return __lan966x_fdma_reload(lan966x, max_mtu); +} + void lan966x_fdma_netdev_init(struct lan966x *lan966x, struct net_device *dev) { if (lan966x->fdma_ndev) @@ -800,6 +1023,7 @@ int lan966x_fdma_init(struct lan966x *lan966x) lan966x->rx.lan966x = lan966x; lan966x->rx.channel_id = FDMA_XTR_CHANNEL; + lan966x->rx.max_mtu = lan966x_fdma_get_max_frame(lan966x); lan966x->tx.lan966x = lan966x; lan966x->tx.channel_id = FDMA_INJ_CHANNEL; lan966x->tx.last_in_use = -1; @@ -832,5 +1056,6 @@ void lan966x_fdma_deinit(struct lan966x *lan966x) lan966x_fdma_rx_free_pages(&lan966x->rx); lan966x_fdma_rx_free(&lan966x->rx); + page_pool_destroy(lan966x->rx.page_pool); lan966x_fdma_tx_free(&lan966x->tx); } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_goto.c b/drivers/net/ethernet/microchip/lan966x/lan966x_goto.c new file mode 100644 index 000000000000..bf0cfe24a8fc --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_goto.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" +#include "vcap_api_client.h" + +int lan966x_goto_port_add(struct lan966x_port *port, + struct flow_action_entry *act, + unsigned long goto_id, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + int err; + + err = vcap_enable_lookups(lan966x->vcap_ctrl, port->dev, + act->chain_index, goto_id, + true); + if (err == -EFAULT) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported goto chain"); + return -EOPNOTSUPP; + } + + if (err == -EADDRINUSE) { + NL_SET_ERR_MSG_MOD(extack, "VCAP already enabled"); + return -EOPNOTSUPP; + } + + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Could not enable VCAP lookups"); + return err; + } + + port->tc.goto_id = goto_id; + + return 0; +} + +int lan966x_goto_port_del(struct lan966x_port *port, + unsigned long goto_id, + struct netlink_ext_ack *extack) +{ + struct lan966x *lan966x = port->lan966x; + int err; + + err = vcap_enable_lookups(lan966x->vcap_ctrl, port->dev, 0, + goto_id, false); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Could not disable VCAP lookups"); + return err; + } + + port->tc.goto_id = 0; + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h b/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h index ca3314789d18..f3b1e0d31826 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ifh.h @@ -8,6 +8,7 @@ */ #define IFH_LEN 7 +#define IFH_LEN_BYTES (IFH_LEN * sizeof(u32)) /* Timestamp for frame */ #define IFH_POS_TIMESTAMP 192 diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 20ee5b28f70a..cadde20505ba 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -47,6 +47,9 @@ static const struct lan966x_main_io_resource lan966x_main_iomap[] = { { TARGET_PTP, 0xc000, 1 }, /* 0xe200c000 */ { TARGET_CHIP_TOP, 0x10000, 1 }, /* 0xe2010000 */ { TARGET_REW, 0x14000, 1 }, /* 0xe2014000 */ + { TARGET_VCAP, 0x18000, 1 }, /* 0xe2018000 */ + { TARGET_VCAP + 1, 0x20000, 1 }, /* 0xe2020000 */ + { TARGET_VCAP + 2, 0x24000, 1 }, /* 0xe2024000 */ { TARGET_SYS, 0x28000, 1 }, /* 0xe2028000 */ { TARGET_DEV, 0x34000, 1 }, /* 0xe2034000 */ { TARGET_DEV + 1, 0x38000, 1 }, /* 0xe2038000 */ @@ -302,13 +305,13 @@ err: return NETDEV_TX_BUSY; } -static void lan966x_ifh_set_bypass(void *ifh, u64 bypass) +void lan966x_ifh_set_bypass(void *ifh, u64 bypass) { packing(ifh, &bypass, IFH_POS_BYPASS + IFH_WID_BYPASS - 1, IFH_POS_BYPASS, IFH_LEN * 4, PACK, 0); } -static void lan966x_ifh_set_port(void *ifh, u64 bypass) +void lan966x_ifh_set_port(void *ifh, u64 bypass) { packing(ifh, &bypass, IFH_POS_DSTS + IFH_WID_DSTS - 1, IFH_POS_DSTS, IFH_LEN * 4, PACK, 0); @@ -440,11 +443,22 @@ static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct lan966x_port *port = netdev_priv(dev); + int err; + + if (cmd == SIOCSHWTSTAMP) { + err = lan966x_ptp_setup_traps(port, ifr); + if (err) + return err; + } if (!phy_has_hwtstamp(dev->phydev) && port->lan966x->ptp) { switch (cmd) { case SIOCSHWTSTAMP: - return lan966x_ptp_hwtstamp_set(port, ifr); + err = lan966x_ptp_hwtstamp_set(port, ifr); + if (err) + lan966x_ptp_del_traps(port); + + return err; case SIOCGHWTSTAMP: return lan966x_ptp_hwtstamp_get(port, ifr); } @@ -453,7 +467,11 @@ static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr, if (!dev->phydev) return -ENODEV; - return phy_mii_ioctl(dev->phydev, ifr, cmd); + err = phy_mii_ioctl(dev->phydev, ifr, cmd); + if (err && cmd == SIOCSHWTSTAMP) + lan966x_ptp_del_traps(port); + + return err; } static const struct net_device_ops lan966x_port_netdev_ops = { @@ -468,6 +486,8 @@ static const struct net_device_ops lan966x_port_netdev_ops = { .ndo_get_port_parent_id = lan966x_port_get_parent_id, .ndo_eth_ioctl = lan966x_port_ioctl, .ndo_setup_tc = lan966x_tc_setup, + .ndo_bpf = lan966x_xdp, + .ndo_xdp_xmit = lan966x_xdp_xmit, }; bool lan966x_netdevice_check(const struct net_device *dev) @@ -694,6 +714,7 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) if (port->dev) unregister_netdev(port->dev); + lan966x_xdp_port_deinit(port); if (lan966x->fdma && lan966x->fdma_ndev == port->dev) lan966x_fdma_netdev_deinit(lan966x, port->dev); @@ -760,7 +781,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_TC; dev->hw_features |= NETIF_F_HW_TC; - dev->needed_headroom = IFH_LEN * sizeof(u32); + dev->needed_headroom = IFH_LEN_BYTES; eth_hw_addr_gen(dev, lan966x->base_mac, p + 1); @@ -1136,6 +1157,9 @@ static int lan966x_probe(struct platform_device *pdev) lan966x->ports[p]->serdes = serdes; lan966x_port_init(lan966x->ports[p]); + err = lan966x_xdp_port_init(lan966x->ports[p]); + if (err) + goto cleanup_ports; } lan966x_mdb_init(lan966x); @@ -1151,8 +1175,15 @@ static int lan966x_probe(struct platform_device *pdev) if (err) goto cleanup_ptp; + err = lan966x_vcap_init(lan966x); + if (err) + goto cleanup_fdma; + return 0; +cleanup_fdma: + lan966x_fdma_deinit(lan966x); + cleanup_ptp: lan966x_ptp_deinit(lan966x); @@ -1176,6 +1207,7 @@ static int lan966x_remove(struct platform_device *pdev) struct lan966x *lan966x = platform_get_drvdata(pdev); lan966x_taprio_deinit(lan966x); + lan966x_vcap_deinit(lan966x); lan966x_fdma_deinit(lan966x); lan966x_cleanup_ports(lan966x); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 4ec33999e4df..3491f1961835 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -9,6 +9,7 @@ #include <linux/phy.h> #include <linux/phylink.h> #include <linux/ptp_clock_kernel.h> +#include <net/page_pool.h> #include <net/pkt_cls.h> #include <net/pkt_sched.h> #include <net/switchdev.h> @@ -87,6 +88,10 @@ #define SE_IDX_QUEUE 0 /* 0-79 : Queue scheduler elements */ #define SE_IDX_PORT 80 /* 80-89 : Port schedular elements */ +#define LAN966X_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ +#define LAN966X_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ +#define LAN966X_VCAP_CID_IS2_MAX (VCAP_CID_INGRESS_STAGE2_L2 - 1) /* IS2 Max */ + /* MAC table entry types. * ENTRYTYPE_NORMAL is subject to aging. * ENTRYTYPE_LOCKED is not subject to aging. @@ -100,6 +105,29 @@ enum macaccess_entry_type { ENTRYTYPE_MACV6, }; +/* FDMA return action codes for checking if the frame is valid + * FDMA_PASS, frame is valid and can be used + * FDMA_ERROR, something went wrong, stop getting more frames + * FDMA_DROP, frame is dropped, but continue to get more frames + * FDMA_TX, frame is given to TX, but continue to get more frames + * FDMA_REDIRECT, frame is given to TX, but continue to get more frames + */ +enum lan966x_fdma_action { + FDMA_PASS = 0, + FDMA_ERROR, + FDMA_DROP, + FDMA_TX, + FDMA_REDIRECT, +}; + +/* Controls how PORT_MASK is applied */ +enum LAN966X_PORT_MASK_MODE { + LAN966X_PMM_NO_ACTION, + LAN966X_PMM_REPLACE, + LAN966X_PMM_FORWARDING, + LAN966X_PMM_REDIRECT, +}; + struct lan966x_port; struct lan966x_db { @@ -150,15 +178,28 @@ struct lan966x_rx { */ u8 page_order; + /* Represents the max size frame that it can receive to the CPU. This + * includes the IFH + VLAN tags + frame + skb_shared_info + */ + u32 max_mtu; + u8 channel_id; + + struct page_pool *page_pool; }; struct lan966x_tx_dcb_buf { - struct net_device *dev; - struct sk_buff *skb; dma_addr_t dma_addr; - bool used; - bool ptp; + struct net_device *dev; + union { + struct sk_buff *skb; + struct xdp_frame *xdpf; + } data; + u32 len; + u32 used : 1; + u32 ptp : 1; + u32 use_skb : 1; + u32 xdp_ndo : 1; }; struct lan966x_tx { @@ -271,6 +312,9 @@ struct lan966x { struct lan966x_port *mirror_monitor; u32 mirror_mask[2]; u32 mirror_count; + + /* vcap */ + struct vcap_control *vcap_ctrl; }; struct lan966x_port_config { @@ -288,6 +332,7 @@ struct lan966x_port_tc { unsigned long police_id; unsigned long ingress_mirror_id; unsigned long egress_mirror_id; + unsigned long goto_id; struct flow_stats police_stat; struct flow_stats mirror_stat; }; @@ -320,6 +365,9 @@ struct lan966x_port { enum netdev_lag_hash hash_type; struct lan966x_port_tc tc; + + struct bpf_prog *xdp_prog; + struct xdp_rxq_info xdp_rxq; }; extern const struct phylink_mac_ops lan966x_phylink_mac_ops; @@ -337,6 +385,8 @@ bool lan966x_hw_offload(struct lan966x *lan966x, u32 port, struct sk_buff *skb); void lan966x_ifh_get_src_port(void *ifh, u64 *src_port); void lan966x_ifh_get_timestamp(void *ifh, u64 *timestamp); +void lan966x_ifh_set_bypass(void *ifh, u64 bypass); +void lan966x_ifh_set_port(void *ifh, u64 bypass); void lan966x_stats_get(struct net_device *dev, struct rtnl_link_stats64 *stats); @@ -435,14 +485,21 @@ irqreturn_t lan966x_ptp_irq_handler(int irq, void *args); irqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args); u32 lan966x_ptp_get_period_ps(void); int lan966x_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts); +int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr); +int lan966x_ptp_del_traps(struct lan966x_port *port); int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev); +int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, + struct xdp_frame *frame, + struct page *page, + bool dma_map); int lan966x_fdma_change_mtu(struct lan966x *lan966x); void lan966x_fdma_netdev_init(struct lan966x *lan966x, struct net_device *dev); void lan966x_fdma_netdev_deinit(struct lan966x *lan966x, struct net_device *dev); int lan966x_fdma_init(struct lan966x *lan966x); void lan966x_fdma_deinit(struct lan966x *lan966x); irqreturn_t lan966x_fdma_irq_handler(int irq, void *args); +int lan966x_fdma_reload_page_pool(struct lan966x *lan966x); int lan966x_lag_port_join(struct lan966x_port *port, struct net_device *brport_dev, @@ -527,6 +584,36 @@ void lan966x_mirror_port_stats(struct lan966x_port *port, struct flow_stats *stats, bool ingress); +int lan966x_xdp_port_init(struct lan966x_port *port); +void lan966x_xdp_port_deinit(struct lan966x_port *port); +int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp); +int lan966x_xdp_run(struct lan966x_port *port, + struct page *page, + u32 data_len); +int lan966x_xdp_xmit(struct net_device *dev, + int n, + struct xdp_frame **frames, + u32 flags); +bool lan966x_xdp_present(struct lan966x *lan966x); +static inline bool lan966x_xdp_port_present(struct lan966x_port *port) +{ + return !!port->xdp_prog; +} + +int lan966x_vcap_init(struct lan966x *lan966x); +void lan966x_vcap_deinit(struct lan966x *lan966x); + +int lan966x_tc_flower(struct lan966x_port *port, + struct flow_cls_offload *f); + +int lan966x_goto_port_add(struct lan966x_port *port, + struct flow_action_entry *act, + unsigned long goto_id, + struct netlink_ext_ack *extack); +int lan966x_goto_port_del(struct lan966x_port *port, + unsigned long goto_id, + struct netlink_ext_ack *extack); + static inline void __iomem *lan_addr(void __iomem *base[], int id, int tinst, int tcnt, int gbase, int ginst, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c index e4ac59480514..c5f9803e6e63 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c @@ -5,7 +5,6 @@ #include <linux/device.h> #include <linux/netdevice.h> #include <linux/phy/phy.h> -#include <linux/sfp.h> #include "lan966x_main.h" @@ -125,7 +124,6 @@ static void lan966x_pcs_aneg_restart(struct phylink_pcs *pcs) } const struct phylink_mac_ops lan966x_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = lan966x_phylink_mac_select, .mac_config = lan966x_phylink_mac_config, .mac_prepare = lan966x_phylink_mac_prepare, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c index e5a2bbe064f8..f9ebfaafbebc 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c @@ -3,6 +3,8 @@ #include <linux/ptp_classify.h> #include "lan966x_main.h" +#include "vcap_api.h" +#include "vcap_api_client.h" #define LAN966X_MAX_PTP_ID 512 @@ -18,6 +20,17 @@ #define TOD_ACC_PIN 0x7 +/* This represents the base rule ID for the PTP rules that are added in the + * VCAP to trap frames to CPU. This number needs to be bigger than the maximum + * number of entries that can exist in the VCAP. + */ +#define LAN966X_VCAP_PTP_RULE_ID 1000000 +#define LAN966X_VCAP_L2_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 0) +#define LAN966X_VCAP_IPV4_EV_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 1) +#define LAN966X_VCAP_IPV4_GEN_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 2) +#define LAN966X_VCAP_IPV6_EV_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 3) +#define LAN966X_VCAP_IPV6_GEN_PTP_TRAP (LAN966X_VCAP_PTP_RULE_ID + 4) + enum { PTP_PIN_ACTION_IDLE = 0, PTP_PIN_ACTION_LOAD, @@ -35,19 +48,226 @@ static u64 lan966x_ptp_get_nominal_value(void) return 0x304d4873ecade305; } +static int lan966x_ptp_add_trap(struct lan966x_port *port, + int (*add_ptp_key)(struct vcap_rule *vrule, + struct lan966x_port*), + u32 rule_id, + u16 proto) +{ + struct lan966x *lan966x = port->lan966x; + struct vcap_rule *vrule; + int err; + + vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id); + if (vrule) { + u32 value, mask; + + /* Just modify the ingress port mask and exit */ + vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, + &value, &mask); + mask &= ~BIT(port->chip_port); + vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, + value, mask); + + err = vcap_mod_rule(vrule); + goto free_rule; + } + + vrule = vcap_alloc_rule(lan966x->vcap_ctrl, port->dev, + LAN966X_VCAP_CID_IS2_L0, + VCAP_USER_PTP, 0, rule_id); + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + err = add_ptp_key(vrule, port); + if (err) + goto free_rule; + + err = vcap_set_rule_set_actionset(vrule, VCAP_AFS_BASE_TYPE); + err |= vcap_rule_add_action_bit(vrule, VCAP_AF_CPU_COPY_ENA, VCAP_BIT_1); + err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, LAN966X_PMM_REPLACE); + err |= vcap_val_rule(vrule, proto); + if (err) + goto free_rule; + + err = vcap_add_rule(vrule); + +free_rule: + /* Free the local copy of the rule */ + vcap_free_rule(vrule); + return err; +} + +static int lan966x_ptp_del_trap(struct lan966x_port *port, + u32 rule_id) +{ + struct lan966x *lan966x = port->lan966x; + struct vcap_rule *vrule; + u32 value, mask; + int err; + + vrule = vcap_get_rule(lan966x->vcap_ctrl, rule_id); + if (!vrule) + return -EEXIST; + + vcap_rule_get_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, &value, &mask); + mask |= BIT(port->chip_port); + + /* No other port requires this trap, so it is safe to remove it */ + if (mask == GENMASK(lan966x->num_phys_ports, 0)) { + err = vcap_del_rule(lan966x->vcap_ctrl, port->dev, rule_id); + goto free_rule; + } + + vcap_rule_mod_key_u32(vrule, VCAP_KF_IF_IGR_PORT_MASK, value, mask); + err = vcap_mod_rule(vrule); + +free_rule: + vcap_free_rule(vrule); + return err; +} + +static int lan966x_ptp_add_l2_key(struct vcap_rule *vrule, + struct lan966x_port *port) +{ + return vcap_rule_add_key_u32(vrule, VCAP_KF_ETYPE, ETH_P_1588, ~0); +} + +static int lan966x_ptp_add_ip_event_key(struct vcap_rule *vrule, + struct lan966x_port *port) +{ + return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_EV_PORT, ~0) || + vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0); +} + +static int lan966x_ptp_add_ip_general_key(struct vcap_rule *vrule, + struct lan966x_port *port) +{ + return vcap_rule_add_key_u32(vrule, VCAP_KF_L4_DPORT, PTP_GEN_PORT, ~0) || + vcap_rule_add_key_bit(vrule, VCAP_KF_TCP_IS, VCAP_BIT_0); +} + +static int lan966x_ptp_add_l2_rule(struct lan966x_port *port) +{ + return lan966x_ptp_add_trap(port, lan966x_ptp_add_l2_key, + LAN966X_VCAP_L2_PTP_TRAP, ETH_P_ALL); +} + +static int lan966x_ptp_add_ipv4_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_event_key, + LAN966X_VCAP_IPV4_EV_PTP_TRAP, ETH_P_IP); + if (err) + return err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_general_key, + LAN966X_VCAP_IPV4_GEN_PTP_TRAP, ETH_P_IP); + if (err) + lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_EV_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_add_ipv6_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_event_key, + LAN966X_VCAP_IPV6_EV_PTP_TRAP, ETH_P_IPV6); + if (err) + return err; + + err = lan966x_ptp_add_trap(port, lan966x_ptp_add_ip_general_key, + LAN966X_VCAP_IPV6_GEN_PTP_TRAP, ETH_P_IPV6); + if (err) + lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_EV_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_del_l2_rule(struct lan966x_port *port) +{ + return lan966x_ptp_del_trap(port, LAN966X_VCAP_L2_PTP_TRAP); +} + +static int lan966x_ptp_del_ipv4_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_EV_PTP_TRAP); + err |= lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV4_GEN_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_del_ipv6_rules(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_EV_PTP_TRAP); + err |= lan966x_ptp_del_trap(port, LAN966X_VCAP_IPV6_GEN_PTP_TRAP); + + return err; +} + +static int lan966x_ptp_add_traps(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_add_l2_rule(port); + if (err) + goto err_l2; + + err = lan966x_ptp_add_ipv4_rules(port); + if (err) + goto err_ipv4; + + err = lan966x_ptp_add_ipv6_rules(port); + if (err) + goto err_ipv6; + + return err; + +err_ipv6: + lan966x_ptp_del_ipv4_rules(port); +err_ipv4: + lan966x_ptp_del_l2_rule(port); +err_l2: + return err; +} + +int lan966x_ptp_del_traps(struct lan966x_port *port) +{ + int err; + + err = lan966x_ptp_del_l2_rule(port); + err |= lan966x_ptp_del_ipv4_rules(port); + err |= lan966x_ptp_del_ipv6_rules(port); + + return err; +} + +int lan966x_ptp_setup_traps(struct lan966x_port *port, struct ifreq *ifr) +{ + struct hwtstamp_config cfg; + + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) + return -EFAULT; + + if (cfg.rx_filter == HWTSTAMP_FILTER_NONE) + return lan966x_ptp_del_traps(port); + else + return lan966x_ptp_add_traps(port); +} + int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr) { struct lan966x *lan966x = port->lan966x; struct hwtstamp_config cfg; struct lan966x_phc *phc; - /* For now don't allow to run ptp on ports that are part of a bridge, - * because in case of transparent clock the HW will still forward the - * frames, so there would be duplicate frames - */ - if (lan966x->bridge_mask & BIT(port->chip_port)) - return -EINVAL; - if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) return -EFAULT; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h index fb5087fef22e..9767b5a1c958 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_regs.h @@ -25,6 +25,7 @@ enum lan966x_target { TARGET_QSYS = 46, TARGET_REW = 47, TARGET_SYS = 52, + TARGET_VCAP = 61, NUM_TARGETS = 66 }; @@ -315,6 +316,69 @@ enum lan966x_target { #define ANA_DROP_CFG_DROP_MC_SMAC_ENA_GET(x)\ FIELD_GET(ANA_DROP_CFG_DROP_MC_SMAC_ENA, x) +/* ANA:PORT:VCAP_S2_CFG */ +#define ANA_VCAP_S2_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 28, 0, 1, 4) + +#define ANA_VCAP_S2_CFG_ISDX_ENA GENMASK(20, 19) +#define ANA_VCAP_S2_CFG_ISDX_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ISDX_ENA, x) +#define ANA_VCAP_S2_CFG_ISDX_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ISDX_ENA, x) + +#define ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA GENMASK(18, 17) +#define ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA, x) +#define ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_UDP_PAYLOAD_ENA, x) + +#define ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA GENMASK(16, 15) +#define ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA, x) +#define ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ETYPE_PAYLOAD_ENA, x) + +#define ANA_VCAP_S2_CFG_ENA BIT(14) +#define ANA_VCAP_S2_CFG_ENA_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ENA, x) +#define ANA_VCAP_S2_CFG_ENA_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ENA, x) + +#define ANA_VCAP_S2_CFG_SNAP_DIS GENMASK(13, 12) +#define ANA_VCAP_S2_CFG_SNAP_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_SNAP_DIS, x) +#define ANA_VCAP_S2_CFG_SNAP_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_SNAP_DIS, x) + +#define ANA_VCAP_S2_CFG_ARP_DIS GENMASK(11, 10) +#define ANA_VCAP_S2_CFG_ARP_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_ARP_DIS, x) +#define ANA_VCAP_S2_CFG_ARP_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_ARP_DIS, x) + +#define ANA_VCAP_S2_CFG_IP_TCPUDP_DIS GENMASK(9, 8) +#define ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_IP_TCPUDP_DIS, x) +#define ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_IP_TCPUDP_DIS, x) + +#define ANA_VCAP_S2_CFG_IP_OTHER_DIS GENMASK(7, 6) +#define ANA_VCAP_S2_CFG_IP_OTHER_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_IP_OTHER_DIS, x) +#define ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_IP_OTHER_DIS, x) + +#define ANA_VCAP_S2_CFG_IP6_CFG GENMASK(5, 2) +#define ANA_VCAP_S2_CFG_IP6_CFG_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_IP6_CFG, x) +#define ANA_VCAP_S2_CFG_IP6_CFG_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_IP6_CFG, x) + +#define ANA_VCAP_S2_CFG_OAM_DIS GENMASK(1, 0) +#define ANA_VCAP_S2_CFG_OAM_DIS_SET(x)\ + FIELD_PREP(ANA_VCAP_S2_CFG_OAM_DIS, x) +#define ANA_VCAP_S2_CFG_OAM_DIS_GET(x)\ + FIELD_GET(ANA_VCAP_S2_CFG_OAM_DIS, x) + /* ANA:PORT:CPU_FWD_CFG */ #define ANA_CPU_FWD_CFG(g) __REG(TARGET_ANA, 0, 1, 28672, g, 9, 128, 96, 0, 1, 4) @@ -1506,4 +1570,136 @@ enum lan966x_target { #define SYS_RAM_INIT_RAM_INIT_GET(x)\ FIELD_GET(SYS_RAM_INIT_RAM_INIT, x) +/* VCAP:VCAP_CORE_CFG:VCAP_UPDATE_CTRL */ +#define VCAP_UPDATE_CTRL(t) __REG(TARGET_VCAP, t, 3, 0, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_UPDATE_CTRL_UPDATE_CMD GENMASK(24, 22) +#define VCAP_UPDATE_CTRL_UPDATE_CMD_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_CMD, x) +#define VCAP_UPDATE_CTRL_UPDATE_CMD_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_CMD, x) + +#define VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS BIT(21) +#define VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS, x) +#define VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS, x) + +#define VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS BIT(20) +#define VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS, x) +#define VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS, x) + +#define VCAP_UPDATE_CTRL_UPDATE_CNT_DIS BIT(19) +#define VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_CNT_DIS, x) +#define VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_CNT_DIS, x) + +#define VCAP_UPDATE_CTRL_UPDATE_ADDR GENMASK(18, 3) +#define VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_ADDR, x) +#define VCAP_UPDATE_CTRL_UPDATE_ADDR_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_ADDR, x) + +#define VCAP_UPDATE_CTRL_UPDATE_SHOT BIT(2) +#define VCAP_UPDATE_CTRL_UPDATE_SHOT_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_UPDATE_SHOT, x) +#define VCAP_UPDATE_CTRL_UPDATE_SHOT_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_UPDATE_SHOT, x) + +#define VCAP_UPDATE_CTRL_CLEAR_CACHE BIT(1) +#define VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_CLEAR_CACHE, x) +#define VCAP_UPDATE_CTRL_CLEAR_CACHE_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_CLEAR_CACHE, x) + +#define VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN BIT(0) +#define VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN_SET(x)\ + FIELD_PREP(VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN, x) +#define VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN_GET(x)\ + FIELD_GET(VCAP_UPDATE_CTRL_MV_TRAFFIC_IGN, x) + +/* VCAP:VCAP_CORE_CFG:VCAP_MV_CFG */ +#define VCAP_MV_CFG(t) __REG(TARGET_VCAP, t, 3, 0, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_MV_CFG_MV_NUM_POS GENMASK(31, 16) +#define VCAP_MV_CFG_MV_NUM_POS_SET(x)\ + FIELD_PREP(VCAP_MV_CFG_MV_NUM_POS, x) +#define VCAP_MV_CFG_MV_NUM_POS_GET(x)\ + FIELD_GET(VCAP_MV_CFG_MV_NUM_POS, x) + +#define VCAP_MV_CFG_MV_SIZE GENMASK(15, 0) +#define VCAP_MV_CFG_MV_SIZE_SET(x)\ + FIELD_PREP(VCAP_MV_CFG_MV_SIZE, x) +#define VCAP_MV_CFG_MV_SIZE_GET(x)\ + FIELD_GET(VCAP_MV_CFG_MV_SIZE, x) + +/* VCAP:VCAP_CORE_CACHE:VCAP_ENTRY_DAT */ +#define VCAP_ENTRY_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 0, r, 64, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_MASK_DAT */ +#define VCAP_MASK_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 256, r, 64, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_ACTION_DAT */ +#define VCAP_ACTION_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 512, r, 64, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_CNT_DAT */ +#define VCAP_CNT_DAT(t, r) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 768, r, 32, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_CNT_FW_DAT */ +#define VCAP_CNT_FW_DAT(t) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 896, 0, 1, 4) + +/* VCAP:VCAP_CORE_CACHE:VCAP_TG_DAT */ +#define VCAP_TG_DAT(t) __REG(TARGET_VCAP, t, 3, 8, 0, 1, 904, 900, 0, 1, 4) + +/* VCAP:VCAP_CORE_MAP:VCAP_CORE_IDX */ +#define VCAP_CORE_IDX(t) __REG(TARGET_VCAP, t, 3, 912, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_CORE_IDX_CORE_IDX GENMASK(3, 0) +#define VCAP_CORE_IDX_CORE_IDX_SET(x)\ + FIELD_PREP(VCAP_CORE_IDX_CORE_IDX, x) +#define VCAP_CORE_IDX_CORE_IDX_GET(x)\ + FIELD_GET(VCAP_CORE_IDX_CORE_IDX, x) + +/* VCAP:VCAP_CORE_MAP:VCAP_CORE_MAP */ +#define VCAP_CORE_MAP(t) __REG(TARGET_VCAP, t, 3, 912, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_CORE_MAP_CORE_MAP GENMASK(2, 0) +#define VCAP_CORE_MAP_CORE_MAP_SET(x)\ + FIELD_PREP(VCAP_CORE_MAP_CORE_MAP, x) +#define VCAP_CORE_MAP_CORE_MAP_GET(x)\ + FIELD_GET(VCAP_CORE_MAP_CORE_MAP, x) + +/* VCAP:VCAP_CONST:VCAP_VER */ +#define VCAP_VER(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 0, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_WIDTH */ +#define VCAP_ENTRY_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 4, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_CNT */ +#define VCAP_ENTRY_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 8, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_SWCNT */ +#define VCAP_ENTRY_SWCNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 12, 0, 1, 4) + +/* VCAP:VCAP_CONST:ENTRY_TG_WIDTH */ +#define VCAP_ENTRY_TG_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 16, 0, 1, 4) + +/* VCAP:VCAP_CONST:ACTION_DEF_CNT */ +#define VCAP_ACTION_DEF_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 20, 0, 1, 4) + +/* VCAP:VCAP_CONST:ACTION_WIDTH */ +#define VCAP_ACTION_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 24, 0, 1, 4) + +/* VCAP:VCAP_CONST:CNT_WIDTH */ +#define VCAP_CNT_WIDTH(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 28, 0, 1, 4) + +/* VCAP:VCAP_CONST:CORE_CNT */ +#define VCAP_CORE_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 32, 0, 1, 4) + +/* VCAP:VCAP_CONST:IF_CNT */ +#define VCAP_IF_CNT(t) __REG(TARGET_VCAP, t, 3, 924, 0, 1, 40, 36, 0, 1, 4) + #endif /* _LAN966X_REGS_H_ */ diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c index 651d5493ae55..01072121c999 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c @@ -69,6 +69,8 @@ static int lan966x_tc_block_cb(enum tc_setup_type type, void *type_data, switch (type) { case TC_SETUP_CLSMATCHALL: return lan966x_tc_matchall(port, type_data, ingress); + case TC_SETUP_CLSFLOWER: + return lan966x_tc_flower(port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c new file mode 100644 index 000000000000..ba3fa917d6b7 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" +#include "vcap_api.h" +#include "vcap_api_client.h" + +struct lan966x_tc_flower_parse_usage { + struct flow_cls_offload *f; + struct flow_rule *frule; + struct vcap_rule *vrule; + unsigned int used_keys; + u16 l3_proto; +}; + +static int lan966x_tc_flower_handler_ethaddr_usage(struct lan966x_tc_flower_parse_usage *st) +{ + enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; + enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; + struct flow_match_eth_addrs match; + struct vcap_u48_key smac, dmac; + int err = 0; + + flow_rule_match_eth_addrs(st->frule, &match); + + if (!is_zero_ether_addr(match.mask->src)) { + vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); + vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); + if (err) + goto out; + } + + if (!is_zero_ether_addr(match.mask->dst)) { + vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); + vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->f->common.extack, "eth_addr parse error"); + return err; +} + +static int +(*lan966x_tc_flower_handlers_usage[])(struct lan966x_tc_flower_parse_usage *st) = { + [FLOW_DISSECTOR_KEY_ETH_ADDRS] = lan966x_tc_flower_handler_ethaddr_usage, +}; + +static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, + struct vcap_admin *admin, + struct vcap_rule *vrule, + u16 *l3_proto) +{ + struct lan966x_tc_flower_parse_usage state = { + .f = f, + .vrule = vrule, + .l3_proto = ETH_P_ALL, + }; + int err = 0; + + state.frule = flow_cls_offload_flow_rule(f); + for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) { + if (!flow_rule_match_key(state.frule, i) || + !lan966x_tc_flower_handlers_usage[i]) + continue; + + err = lan966x_tc_flower_handlers_usage[i](&state); + if (err) + return err; + } + + if (l3_proto) + *l3_proto = state.l3_proto; + + return err; +} + +static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(fco); + struct flow_action_entry *actent, *last_actent = NULL; + struct flow_action *act = &rule->action; + u64 action_mask = 0; + int idx; + + if (!flow_action_has_entries(act)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); + return -EINVAL; + } + + if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) + return -EOPNOTSUPP; + + flow_action_for_each(idx, actent, act) { + if (action_mask & BIT(actent->id)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "More actions of the same type"); + return -EINVAL; + } + action_mask |= BIT(actent->id); + last_actent = actent; /* Save last action for later check */ + } + + /* Check that last action is a goto */ + if (last_actent->id != FLOW_ACTION_GOTO) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Last action must be 'goto'"); + return -EINVAL; + } + + /* Check if the goto chain is in the next lookup */ + if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, + last_actent->chain_index)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Invalid goto chain"); + return -EINVAL; + } + + /* Catch unsupported combinations of actions */ + if (action_mask & BIT(FLOW_ACTION_TRAP) && + action_mask & BIT(FLOW_ACTION_ACCEPT)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Cannot combine pass and trap action"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int lan966x_tc_flower_add(struct lan966x_port *port, + struct flow_cls_offload *f, + struct vcap_admin *admin) +{ + struct flow_action_entry *act; + u16 l3_proto = ETH_P_ALL; + struct flow_rule *frule; + struct vcap_rule *vrule; + int err, idx; + + err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, f, + admin); + if (err) + return err; + + vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev, + f->common.chain_index, VCAP_USER_TC, + f->common.prio, 0); + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + vrule->cookie = f->cookie; + err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto); + if (err) + goto out; + + frule = flow_cls_offload_flow_rule(f); + + flow_action_for_each(idx, act, &frule->action) { + switch (act->id) { + case FLOW_ACTION_TRAP: + err = vcap_rule_add_action_bit(vrule, + VCAP_AF_CPU_COPY_ENA, + VCAP_BIT_1); + err |= vcap_rule_add_action_u32(vrule, + VCAP_AF_CPU_QUEUE_NUM, + 0); + err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, + LAN966X_PMM_REPLACE); + err |= vcap_set_rule_set_actionset(vrule, + VCAP_AFS_BASE_TYPE); + if (err) + goto out; + + break; + case FLOW_ACTION_GOTO: + break; + default: + NL_SET_ERR_MSG_MOD(f->common.extack, + "Unsupported TC action"); + err = -EOPNOTSUPP; + goto out; + } + } + + err = vcap_val_rule(vrule, l3_proto); + if (err) { + vcap_set_tc_exterr(f, vrule); + goto out; + } + + err = vcap_add_rule(vrule); + if (err) + NL_SET_ERR_MSG_MOD(f->common.extack, + "Could not add the filter"); +out: + vcap_free_rule(vrule); + return err; +} + +static int lan966x_tc_flower_del(struct lan966x_port *port, + struct flow_cls_offload *f, + struct vcap_admin *admin) +{ + struct vcap_control *vctrl; + int err = -ENOENT, rule_id; + + vctrl = port->lan966x->vcap_ctrl; + while (true) { + rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie); + if (rule_id <= 0) + break; + + err = vcap_del_rule(vctrl, port->dev, rule_id); + if (err) { + NL_SET_ERR_MSG_MOD(f->common.extack, + "Cannot delete rule"); + break; + } + } + + return err; +} + +int lan966x_tc_flower(struct lan966x_port *port, + struct flow_cls_offload *f) +{ + struct vcap_admin *admin; + + admin = vcap_find_admin(port->lan966x->vcap_ctrl, + f->common.chain_index); + if (!admin) { + NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain"); + return -EINVAL; + } + + switch (f->command) { + case FLOW_CLS_REPLACE: + return lan966x_tc_flower_add(port, f, admin); + case FLOW_CLS_DESTROY: + return lan966x_tc_flower_del(port, f, admin); + default: + return -EOPNOTSUPP; + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c index 7368433b9277..a539abaad9b6 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_matchall.c @@ -23,6 +23,9 @@ static int lan966x_tc_matchall_add(struct lan966x_port *port, case FLOW_ACTION_MIRRED: return lan966x_mirror_port_add(port, act, f->cookie, ingress, f->common.extack); + case FLOW_ACTION_GOTO: + return lan966x_goto_port_add(port, act, f->cookie, + f->common.extack); default: NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported action"); @@ -43,6 +46,9 @@ static int lan966x_tc_matchall_del(struct lan966x_port *port, f->cookie == port->tc.egress_mirror_id) { return lan966x_mirror_port_del(port, ingress, f->common.extack); + } else if (f->cookie == port->tc.goto_id) { + return lan966x_goto_port_del(port, f->cookie, + f->common.extack); } else { NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported action"); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c new file mode 100644 index 000000000000..928e711960e6 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.c @@ -0,0 +1,1608 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +#include <linux/types.h> +#include <linux/kernel.h> + +#include "lan966x_vcap_ag_api.h" + +/* keyfields */ +static const struct vcap_field is2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 139, + .width = 16, + }, + [VCAP_KF_L2_FRM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 155, + .width = 4, + }, + [VCAP_KF_L2_PAYLOAD0] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD1] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 8, + }, + [VCAP_KF_L2_PAYLOAD2] = { + .type = VCAP_FIELD_U32, + .offset = 183, + .width = 3, + }, +}; + +static const struct vcap_field is2_mac_llc_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_L2_LLC] = { + .type = VCAP_FIELD_U48, + .offset = 139, + .width = 40, + }, +}; + +static const struct vcap_field is2_mac_snap_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_L2_SNAP] = { + .type = VCAP_FIELD_U48, + .offset = 139, + .width = 40, + }, +}; + +static const struct vcap_field is2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 91, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 92, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 131, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 163, + .width = 1, + }, +}; + +static const struct vcap_field is2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 44, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 45, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 47, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 48, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 121, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 122, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 138, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 154, + .width = 8, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 162, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 163, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 164, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 165, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 166, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 167, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 169, + .width = 1, + }, + [VCAP_KF_L4_1588_DOM] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 8, + }, + [VCAP_KF_L4_1588_VER] = { + .type = VCAP_FIELD_U32, + .offset = 178, + .width = 4, + }, +}; + +static const struct vcap_field is2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT] = { + .type = VCAP_FIELD_BIT, + .offset = 44, + .width = 1, + }, + [VCAP_KF_L3_FRAG_OFS_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 45, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 47, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 48, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 121, + .width = 8, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U56, + .offset = 129, + .width = 56, + }, +}; + +static const struct vcap_field is2_ip6_std_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 44, + .width = 128, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 172, + .width = 8, + }, +}; + +static const struct vcap_field is2_oam_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 26, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 3, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_OAM_MEL_FLAGS] = { + .type = VCAP_FIELD_U32, + .offset = 139, + .width = 7, + }, + [VCAP_KF_OAM_VER] = { + .type = VCAP_FIELD_U32, + .offset = 146, + .width = 5, + }, + [VCAP_KF_OAM_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 151, + .width = 8, + }, + [VCAP_KF_OAM_FLAGS] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 8, + }, + [VCAP_KF_OAM_MEPID] = { + .type = VCAP_FIELD_U32, + .offset = 167, + .width = 16, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 183, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 184, + .width = 1, + }, + [VCAP_KF_OAM_DETECTED] = { + .type = VCAP_FIELD_BIT, + .offset = 185, + .width = 1, + }, +}; + +static const struct vcap_field is2_ip6_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 11, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 21, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 37, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 38, + .width = 3, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 50, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 178, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 306, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 307, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 308, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 324, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 340, + .width = 8, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 348, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 349, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 350, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 351, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 352, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 353, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 354, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 355, + .width = 1, + }, + [VCAP_KF_L4_1588_DOM] = { + .type = VCAP_FIELD_U32, + .offset = 356, + .width = 8, + }, + [VCAP_KF_L4_1588_VER] = { + .type = VCAP_FIELD_U32, + .offset = 364, + .width = 4, + }, +}; + +static const struct vcap_field is2_ip6_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 11, + .width = 9, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_KF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 21, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 22, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 24, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 12, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 37, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 38, + .width = 3, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 50, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 178, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 306, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 307, + .width = 8, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U56, + .offset = 315, + .width = 56, + }, +}; + +static const struct vcap_field is2_smac_sip4_keyfield[] = { + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 4, + .width = 48, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 52, + .width = 32, + }, +}; + +static const struct vcap_field is2_smac_sip6_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 4, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 8, + .width = 48, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 56, + .width = 128, + }, +}; + +/* keyfield_set */ +static const struct vcap_set is2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_MAC_LLC] = { + .type_id = 1, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_MAC_SNAP] = { + .type_id = 2, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 3, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 4, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 5, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_STD] = { + .type_id = 6, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_OAM] = { + .type_id = 7, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_TCP_UDP] = { + .type_id = 0, + .sw_per_item = 4, + .sw_cnt = 1, + }, + [VCAP_KFS_IP6_OTHER] = { + .type_id = 1, + .sw_per_item = 4, + .sw_cnt = 1, + }, + [VCAP_KFS_SMAC_SIP4] = { + .type_id = -1, + .sw_per_item = 1, + .sw_cnt = 4, + }, + [VCAP_KFS_SMAC_SIP6] = { + .type_id = 8, + .sw_per_item = 2, + .sw_cnt = 2, + }, +}; + +/* keyfield_set map */ +static const struct vcap_field *is2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = is2_mac_etype_keyfield, + [VCAP_KFS_MAC_LLC] = is2_mac_llc_keyfield, + [VCAP_KFS_MAC_SNAP] = is2_mac_snap_keyfield, + [VCAP_KFS_ARP] = is2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = is2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = is2_ip4_other_keyfield, + [VCAP_KFS_IP6_STD] = is2_ip6_std_keyfield, + [VCAP_KFS_OAM] = is2_oam_keyfield, + [VCAP_KFS_IP6_TCP_UDP] = is2_ip6_tcp_udp_keyfield, + [VCAP_KFS_IP6_OTHER] = is2_ip6_other_keyfield, + [VCAP_KFS_SMAC_SIP4] = is2_smac_sip4_keyfield, + [VCAP_KFS_SMAC_SIP6] = is2_smac_sip6_keyfield, +}; + +/* keyfield_set map sizes */ +static int is2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(is2_mac_etype_keyfield), + [VCAP_KFS_MAC_LLC] = ARRAY_SIZE(is2_mac_llc_keyfield), + [VCAP_KFS_MAC_SNAP] = ARRAY_SIZE(is2_mac_snap_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(is2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(is2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(is2_ip4_other_keyfield), + [VCAP_KFS_IP6_STD] = ARRAY_SIZE(is2_ip6_std_keyfield), + [VCAP_KFS_OAM] = ARRAY_SIZE(is2_oam_keyfield), + [VCAP_KFS_IP6_TCP_UDP] = ARRAY_SIZE(is2_ip6_tcp_udp_keyfield), + [VCAP_KFS_IP6_OTHER] = ARRAY_SIZE(is2_ip6_other_keyfield), + [VCAP_KFS_SMAC_SIP4] = ARRAY_SIZE(is2_smac_sip4_keyfield), + [VCAP_KFS_SMAC_SIP6] = ARRAY_SIZE(is2_smac_sip6_keyfield), +}; + +/* actionfields */ +static const struct vcap_field is2_base_type_actionfield[] = { + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 2, + }, + [VCAP_AF_MIRROR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_LRN_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 9, + }, + [VCAP_AF_POLICE_VCAP_ONLY] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 8, + }, + [VCAP_AF_REW_OP] = { + .type = VCAP_FIELD_U32, + .offset = 28, + .width = 16, + }, + [VCAP_AF_ISDX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 44, + .width = 1, + }, + [VCAP_AF_ACL_ID] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 6, + }, +}; + +static const struct vcap_field is2_smac_sip_actionfield[] = { + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 3, + }, + [VCAP_AF_FWD_KILL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_AF_HOST_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, +}; + +/* actionfield_set */ +static const struct vcap_set is2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 2, + .sw_cnt = 2, + }, + [VCAP_AFS_SMAC_SIP] = { + .type_id = -1, + .sw_per_item = 1, + .sw_cnt = 4, + }, +}; + +/* actionfield_set map */ +static const struct vcap_field *is2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = is2_base_type_actionfield, + [VCAP_AFS_SMAC_SIP] = is2_smac_sip_actionfield, +}; + +/* actionfield_set map size */ +static int is2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(is2_base_type_actionfield), + [VCAP_AFS_SMAC_SIP] = ARRAY_SIZE(is2_smac_sip_actionfield), +}; + +/* Type Groups */ +static const struct vcap_typegroup is2_x4_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 96, + .width = 1, + .value = 0, + }, + { + .offset = 192, + .width = 2, + .value = 0, + }, + { + .offset = 288, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x2_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 96, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { + [4] = is2_x4_keyfield_set_typegroups, + [2] = is2_x2_keyfield_set_typegroups, + [1] = is2_x1_keyfield_set_typegroups, + [5] = NULL, +}; + +static const struct vcap_typegroup is2_x2_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 31, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { + [2] = is2_x2_actionfield_set_typegroups, + [1] = is2_x1_actionfield_set_typegroups, + [5] = NULL, +}; + +/* Keyfieldset names */ +static const char * const vcap_keyfield_set_names[] = { + [VCAP_KFS_NO_VALUE] = "(None)", + [VCAP_KFS_ARP] = "VCAP_KFS_ARP", + [VCAP_KFS_IP4_OTHER] = "VCAP_KFS_IP4_OTHER", + [VCAP_KFS_IP4_TCP_UDP] = "VCAP_KFS_IP4_TCP_UDP", + [VCAP_KFS_IP6_OTHER] = "VCAP_KFS_IP6_OTHER", + [VCAP_KFS_IP6_STD] = "VCAP_KFS_IP6_STD", + [VCAP_KFS_IP6_TCP_UDP] = "VCAP_KFS_IP6_TCP_UDP", + [VCAP_KFS_MAC_ETYPE] = "VCAP_KFS_MAC_ETYPE", + [VCAP_KFS_MAC_LLC] = "VCAP_KFS_MAC_LLC", + [VCAP_KFS_MAC_SNAP] = "VCAP_KFS_MAC_SNAP", + [VCAP_KFS_OAM] = "VCAP_KFS_OAM", + [VCAP_KFS_SMAC_SIP4] = "VCAP_KFS_SMAC_SIP4", + [VCAP_KFS_SMAC_SIP6] = "VCAP_KFS_SMAC_SIP6", +}; + +/* Actionfieldset names */ +static const char * const vcap_actionfield_set_names[] = { + [VCAP_AFS_NO_VALUE] = "(None)", + [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", + [VCAP_AFS_SMAC_SIP] = "VCAP_AFS_SMAC_SIP", +}; + +/* Keyfield names */ +static const char * const vcap_keyfield_names[] = { + [VCAP_KF_NO_VALUE] = "(None)", + [VCAP_KF_8021Q_DEI_CLS] = "8021Q_DEI_CLS", + [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_VID_CLS] = "8021Q_VID_CLS", + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = "8021Q_VLAN_TAGGED_IS", + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = "ARP_ADDR_SPACE_OK_IS", + [VCAP_KF_ARP_LEN_OK_IS] = "ARP_LEN_OK_IS", + [VCAP_KF_ARP_OPCODE] = "ARP_OPCODE", + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = "ARP_OPCODE_UNKNOWN_IS", + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = "ARP_PROTO_SPACE_OK_IS", + [VCAP_KF_ARP_SENDER_MATCH_IS] = "ARP_SENDER_MATCH_IS", + [VCAP_KF_ARP_TGT_MATCH_IS] = "ARP_TGT_MATCH_IS", + [VCAP_KF_ETYPE] = "ETYPE", + [VCAP_KF_HOST_MATCH] = "HOST_MATCH", + [VCAP_KF_IF_IGR_PORT] = "IF_IGR_PORT", + [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", + [VCAP_KF_IP4_IS] = "IP4_IS", + [VCAP_KF_ISDX_GT0_IS] = "ISDX_GT0_IS", + [VCAP_KF_L2_BC_IS] = "L2_BC_IS", + [VCAP_KF_L2_DMAC] = "L2_DMAC", + [VCAP_KF_L2_FRM_TYPE] = "L2_FRM_TYPE", + [VCAP_KF_L2_LLC] = "L2_LLC", + [VCAP_KF_L2_MC_IS] = "L2_MC_IS", + [VCAP_KF_L2_PAYLOAD0] = "L2_PAYLOAD0", + [VCAP_KF_L2_PAYLOAD1] = "L2_PAYLOAD1", + [VCAP_KF_L2_PAYLOAD2] = "L2_PAYLOAD2", + [VCAP_KF_L2_SMAC] = "L2_SMAC", + [VCAP_KF_L2_SNAP] = "L2_SNAP", + [VCAP_KF_L3_DIP_EQ_SIP_IS] = "L3_DIP_EQ_SIP_IS", + [VCAP_KF_L3_FRAGMENT] = "L3_FRAGMENT", + [VCAP_KF_L3_FRAG_OFS_GT0] = "L3_FRAG_OFS_GT0", + [VCAP_KF_L3_IP4_DIP] = "L3_IP4_DIP", + [VCAP_KF_L3_IP4_SIP] = "L3_IP4_SIP", + [VCAP_KF_L3_IP6_DIP] = "L3_IP6_DIP", + [VCAP_KF_L3_IP6_SIP] = "L3_IP6_SIP", + [VCAP_KF_L3_IP_PROTO] = "L3_IP_PROTO", + [VCAP_KF_L3_OPTIONS_IS] = "L3_OPTIONS_IS", + [VCAP_KF_L3_PAYLOAD] = "L3_PAYLOAD", + [VCAP_KF_L3_TOS] = "L3_TOS", + [VCAP_KF_L3_TTL_GT0] = "L3_TTL_GT0", + [VCAP_KF_L4_1588_DOM] = "L4_1588_DOM", + [VCAP_KF_L4_1588_VER] = "L4_1588_VER", + [VCAP_KF_L4_ACK] = "L4_ACK", + [VCAP_KF_L4_DPORT] = "L4_DPORT", + [VCAP_KF_L4_FIN] = "L4_FIN", + [VCAP_KF_L4_PSH] = "L4_PSH", + [VCAP_KF_L4_RNG] = "L4_RNG", + [VCAP_KF_L4_RST] = "L4_RST", + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = "L4_SEQUENCE_EQ0_IS", + [VCAP_KF_L4_SPORT] = "L4_SPORT", + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = "L4_SPORT_EQ_DPORT_IS", + [VCAP_KF_L4_SYN] = "L4_SYN", + [VCAP_KF_L4_URG] = "L4_URG", + [VCAP_KF_LOOKUP_FIRST_IS] = "LOOKUP_FIRST_IS", + [VCAP_KF_LOOKUP_PAG] = "LOOKUP_PAG", + [VCAP_KF_OAM_CCM_CNTS_EQ0] = "OAM_CCM_CNTS_EQ0", + [VCAP_KF_OAM_DETECTED] = "OAM_DETECTED", + [VCAP_KF_OAM_FLAGS] = "OAM_FLAGS", + [VCAP_KF_OAM_MEL_FLAGS] = "OAM_MEL_FLAGS", + [VCAP_KF_OAM_MEPID] = "OAM_MEPID", + [VCAP_KF_OAM_OPCODE] = "OAM_OPCODE", + [VCAP_KF_OAM_VER] = "OAM_VER", + [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_TCP_IS] = "TCP_IS", + [VCAP_KF_TYPE] = "TYPE", +}; + +/* Actionfield names */ +static const char * const vcap_actionfield_names[] = { + [VCAP_AF_NO_VALUE] = "(None)", + [VCAP_AF_ACL_ID] = "ACL_ID", + [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", + [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", + [VCAP_AF_FWD_KILL_ENA] = "FWD_KILL_ENA", + [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", + [VCAP_AF_HOST_MATCH] = "HOST_MATCH", + [VCAP_AF_ISDX_ENA] = "ISDX_ENA", + [VCAP_AF_LRN_DIS] = "LRN_DIS", + [VCAP_AF_MASK_MODE] = "MASK_MODE", + [VCAP_AF_MIRROR_ENA] = "MIRROR_ENA", + [VCAP_AF_POLICE_ENA] = "POLICE_ENA", + [VCAP_AF_POLICE_IDX] = "POLICE_IDX", + [VCAP_AF_POLICE_VCAP_ONLY] = "POLICE_VCAP_ONLY", + [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_REW_OP] = "REW_OP", +}; + +/* VCAPs */ +const struct vcap_info lan966x_vcaps[] = { + [VCAP_TYPE_IS2] = { + .name = "is2", + .rows = 64, + .sw_count = 4, + .sw_width = 96, + .sticky_width = 32, + .act_width = 31, + .default_cnt = 11, + .require_cnt_dis = 1, + .version = 1, + .keyfield_set = is2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is2_keyfield_set), + .actionfield_set = is2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is2_actionfield_set), + .keyfield_set_map = is2_keyfield_set_map, + .keyfield_set_map_size = is2_keyfield_set_map_size, + .actionfield_set_map = is2_actionfield_set_map, + .actionfield_set_map_size = is2_actionfield_set_map_size, + .keyfield_set_typegroups = is2_keyfield_set_typegroups, + .actionfield_set_typegroups = is2_actionfield_set_typegroups, + }, +}; + +const struct vcap_statistics lan966x_vcap_stats = { + .name = "lan966x", + .count = 1, + .keyfield_set_names = vcap_keyfield_set_names, + .actionfield_set_names = vcap_actionfield_set_names, + .keyfield_names = vcap_keyfield_names, + .actionfield_names = vcap_actionfield_names, +}; diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h new file mode 100644 index 000000000000..0d8bbee73b2a --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_ag_api.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef __LAN966X_VCAP_AG_API_H__ +#define __LAN966X_VCAP_AG_API_H__ + +#include "vcap_api.h" + +extern const struct vcap_info lan966x_vcaps[]; +extern const struct vcap_statistics lan966x_vcap_stats; + +#endif /* __LAN966X_VCAP_AG_API_H__ */ diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c new file mode 100644 index 000000000000..d8dc9fbb81e1 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" +#include "lan966x_vcap_ag_api.h" +#include "vcap_api.h" +#include "vcap_api_client.h" + +#define STREAMSIZE (64 * 4) + +#define LAN966X_IS2_LOOKUPS 2 + +enum vcap_is2_port_sel_ipv6 { + VCAP_IS2_PS_IPV6_TCPUDP_OTHER, + VCAP_IS2_PS_IPV6_STD, + VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER, + VCAP_IS2_PS_IPV6_MAC_ETYPE, +}; + +static struct lan966x_vcap_inst { + enum vcap_type vtype; /* type of vcap */ + int tgt_inst; /* hardware instance number */ + int lookups; /* number of lookups in this vcap type */ + int first_cid; /* first chain id in this vcap */ + int last_cid; /* last chain id in this vcap */ + int count; /* number of available addresses */ +} lan966x_vcap_inst_cfg[] = { + { + .vtype = VCAP_TYPE_IS2, /* IS2-0 */ + .tgt_inst = 2, + .lookups = LAN966X_IS2_LOOKUPS, + .first_cid = LAN966X_VCAP_CID_IS2_L0, + .last_cid = LAN966X_VCAP_CID_IS2_MAX, + .count = 256, + }, +}; + +struct lan966x_vcap_cmd_cb { + struct lan966x *lan966x; + u32 instance; +}; + +static u32 lan966x_vcap_read_update_ctrl(const struct lan966x_vcap_cmd_cb *cb) +{ + return lan_rd(cb->lan966x, VCAP_UPDATE_CTRL(cb->instance)); +} + +static void lan966x_vcap_wait_update(struct lan966x *lan966x, int instance) +{ + const struct lan966x_vcap_cmd_cb cb = { .lan966x = lan966x, + .instance = instance }; + u32 val; + + readx_poll_timeout(lan966x_vcap_read_update_ctrl, &cb, val, + (val & VCAP_UPDATE_CTRL_UPDATE_SHOT) == 0, 10, + 100000); +} + +static void __lan966x_vcap_range_init(struct lan966x *lan966x, + struct vcap_admin *admin, + u32 addr, + u32 count) +{ + lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | + VCAP_MV_CFG_MV_SIZE_SET(count - 1), + lan966x, VCAP_MV_CFG(admin->tgt_inst)); + + lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) | + VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(true) | + VCAP_UPDATE_CTRL_UPDATE_SHOT_SET(1), + lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); + + lan966x_vcap_wait_update(lan966x, admin->tgt_inst); +} + +static int lan966x_vcap_cid_to_lookup(int cid) +{ + if (cid >= LAN966X_VCAP_CID_IS2_L1 && + cid < LAN966X_VCAP_CID_IS2_MAX) + return 1; + + return 0; +} + +static int +lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup, + struct vcap_keyset_list *keysetlist, + u16 l3_proto) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + bool found = false; + u32 val; + + /* Check if the port keyset selection is enabled */ + val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port)); + if (!ANA_VCAP_S2_CFG_ENA_GET(val)) + return -ENOENT; + + /* Collect all keysets for the port in a list */ + if (l3_proto == ETH_P_ALL) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_SNAP) { + if (ANA_VCAP_S2_CFG_SNAP_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_LLC); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_SNAP); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_CFM) { + if (ANA_VCAP_S2_CFG_OAM_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_OAM); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) { + if (ANA_VCAP_S2_CFG_ARP_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { + if (ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + + if (ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(val) & (BIT(0) << lookup)) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + else + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + + found = true; + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { + switch (ANA_VCAP_S2_CFG_IP6_CFG_GET(val) & (0x3 << lookup)) { + case VCAP_IS2_PS_IPV6_TCPUDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_OTHER); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_TCP_UDP); + break; + case VCAP_IS2_PS_IPV6_STD: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); + break; + case VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + break; + case VCAP_IS2_PS_IPV6_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + } + + found = true; + } + + if (!found) + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + + return 0; +} + +static enum vcap_keyfield_set +lan966x_vcap_validate_keyset(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + struct vcap_keyset_list keysetlist = {}; + enum vcap_keyfield_set keysets[10] = {}; + int lookup; + int err; + + if (!kslist || kslist->cnt == 0) + return VCAP_KFS_NO_VALUE; + + lookup = lan966x_vcap_cid_to_lookup(rule->vcap_chain_id); + keysetlist.max = ARRAY_SIZE(keysets); + keysetlist.keysets = keysets; + err = lan966x_vcap_is2_get_port_keysets(dev, lookup, &keysetlist, + l3_proto); + if (err) + return VCAP_KFS_NO_VALUE; + + /* Check if there is a match and return the match */ + for (int i = 0; i < kslist->cnt; ++i) + for (int j = 0; j < keysetlist.cnt; ++j) + if (kslist->keysets[i] == keysets[j]) + return kslist->keysets[i]; + + return VCAP_KFS_NO_VALUE; +} + +static bool lan966x_vcap_is_first_chain(struct vcap_rule *rule) +{ + return (rule->vcap_chain_id >= LAN966X_VCAP_CID_IS2_L0 && + rule->vcap_chain_id < LAN966X_VCAP_CID_IS2_L1); +} + +static void lan966x_vcap_add_default_fields(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + struct lan966x_port *port = netdev_priv(dev); + u32 value, mask; + + if (vcap_rule_get_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, + &value, &mask)) + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, + ~BIT(port->chip_port)); + + if (lan966x_vcap_is_first_chain(rule)) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_0); +} + +static void lan966x_vcap_cache_erase(struct vcap_admin *admin) +{ + memset(admin->cache.keystream, 0, STREAMSIZE); + memset(admin->cache.maskstream, 0, STREAMSIZE); + memset(admin->cache.actionstream, 0, STREAMSIZE); + memset(&admin->cache.counter, 0, sizeof(admin->cache.counter)); +} + +static void lan966x_vcap_cache_write(struct net_device *dev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 start, + u32 count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + u32 *keystr, *mskstr, *actstr; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + + switch (sel) { + case VCAP_SEL_ENTRY: + for (int i = 0; i < count; ++i) { + lan_wr(keystr[i] & mskstr[i], lan966x, + VCAP_ENTRY_DAT(admin->tgt_inst, i)); + lan_wr(~mskstr[i], lan966x, + VCAP_MASK_DAT(admin->tgt_inst, i)); + } + break; + case VCAP_SEL_ACTION: + for (int i = 0; i < count; ++i) + lan_wr(actstr[i], lan966x, + VCAP_ACTION_DAT(admin->tgt_inst, i)); + break; + case VCAP_SEL_COUNTER: + admin->cache.sticky = admin->cache.counter > 0; + lan_wr(admin->cache.counter, lan966x, + VCAP_CNT_DAT(admin->tgt_inst, 0)); + break; + default: + break; + } +} + +static void lan966x_vcap_cache_read(struct net_device *dev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 start, + u32 count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + int instance = admin->tgt_inst; + u32 *keystr, *mskstr, *actstr; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + + if (sel & VCAP_SEL_ENTRY) { + for (int i = 0; i < count; ++i) { + keystr[i] = + lan_rd(lan966x, VCAP_ENTRY_DAT(instance, i)); + mskstr[i] = + ~lan_rd(lan966x, VCAP_MASK_DAT(instance, i)); + } + } + + if (sel & VCAP_SEL_ACTION) + for (int i = 0; i < count; ++i) + actstr[i] = + lan_rd(lan966x, VCAP_ACTION_DAT(instance, i)); + + if (sel & VCAP_SEL_COUNTER) { + admin->cache.counter = + lan_rd(lan966x, VCAP_CNT_DAT(instance, 0)); + admin->cache.sticky = admin->cache.counter > 0; + } +} + +static void lan966x_vcap_range_init(struct net_device *dev, + struct vcap_admin *admin, + u32 addr, + u32 count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + + __lan966x_vcap_range_init(lan966x, admin, addr, count); +} + +static void lan966x_vcap_update(struct net_device *dev, + struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, + u32 addr) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + bool clear; + + clear = (cmd == VCAP_CMD_INITIALIZE); + + lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(0) | + VCAP_MV_CFG_MV_SIZE_SET(0), + lan966x, VCAP_MV_CFG(admin->tgt_inst)); + + lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) | + VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) | + VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) | + VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(clear) | + VCAP_UPDATE_CTRL_UPDATE_SHOT, + lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); + + lan966x_vcap_wait_update(lan966x, admin->tgt_inst); +} + +static void lan966x_vcap_move(struct net_device *dev, + struct vcap_admin *admin, + u32 addr, int offset, int count) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + enum vcap_command cmd; + u16 mv_num_pos; + u16 mv_size; + + mv_size = count - 1; + if (offset > 0) { + mv_num_pos = offset - 1; + cmd = VCAP_CMD_MOVE_DOWN; + } else { + mv_num_pos = -offset - 1; + cmd = VCAP_CMD_MOVE_UP; + } + + lan_wr(VCAP_MV_CFG_MV_NUM_POS_SET(mv_num_pos) | + VCAP_MV_CFG_MV_SIZE_SET(mv_size), + lan966x, VCAP_MV_CFG(admin->tgt_inst)); + + lan_wr(VCAP_UPDATE_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_UPDATE_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_UPDATE_CTRL_CLEAR_CACHE_SET(false) | + VCAP_UPDATE_CTRL_UPDATE_SHOT, + lan966x, VCAP_UPDATE_CTRL(admin->tgt_inst)); + + lan966x_vcap_wait_update(lan966x, admin->tgt_inst); +} + +static int lan966x_vcap_port_info(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} + +static int lan966x_vcap_enable(struct net_device *dev, + struct vcap_admin *admin, + bool enable) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + + lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(enable), + ANA_VCAP_S2_CFG_ENA, + lan966x, ANA_VCAP_S2_CFG(port->chip_port)); + + return 0; +} + +static struct vcap_operations lan966x_vcap_ops = { + .validate_keyset = lan966x_vcap_validate_keyset, + .add_default_fields = lan966x_vcap_add_default_fields, + .cache_erase = lan966x_vcap_cache_erase, + .cache_write = lan966x_vcap_cache_write, + .cache_read = lan966x_vcap_cache_read, + .init = lan966x_vcap_range_init, + .update = lan966x_vcap_update, + .move = lan966x_vcap_move, + .port_info = lan966x_vcap_port_info, + .enable = lan966x_vcap_enable, +}; + +static void lan966x_vcap_admin_free(struct vcap_admin *admin) +{ + if (!admin) + return; + + kfree(admin->cache.keystream); + kfree(admin->cache.maskstream); + kfree(admin->cache.actionstream); + mutex_destroy(&admin->lock); + kfree(admin); +} + +static struct vcap_admin * +lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, + const struct lan966x_vcap_inst *cfg) +{ + struct vcap_admin *admin; + + admin = kzalloc(sizeof(*admin), GFP_KERNEL); + if (!admin) + return ERR_PTR(-ENOMEM); + + mutex_init(&admin->lock); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + INIT_LIST_HEAD(&admin->enabled); + + admin->vtype = cfg->vtype; + admin->vinst = 0; + admin->w32be = true; + admin->tgt_inst = cfg->tgt_inst; + + admin->lookups = cfg->lookups; + admin->lookups_per_instance = cfg->lookups; + + admin->first_cid = cfg->first_cid; + admin->last_cid = cfg->last_cid; + + admin->cache.keystream = kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.maskstream = kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.actionstream = kzalloc(STREAMSIZE, GFP_KERNEL); + if (!admin->cache.keystream || + !admin->cache.maskstream || + !admin->cache.actionstream) { + lan966x_vcap_admin_free(admin); + return ERR_PTR(-ENOMEM); + } + + return admin; +} + +static void lan966x_vcap_block_init(struct lan966x *lan966x, + struct vcap_admin *admin, + struct lan966x_vcap_inst *cfg) +{ + admin->first_valid_addr = 0; + admin->last_used_addr = cfg->count; + admin->last_valid_addr = cfg->count - 1; + + lan_wr(VCAP_CORE_IDX_CORE_IDX_SET(0), + lan966x, VCAP_CORE_IDX(admin->tgt_inst)); + lan_wr(VCAP_CORE_MAP_CORE_MAP_SET(1), + lan966x, VCAP_CORE_MAP(admin->tgt_inst)); + + __lan966x_vcap_range_init(lan966x, admin, admin->first_valid_addr, + admin->last_valid_addr - + admin->first_valid_addr); +} + +static void lan966x_vcap_port_key_deselection(struct lan966x *lan966x, + struct vcap_admin *admin) +{ + for (int p = 0; p < lan966x->num_phys_ports; ++p) + lan_wr(0, lan966x, ANA_VCAP_S2_CFG(p)); +} + +int lan966x_vcap_init(struct lan966x *lan966x) +{ + struct lan966x_vcap_inst *cfg; + struct vcap_control *ctrl; + struct vcap_admin *admin; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->vcaps = lan966x_vcaps; + ctrl->stats = &lan966x_vcap_stats; + ctrl->ops = &lan966x_vcap_ops; + + INIT_LIST_HEAD(&ctrl->list); + for (int i = 0; i < ARRAY_SIZE(lan966x_vcap_inst_cfg); ++i) { + cfg = &lan966x_vcap_inst_cfg[i]; + + admin = lan966x_vcap_admin_alloc(lan966x, ctrl, cfg); + if (IS_ERR(admin)) + return PTR_ERR(admin); + + lan966x_vcap_block_init(lan966x, admin, cfg); + lan966x_vcap_port_key_deselection(lan966x, admin); + + list_add_tail(&admin->list, &ctrl->list); + } + + lan966x->vcap_ctrl = ctrl; + + return 0; +} + +void lan966x_vcap_deinit(struct lan966x *lan966x) +{ + struct vcap_admin *admin, *admin_next; + struct vcap_control *ctrl; + + ctrl = lan966x->vcap_ctrl; + if (!ctrl) + return; + + list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { + lan966x_vcap_port_key_deselection(lan966x, admin); + vcap_del_rules(ctrl, admin); + list_del(&admin->list); + lan966x_vcap_admin_free(admin); + } + + kfree(ctrl); +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c new file mode 100644 index 000000000000..2e6f486ec67d --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_xdp.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/bpf.h> +#include <linux/bpf_trace.h> +#include <linux/filter.h> + +#include "lan966x_main.h" + +static int lan966x_xdp_setup(struct net_device *dev, struct netdev_bpf *xdp) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + struct bpf_prog *old_prog; + bool old_xdp, new_xdp; + int err; + + if (!lan966x->fdma) { + NL_SET_ERR_MSG_MOD(xdp->extack, + "Allow to set xdp only when using fdma"); + return -EOPNOTSUPP; + } + + old_xdp = lan966x_xdp_present(lan966x); + old_prog = xchg(&port->xdp_prog, xdp->prog); + new_xdp = lan966x_xdp_present(lan966x); + + if (old_xdp == new_xdp) + goto out; + + err = lan966x_fdma_reload_page_pool(lan966x); + if (err) { + xchg(&port->xdp_prog, old_prog); + return err; + } + +out: + if (old_prog) + bpf_prog_put(old_prog); + + return 0; +} + +int lan966x_xdp(struct net_device *dev, struct netdev_bpf *xdp) +{ + switch (xdp->command) { + case XDP_SETUP_PROG: + return lan966x_xdp_setup(dev, xdp); + default: + return -EINVAL; + } +} + +int lan966x_xdp_xmit(struct net_device *dev, + int n, + struct xdp_frame **frames, + u32 flags) +{ + struct lan966x_port *port = netdev_priv(dev); + int nxmit = 0; + + for (int i = 0; i < n; ++i) { + struct xdp_frame *xdpf = frames[i]; + int err; + + err = lan966x_fdma_xmit_xdpf(port, xdpf, NULL, true); + if (err) + break; + + nxmit++; + } + + return nxmit; +} + +int lan966x_xdp_run(struct lan966x_port *port, struct page *page, u32 data_len) +{ + struct bpf_prog *xdp_prog = port->xdp_prog; + struct lan966x *lan966x = port->lan966x; + struct xdp_frame *xdpf; + struct xdp_buff xdp; + u32 act; + + xdp_init_buff(&xdp, PAGE_SIZE << lan966x->rx.page_order, + &port->xdp_rxq); + xdp_prepare_buff(&xdp, page_address(page), + IFH_LEN_BYTES + XDP_PACKET_HEADROOM, + data_len - IFH_LEN_BYTES, false); + act = bpf_prog_run_xdp(xdp_prog, &xdp); + switch (act) { + case XDP_PASS: + return FDMA_PASS; + case XDP_TX: + xdpf = xdp_convert_buff_to_frame(&xdp); + if (!xdpf) + return FDMA_DROP; + + return lan966x_fdma_xmit_xdpf(port, xdpf, page, false) ? + FDMA_DROP : FDMA_TX; + case XDP_REDIRECT: + if (xdp_do_redirect(port->dev, &xdp, xdp_prog)) + return FDMA_DROP; + + return FDMA_REDIRECT; + default: + bpf_warn_invalid_xdp_action(port->dev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: + trace_xdp_exception(port->dev, xdp_prog, act); + fallthrough; + case XDP_DROP: + return FDMA_DROP; + } +} + +bool lan966x_xdp_present(struct lan966x *lan966x) +{ + for (int p = 0; p < lan966x->num_phys_ports; ++p) { + if (!lan966x->ports[p]) + continue; + + if (lan966x_xdp_port_present(lan966x->ports[p])) + return true; + } + + return false; +} + +int lan966x_xdp_port_init(struct lan966x_port *port) +{ + struct lan966x *lan966x = port->lan966x; + + return xdp_rxq_info_reg(&port->xdp_rxq, port->dev, 0, + lan966x->napi.napi_id); +} + +void lan966x_xdp_port_deinit(struct lan966x_port *port) +{ + if (xdp_rxq_info_is_reg(&port->xdp_rxq)) + xdp_rxq_info_unreg(&port->xdp_rxq); +} diff --git a/drivers/net/ethernet/microchip/sparx5/Kconfig b/drivers/net/ethernet/microchip/sparx5/Kconfig index cc5e48e1bb4c..f58c506bda22 100644 --- a/drivers/net/ethernet/microchip/sparx5/Kconfig +++ b/drivers/net/ethernet/microchip/sparx5/Kconfig @@ -9,5 +9,17 @@ config SPARX5_SWITCH select PHYLINK select PHY_SPARX5_SERDES select RESET_CONTROLLER + select VCAP help This driver supports the Sparx5 network switch device. + +config SPARX5_DCB + bool "Data Center Bridging (DCB) support" + depends on SPARX5_SWITCH && DCB + default y + help + Say Y here if you want to use Data Center Bridging (DCB) in the + driver. This can be used to assign priority to traffic, based on + DSCP and PCP. + + If unsure, set to Y. diff --git a/drivers/net/ethernet/microchip/sparx5/Makefile b/drivers/net/ethernet/microchip/sparx5/Makefile index d1c6ad966747..d0ed7090aa54 100644 --- a/drivers/net/ethernet/microchip/sparx5/Makefile +++ b/drivers/net/ethernet/microchip/sparx5/Makefile @@ -5,7 +5,14 @@ obj-$(CONFIG_SPARX5_SWITCH) += sparx5-switch.o -sparx5-switch-objs := sparx5_main.o sparx5_packet.o \ +sparx5-switch-y := sparx5_main.o sparx5_packet.o \ sparx5_netdev.o sparx5_phylink.o sparx5_port.o sparx5_mactable.o sparx5_vlan.o \ sparx5_switchdev.o sparx5_calendar.o sparx5_ethtool.o sparx5_fdma.o \ - sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o + sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \ + sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o sparx5_tc_matchall.o + +sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o +sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o + +# Provide include files +ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c new file mode 100644 index 000000000000..74abb946b2a3 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include <net/dcbnl.h> + +#include "sparx5_port.h" + +enum sparx5_dcb_apptrust_values { + SPARX5_DCB_APPTRUST_EMPTY, + SPARX5_DCB_APPTRUST_DSCP, + SPARX5_DCB_APPTRUST_PCP, + SPARX5_DCB_APPTRUST_DSCP_PCP, + __SPARX5_DCB_APPTRUST_MAX +}; + +static const struct sparx5_dcb_apptrust { + u8 selectors[IEEE_8021QAZ_APP_SEL_MAX + 1]; + int nselectors; +} *sparx5_port_apptrust[SPX5_PORTS]; + +static const char *sparx5_dcb_apptrust_names[__SPARX5_DCB_APPTRUST_MAX] = { + [SPARX5_DCB_APPTRUST_EMPTY] = "empty", + [SPARX5_DCB_APPTRUST_DSCP] = "dscp", + [SPARX5_DCB_APPTRUST_PCP] = "pcp", + [SPARX5_DCB_APPTRUST_DSCP_PCP] = "dscp pcp" +}; + +/* Sparx5 supported apptrust policies */ +static const struct sparx5_dcb_apptrust + sparx5_dcb_apptrust_policies[__SPARX5_DCB_APPTRUST_MAX] = { + /* Empty *must* be first */ + [SPARX5_DCB_APPTRUST_EMPTY] = { { 0 }, 0 }, + [SPARX5_DCB_APPTRUST_DSCP] = { { IEEE_8021QAZ_APP_SEL_DSCP }, 1 }, + [SPARX5_DCB_APPTRUST_PCP] = { { DCB_APP_SEL_PCP }, 1 }, + [SPARX5_DCB_APPTRUST_DSCP_PCP] = { { IEEE_8021QAZ_APP_SEL_DSCP, + DCB_APP_SEL_PCP }, 2 }, +}; + +/* Validate app entry. + * + * Check for valid selectors and valid protocol and priority ranges. + */ +static int sparx5_dcb_app_validate(struct net_device *dev, + const struct dcb_app *app) +{ + int err = 0; + + switch (app->selector) { + /* Default priority checks */ + case IEEE_8021QAZ_APP_SEL_ETHERTYPE: + if (app->protocol != 0) + err = -EINVAL; + else if (app->priority >= SPX5_PRIOS) + err = -ERANGE; + break; + /* Dscp checks */ + case IEEE_8021QAZ_APP_SEL_DSCP: + if (app->protocol >= SPARX5_PORT_QOS_DSCP_COUNT) + err = -EINVAL; + else if (app->priority >= SPX5_PRIOS) + err = -ERANGE; + break; + /* Pcp checks */ + case DCB_APP_SEL_PCP: + if (app->protocol >= SPARX5_PORT_QOS_PCP_DEI_COUNT) + err = -EINVAL; + else if (app->priority >= SPX5_PRIOS) + err = -ERANGE; + break; + default: + err = -EINVAL; + break; + } + + if (err) + netdev_err(dev, "Invalid entry: %d:%d\n", app->protocol, + app->priority); + + return err; +} + +/* Validate apptrust configuration. + * + * Return index of supported apptrust configuration if valid, otherwise return + * error. + */ +static int sparx5_dcb_apptrust_validate(struct net_device *dev, u8 *selectors, + int nselectors, int *err) +{ + bool match = false; + int i, ii; + + for (i = 0; i < ARRAY_SIZE(sparx5_dcb_apptrust_policies); i++) { + if (sparx5_dcb_apptrust_policies[i].nselectors != nselectors) + continue; + match = true; + for (ii = 0; ii < nselectors; ii++) { + if (sparx5_dcb_apptrust_policies[i].selectors[ii] != + *(selectors + ii)) { + match = false; + break; + } + } + if (match) + break; + } + + /* Requested trust configuration is not supported */ + if (!match) { + netdev_err(dev, "Valid apptrust configurations are:\n"); + for (i = 0; i < ARRAY_SIZE(sparx5_dcb_apptrust_names); i++) + pr_info("order: %s\n", sparx5_dcb_apptrust_names[i]); + *err = -EOPNOTSUPP; + } + + return i; +} + +static bool sparx5_dcb_apptrust_contains(int portno, u8 selector) +{ + const struct sparx5_dcb_apptrust *conf = sparx5_port_apptrust[portno]; + int i; + + for (i = 0; i < conf->nselectors; i++) + if (conf->selectors[i] == selector) + return true; + + return false; +} + +static int sparx5_dcb_app_update(struct net_device *dev) +{ + struct sparx5_port *port = netdev_priv(dev); + struct sparx5_port_qos_dscp_map *dscp_map; + struct sparx5_port_qos_pcp_map *pcp_map; + struct sparx5_port_qos qos = {0}; + struct dcb_app app_itr = {0}; + int portno = port->portno; + int i; + + dscp_map = &qos.dscp.map; + pcp_map = &qos.pcp.map; + + /* Get default prio. */ + qos.default_prio = dcb_ieee_getapp_default_prio_mask(dev); + if (qos.default_prio) + qos.default_prio = fls(qos.default_prio) - 1; + + /* Get dscp ingress mapping */ + for (i = 0; i < ARRAY_SIZE(dscp_map->map); i++) { + app_itr.selector = IEEE_8021QAZ_APP_SEL_DSCP; + app_itr.protocol = i; + dscp_map->map[i] = dcb_getapp(dev, &app_itr); + } + + /* Get pcp ingress mapping */ + for (i = 0; i < ARRAY_SIZE(pcp_map->map); i++) { + app_itr.selector = DCB_APP_SEL_PCP; + app_itr.protocol = i; + pcp_map->map[i] = dcb_getapp(dev, &app_itr); + } + + /* Enable use of pcp for queue classification ? */ + if (sparx5_dcb_apptrust_contains(portno, DCB_APP_SEL_PCP)) { + qos.pcp.qos_enable = true; + qos.pcp.dp_enable = qos.pcp.qos_enable; + } + + /* Enable use of dscp for queue classification ? */ + if (sparx5_dcb_apptrust_contains(portno, IEEE_8021QAZ_APP_SEL_DSCP)) { + qos.dscp.qos_enable = true; + qos.dscp.dp_enable = qos.dscp.qos_enable; + } + + return sparx5_port_qos_set(port, &qos); +} + +/* Set or delete dscp app entry. + * + * Dscp mapping is global for all ports, so set and delete app entries are + * replicated for each port. + */ +static int sparx5_dcb_ieee_dscp_setdel_app(struct net_device *dev, + struct dcb_app *app, bool del) +{ + struct sparx5_port *port = netdev_priv(dev); + struct dcb_app apps[SPX5_PORTS]; + struct sparx5_port *port_itr; + int err, i; + + for (i = 0; i < SPX5_PORTS; i++) { + port_itr = port->sparx5->ports[i]; + if (!port_itr) + continue; + memcpy(&apps[i], app, sizeof(struct dcb_app)); + if (del) + err = dcb_ieee_delapp(port_itr->ndev, &apps[i]); + else + err = dcb_ieee_setapp(port_itr->ndev, &apps[i]); + if (err) + return err; + } + + return 0; +} + +static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app) +{ + struct dcb_app app_itr; + int err = 0; + u8 prio; + + err = sparx5_dcb_app_validate(dev, app); + if (err) + goto out; + + /* Delete current mapping, if it exists */ + prio = dcb_getapp(dev, app); + if (prio) { + app_itr = *app; + app_itr.priority = prio; + dcb_ieee_delapp(dev, &app_itr); + } + + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = sparx5_dcb_ieee_dscp_setdel_app(dev, app, false); + else + err = dcb_ieee_setapp(dev, app); + + if (err) + goto out; + + sparx5_dcb_app_update(dev); + +out: + return err; +} + +static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app) +{ + int err; + + if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP) + err = sparx5_dcb_ieee_dscp_setdel_app(dev, app, true); + else + err = dcb_ieee_delapp(dev, app); + + if (err < 0) + return err; + + return sparx5_dcb_app_update(dev); +} + +static int sparx5_dcb_setapptrust(struct net_device *dev, u8 *selectors, + int nselectors) +{ + struct sparx5_port *port = netdev_priv(dev); + int err = 0, idx; + + idx = sparx5_dcb_apptrust_validate(dev, selectors, nselectors, &err); + if (err < 0) + return err; + + sparx5_port_apptrust[port->portno] = &sparx5_dcb_apptrust_policies[idx]; + + return sparx5_dcb_app_update(dev); +} + +static int sparx5_dcb_getapptrust(struct net_device *dev, u8 *selectors, + int *nselectors) +{ + struct sparx5_port *port = netdev_priv(dev); + const struct sparx5_dcb_apptrust *trust; + + trust = sparx5_port_apptrust[port->portno]; + + memcpy(selectors, trust->selectors, trust->nselectors); + *nselectors = trust->nselectors; + + return 0; +} + +const struct dcbnl_rtnl_ops sparx5_dcbnl_ops = { + .ieee_setapp = sparx5_dcb_ieee_setapp, + .ieee_delapp = sparx5_dcb_ieee_delapp, + .dcbnl_setapptrust = sparx5_dcb_setapptrust, + .dcbnl_getapptrust = sparx5_dcb_getapptrust, +}; + +int sparx5_dcb_init(struct sparx5 *sparx5) +{ + struct sparx5_port *port; + int i; + + for (i = 0; i < SPX5_PORTS; i++) { + port = sparx5->ports[i]; + if (!port) + continue; + port->ndev->dcbnl_ops = &sparx5_dcbnl_ops; + /* Initialize [dscp, pcp] default trust */ + sparx5_port_apptrust[port->portno] = + &sparx5_dcb_apptrust_policies + [SPARX5_DCB_APPTRUST_DSCP_PCP]; + } + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index b6bbb3c9bd7a..d25f4f09faa0 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -675,6 +675,14 @@ static int sparx5_start(struct sparx5 *sparx5) sparx5_board_init(sparx5); err = sparx5_register_notifier_blocks(sparx5); + if (err) + return err; + + err = sparx5_vcap_init(sparx5); + if (err) { + sparx5_unregister_notifier_blocks(sparx5); + return err; + } /* Start Frame DMA with fallback to register based INJ/XTR */ err = -ENXIO; @@ -755,6 +763,8 @@ static int mchp_sparx5_probe(struct platform_device *pdev) /* Default values, some from DT */ sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT; + sparx5->debugfs_root = debugfs_create_dir("sparx5", NULL); + ports = of_get_child_by_name(np, "ethernet-ports"); if (!ports) { dev_err(sparx5->dev, "no ethernet-ports child node found\n"); @@ -900,6 +910,7 @@ static int mchp_sparx5_remove(struct platform_device *pdev) { struct sparx5 *sparx5 = platform_get_drvdata(pdev); + debugfs_remove_recursive(sparx5->debugfs_root); if (sparx5->xtr_irq) { disable_irq(sparx5->xtr_irq); sparx5->xtr_irq = -ENXIO; @@ -911,6 +922,7 @@ static int mchp_sparx5_remove(struct platform_device *pdev) sparx5_ptp_deinit(sparx5); sparx5_fdma_stop(sparx5); sparx5_cleanup_ports(sparx5); + sparx5_vcap_destroy(sparx5); /* Unregister netdevs */ sparx5_unregister_notifier_blocks(sparx5); destroy_workqueue(sparx5->mact_queue); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 7a83222caa73..4a574cdcb584 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -17,6 +17,7 @@ #include <linux/net_tstamp.h> #include <linux/ptp_clock_kernel.h> #include <linux/hrtimer.h> +#include <linux/debugfs.h> #include "sparx5_main_regs.h" @@ -288,8 +289,12 @@ struct sparx5 { struct mutex ptp_lock; /* lock for ptp interface state */ u16 ptp_skbs; int ptp_irq; + /* VCAP */ + struct vcap_control *vcap_ctrl; /* PGID allocation map */ u8 pgid_map[PGID_TABLE_SIZE]; + /* Common root for debugfs */ + struct dentry *debugfs_root; }; /* sparx5_switchdev.c */ @@ -357,6 +362,16 @@ int sparx5_config_dsm_calendar(struct sparx5 *sparx5); void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats); int sparx_stats_init(struct sparx5 *sparx5); +/* sparx5_dcb.c */ +#ifdef CONFIG_SPARX5_DCB +int sparx5_dcb_init(struct sparx5 *sparx5); +#else +static inline int sparx5_dcb_init(struct sparx5 *sparx5) +{ + return 0; +} +#endif + /* sparx5_netdev.c */ void sparx5_set_port_ifh_timestamp(void *ifh_hdr, u64 timestamp); void sparx5_set_port_ifh_rew_op(void *ifh_hdr, u32 rew_op); @@ -382,6 +397,10 @@ void sparx5_ptp_txtstamp_release(struct sparx5_port *port, struct sk_buff *skb); irqreturn_t sparx5_ptp_irq_handler(int irq, void *args); +/* sparx5_vcap_impl.c */ +int sparx5_vcap_init(struct sparx5 *sparx5); +void sparx5_vcap_destroy(struct sparx5 *sparx5); + /* sparx5_pgid.c */ enum sparx5_pgid_type { SPX5_PGID_FREE, @@ -418,6 +437,7 @@ static inline bool sparx5_is_baser(phy_interface_t interface) extern const struct phylink_mac_ops sparx5_phylink_mac_ops; extern const struct phylink_pcs_ops sparx5_phylink_pcs_ops; extern const struct ethtool_ops sparx5_ethtool_ops; +extern const struct dcbnl_rtnl_ops sparx5_dcbnl_ops; /* Calculate raw offset */ static inline __pure int spx5_offset(int id, int tinst, int tcnt, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h index fa2eb70f487a..6c93dd6b01b0 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h @@ -4,8 +4,8 @@ * Copyright (c) 2021 Microchip Technology Inc. */ -/* This file is autogenerated by cml-utils 2022-02-26 14:15:01 +0100. - * Commit ID: 98bdd3d171cc2a1afd30d241d41a4281d471a48c (dirty) +/* This file is autogenerated by cml-utils 2022-09-28 11:17:02 +0200. + * Commit ID: 385c8a11d71a9f6a60368d3a3cb648fa257b479a */ #ifndef _SPARX5_MAIN_REGS_H_ @@ -171,6 +171,162 @@ enum sparx5_target { /* ANA_AC:STAT_CNT_CFG_PORT:STAT_LSB_CNT */ #define ANA_AC_PORT_STAT_LSB_CNT(g, r) __REG(TARGET_ANA_AC, 0, 1, 843776, g, 70, 64, 20, r, 4, 4) +/* ANA_ACL:COMMON:VCAP_S2_CFG */ +#define ANA_ACL_VCAP_S2_CFG(r) __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 0, r, 70, 4) + +#define ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA BIT(28) +#define ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_ROUTE_HANDLING_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA GENMASK(27, 26) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_OAM_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA GENMASK(25, 24) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_OTHER_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA GENMASK(23, 22) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_VID_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA GENMASK(21, 20) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_STD_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA GENMASK(19, 18) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP6_TCPUDP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA GENMASK(17, 16) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP_7TUPLE_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA GENMASK(15, 14) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_VID_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA GENMASK(13, 12) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_TCPUDP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA GENMASK(11, 10) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_IP4_OTHER_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA GENMASK(9, 8) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_ARP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA GENMASK(7, 6) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_SNAP_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA GENMASK(5, 4) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_TYPE_MAC_LLC_ENA, x) + +#define ANA_ACL_VCAP_S2_CFG_SEC_ENA GENMASK(3, 0) +#define ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_CFG_SEC_ENA, x) +#define ANA_ACL_VCAP_S2_CFG_SEC_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_CFG_SEC_ENA, x) + +/* ANA_ACL:COMMON:SWAP_IP_CTRL */ +#define ANA_ACL_SWAP_IP_CTRL __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 412, 0, 1, 4) + +#define ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL GENMASK(23, 18) +#define ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL, x) +#define ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_DMAC_REPL_OFFSET_VAL, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL GENMASK(17, 10) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_VAL, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL GENMASK(9, 2) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_VAL, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA BIT(1) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP6_HOPC_ENA, x) + +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA BIT(0) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA, x) +#define ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA_GET(x)\ + FIELD_GET(ANA_ACL_SWAP_IP_CTRL_IP_SWAP_IP4_TTL_ENA, x) + +/* ANA_ACL:COMMON:VCAP_S2_RLEG_STAT */ +#define ANA_ACL_VCAP_S2_RLEG_STAT(r) __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 424, r, 4, 4) + +#define ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK GENMASK(12, 6) +#define ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK, x) +#define ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_RLEG_STAT_IRLEG_STAT_MASK, x) + +#define ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK GENMASK(5, 0) +#define ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK, x) +#define ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_RLEG_STAT_ERLEG_STAT_MASK, x) + +/* ANA_ACL:COMMON:VCAP_S2_FRAGMENT_CFG */ +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 440, 0, 1, 4) + +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN GENMASK(9, 5) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN, x) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_FRAGMENT_CFG_L4_MIN_LEN, x) + +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS BIT(4) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS, x) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_DIS, x) + +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES GENMASK(3, 0) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES, x) +#define ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_FRAGMENT_CFG_FRAGMENT_OFFSET_THRES, x) + /* ANA_ACL:COMMON:OWN_UPSID */ #define ANA_ACL_OWN_UPSID(r) __REG(TARGET_ANA_ACL, 0, 1, 32768, 0, 1, 592, 580, r, 3, 4) @@ -180,6 +336,174 @@ enum sparx5_target { #define ANA_ACL_OWN_UPSID_OWN_UPSID_GET(x)\ FIELD_GET(ANA_ACL_OWN_UPSID_OWN_UPSID, x) +/* ANA_ACL:KEY_SEL:VCAP_S2_KEY_SEL */ +#define ANA_ACL_VCAP_S2_KEY_SEL(g, r) __REG(TARGET_ANA_ACL, 0, 1, 34200, g, 134, 16, 0, r, 4, 4) + +#define ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA BIT(13) +#define ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL BIT(12) +#define ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IGR_PORT_MASK_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL GENMASK(11, 10) +#define ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL GENMASK(9, 8) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL GENMASK(7, 6) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL GENMASK(5, 3) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL GENMASK(2, 1) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL, x) + +#define ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL BIT(0) +#define ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(x)\ + FIELD_PREP(ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL, x) +#define ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(x)\ + FIELD_GET(ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL, x) + +/* ANA_ACL:CNT_A:CNT_A */ +#define ANA_ACL_CNT_A(g) __REG(TARGET_ANA_ACL, 0, 1, 0, g, 4096, 4, 0, 0, 1, 4) + +/* ANA_ACL:CNT_B:CNT_B */ +#define ANA_ACL_CNT_B(g) __REG(TARGET_ANA_ACL, 0, 1, 16384, g, 4096, 4, 0, 0, 1, 4) + +/* ANA_ACL:STICKY:SEC_LOOKUP_STICKY */ +#define ANA_ACL_SEC_LOOKUP_STICKY(r) __REG(TARGET_ANA_ACL, 0, 1, 36408, 0, 1, 16, 0, r, 4, 4) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY BIT(17) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY BIT(16) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY BIT(15) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY BIT(14) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY BIT(13) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY BIT(12) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY BIT(11) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY BIT(10) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY BIT(9) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY BIT(8) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY BIT(7) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY BIT(6) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY BIT(5) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY BIT(4) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY BIT(3) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY BIT(2) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY BIT(1) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY, x) + +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY BIT(0) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY_SET(x)\ + FIELD_PREP(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY, x) +#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY_GET(x)\ + FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY, x) + /* ANA_AC_POL:POL_ALL_CFG:POL_UPD_INT_CFG */ #define ANA_AC_POL_POL_UPD_INT_CFG __REG(TARGET_ANA_AC_POL, 0, 1, 75968, 0, 1, 1160, 1148, 0, 1, 4) @@ -426,6 +750,96 @@ enum sparx5_target { #define ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT_GET(x)\ FIELD_GET(ANA_CL_VLAN_CTRL_2_VLAN_PUSH_CNT, x) +/* ANA_CL:PORT:PCP_DEI_MAP_CFG */ +#define ANA_CL_PCP_DEI_MAP_CFG(g, r) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 108, r, 16, 4) + +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL GENMASK(4, 3) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL_SET(x)\ + FIELD_PREP(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL, x) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL_GET(x)\ + FIELD_GET(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL, x) + +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL GENMASK(2, 0) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL_SET(x)\ + FIELD_PREP(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL, x) +#define ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL_GET(x)\ + FIELD_GET(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL, x) + +/* ANA_CL:PORT:QOS_CFG */ +#define ANA_CL_QOS_CFG(g) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 172, 0, 1, 4) + +#define ANA_CL_QOS_CFG_DEFAULT_COSID_ENA BIT(17) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_COSID_ENA, x) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_COSID_ENA, x) + +#define ANA_CL_QOS_CFG_DEFAULT_COSID_VAL GENMASK(16, 14) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_VAL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_COSID_VAL, x) +#define ANA_CL_QOS_CFG_DEFAULT_COSID_VAL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_COSID_VAL, x) + +#define ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL GENMASK(13, 12) +#define ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL, x) +#define ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_REWR_MODE_SEL, x) + +#define ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA BIT(11) +#define ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_TRANSLATE_ENA, x) + +#define ANA_CL_QOS_CFG_DSCP_KEEP_ENA BIT(10) +#define ANA_CL_QOS_CFG_DSCP_KEEP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_KEEP_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_KEEP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_KEEP_ENA, x) + +#define ANA_CL_QOS_CFG_KEEP_ENA BIT(9) +#define ANA_CL_QOS_CFG_KEEP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_KEEP_ENA, x) +#define ANA_CL_QOS_CFG_KEEP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_KEEP_ENA, x) + +#define ANA_CL_QOS_CFG_PCP_DEI_DP_ENA BIT(8) +#define ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_PCP_DEI_DP_ENA, x) +#define ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_PCP_DEI_DP_ENA, x) + +#define ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA BIT(7) +#define ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA, x) +#define ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA, x) + +#define ANA_CL_QOS_CFG_DSCP_DP_ENA BIT(6) +#define ANA_CL_QOS_CFG_DSCP_DP_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_DP_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_DP_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_DP_ENA, x) + +#define ANA_CL_QOS_CFG_DSCP_QOS_ENA BIT(5) +#define ANA_CL_QOS_CFG_DSCP_QOS_ENA_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DSCP_QOS_ENA, x) +#define ANA_CL_QOS_CFG_DSCP_QOS_ENA_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DSCP_QOS_ENA, x) + +#define ANA_CL_QOS_CFG_DEFAULT_DP_VAL GENMASK(4, 3) +#define ANA_CL_QOS_CFG_DEFAULT_DP_VAL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_DP_VAL, x) +#define ANA_CL_QOS_CFG_DEFAULT_DP_VAL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_DP_VAL, x) + +#define ANA_CL_QOS_CFG_DEFAULT_QOS_VAL GENMASK(2, 0) +#define ANA_CL_QOS_CFG_DEFAULT_QOS_VAL_SET(x)\ + FIELD_PREP(ANA_CL_QOS_CFG_DEFAULT_QOS_VAL, x) +#define ANA_CL_QOS_CFG_DEFAULT_QOS_VAL_GET(x)\ + FIELD_GET(ANA_CL_QOS_CFG_DEFAULT_QOS_VAL, x) + /* ANA_CL:PORT:CAPTURE_BPDU_CFG */ #define ANA_CL_CAPTURE_BPDU_CFG(g) __REG(TARGET_ANA_CL, 0, 1, 131072, g, 70, 512, 196, 0, 1, 4) @@ -438,6 +852,39 @@ enum sparx5_target { #define ANA_CL_OWN_UPSID_OWN_UPSID_GET(x)\ FIELD_GET(ANA_CL_OWN_UPSID_OWN_UPSID, x) +/* ANA_CL:COMMON:DSCP_CFG */ +#define ANA_CL_DSCP_CFG(r) __REG(TARGET_ANA_CL, 0, 1, 166912, 0, 1, 756, 256, r, 64, 4) + +#define ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL GENMASK(12, 7) +#define ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL, x) +#define ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_TRANSLATE_VAL, x) + +#define ANA_CL_DSCP_CFG_DSCP_QOS_VAL GENMASK(6, 4) +#define ANA_CL_DSCP_CFG_DSCP_QOS_VAL_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_QOS_VAL, x) +#define ANA_CL_DSCP_CFG_DSCP_QOS_VAL_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_QOS_VAL, x) + +#define ANA_CL_DSCP_CFG_DSCP_DP_VAL GENMASK(3, 2) +#define ANA_CL_DSCP_CFG_DSCP_DP_VAL_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_DP_VAL, x) +#define ANA_CL_DSCP_CFG_DSCP_DP_VAL_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_DP_VAL, x) + +#define ANA_CL_DSCP_CFG_DSCP_REWR_ENA BIT(1) +#define ANA_CL_DSCP_CFG_DSCP_REWR_ENA_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_REWR_ENA, x) +#define ANA_CL_DSCP_CFG_DSCP_REWR_ENA_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_REWR_ENA, x) + +#define ANA_CL_DSCP_CFG_DSCP_TRUST_ENA BIT(0) +#define ANA_CL_DSCP_CFG_DSCP_TRUST_ENA_SET(x)\ + FIELD_PREP(ANA_CL_DSCP_CFG_DSCP_TRUST_ENA, x) +#define ANA_CL_DSCP_CFG_DSCP_TRUST_ENA_GET(x)\ + FIELD_GET(ANA_CL_DSCP_CFG_DSCP_TRUST_ENA, x) + /* ANA_L2:COMMON:AUTO_LRN_CFG */ #define ANA_L2_AUTO_LRN_CFG __REG(TARGET_ANA_L2, 0, 1, 566024, 0, 1, 700, 24, 0, 1, 4) @@ -5039,6 +5486,138 @@ enum sparx5_target { #define REW_RAM_INIT_RAM_CFG_HOOK_GET(x)\ FIELD_GET(REW_RAM_INIT_RAM_CFG_HOOK, x) +/* VCAP_SUPER:VCAP_CORE_CFG:VCAP_UPDATE_CTRL */ +#define VCAP_SUPER_CTRL __REG(TARGET_VCAP_SUPER, 0, 1, 0, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_SUPER_CTRL_UPDATE_CMD GENMASK(24, 22) +#define VCAP_SUPER_CTRL_UPDATE_CMD_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_CMD, x) +#define VCAP_SUPER_CTRL_UPDATE_CMD_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_CMD, x) + +#define VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS BIT(21) +#define VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS, x) +#define VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS, x) + +#define VCAP_SUPER_CTRL_UPDATE_ACTION_DIS BIT(20) +#define VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_ACTION_DIS, x) +#define VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_ACTION_DIS, x) + +#define VCAP_SUPER_CTRL_UPDATE_CNT_DIS BIT(19) +#define VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_CNT_DIS, x) +#define VCAP_SUPER_CTRL_UPDATE_CNT_DIS_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_CNT_DIS, x) + +#define VCAP_SUPER_CTRL_UPDATE_ADDR GENMASK(18, 3) +#define VCAP_SUPER_CTRL_UPDATE_ADDR_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_ADDR, x) +#define VCAP_SUPER_CTRL_UPDATE_ADDR_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_ADDR, x) + +#define VCAP_SUPER_CTRL_UPDATE_SHOT BIT(2) +#define VCAP_SUPER_CTRL_UPDATE_SHOT_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_UPDATE_SHOT, x) +#define VCAP_SUPER_CTRL_UPDATE_SHOT_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_UPDATE_SHOT, x) + +#define VCAP_SUPER_CTRL_CLEAR_CACHE BIT(1) +#define VCAP_SUPER_CTRL_CLEAR_CACHE_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_CLEAR_CACHE, x) +#define VCAP_SUPER_CTRL_CLEAR_CACHE_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_CLEAR_CACHE, x) + +#define VCAP_SUPER_CTRL_MV_TRAFFIC_IGN BIT(0) +#define VCAP_SUPER_CTRL_MV_TRAFFIC_IGN_SET(x)\ + FIELD_PREP(VCAP_SUPER_CTRL_MV_TRAFFIC_IGN, x) +#define VCAP_SUPER_CTRL_MV_TRAFFIC_IGN_GET(x)\ + FIELD_GET(VCAP_SUPER_CTRL_MV_TRAFFIC_IGN, x) + +/* VCAP_SUPER:VCAP_CORE_CFG:VCAP_MV_CFG */ +#define VCAP_SUPER_CFG __REG(TARGET_VCAP_SUPER, 0, 1, 0, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_SUPER_CFG_MV_NUM_POS GENMASK(31, 16) +#define VCAP_SUPER_CFG_MV_NUM_POS_SET(x)\ + FIELD_PREP(VCAP_SUPER_CFG_MV_NUM_POS, x) +#define VCAP_SUPER_CFG_MV_NUM_POS_GET(x)\ + FIELD_GET(VCAP_SUPER_CFG_MV_NUM_POS, x) + +#define VCAP_SUPER_CFG_MV_SIZE GENMASK(15, 0) +#define VCAP_SUPER_CFG_MV_SIZE_SET(x)\ + FIELD_PREP(VCAP_SUPER_CFG_MV_SIZE, x) +#define VCAP_SUPER_CFG_MV_SIZE_GET(x)\ + FIELD_GET(VCAP_SUPER_CFG_MV_SIZE, x) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_ENTRY_DAT */ +#define VCAP_SUPER_VCAP_ENTRY_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 0, r, 64, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_MASK_DAT */ +#define VCAP_SUPER_VCAP_MASK_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 256, r, 64, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_ACTION_DAT */ +#define VCAP_SUPER_VCAP_ACTION_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 512, r, 64, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_CNT_DAT */ +#define VCAP_SUPER_VCAP_CNT_DAT(r) __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 768, r, 32, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_CNT_FW_DAT */ +#define VCAP_SUPER_VCAP_CNT_FW_DAT __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 896, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CORE_CACHE:VCAP_TG_DAT */ +#define VCAP_SUPER_VCAP_TG_DAT __REG(TARGET_VCAP_SUPER, 0, 1, 8, 0, 1, 904, 900, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CORE_MAP:VCAP_CORE_IDX */ +#define VCAP_SUPER_IDX __REG(TARGET_VCAP_SUPER, 0, 1, 912, 0, 1, 8, 0, 0, 1, 4) + +#define VCAP_SUPER_IDX_CORE_IDX GENMASK(3, 0) +#define VCAP_SUPER_IDX_CORE_IDX_SET(x)\ + FIELD_PREP(VCAP_SUPER_IDX_CORE_IDX, x) +#define VCAP_SUPER_IDX_CORE_IDX_GET(x)\ + FIELD_GET(VCAP_SUPER_IDX_CORE_IDX, x) + +/* VCAP_SUPER:VCAP_CORE_MAP:VCAP_CORE_MAP */ +#define VCAP_SUPER_MAP __REG(TARGET_VCAP_SUPER, 0, 1, 912, 0, 1, 8, 4, 0, 1, 4) + +#define VCAP_SUPER_MAP_CORE_MAP GENMASK(2, 0) +#define VCAP_SUPER_MAP_CORE_MAP_SET(x)\ + FIELD_PREP(VCAP_SUPER_MAP_CORE_MAP, x) +#define VCAP_SUPER_MAP_CORE_MAP_GET(x)\ + FIELD_GET(VCAP_SUPER_MAP_CORE_MAP, x) + +/* VCAP_SUPER:VCAP_CONST:VCAP_VER */ +#define VCAP_SUPER_VCAP_VER __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 0, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_WIDTH */ +#define VCAP_SUPER_ENTRY_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 4, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_CNT */ +#define VCAP_SUPER_ENTRY_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 8, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_SWCNT */ +#define VCAP_SUPER_ENTRY_SWCNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 12, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ENTRY_TG_WIDTH */ +#define VCAP_SUPER_ENTRY_TG_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 16, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ACTION_DEF_CNT */ +#define VCAP_SUPER_ACTION_DEF_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 20, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:ACTION_WIDTH */ +#define VCAP_SUPER_ACTION_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 24, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:CNT_WIDTH */ +#define VCAP_SUPER_CNT_WIDTH __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 28, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:CORE_CNT */ +#define VCAP_SUPER_CORE_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 32, 0, 1, 4) + +/* VCAP_SUPER:VCAP_CONST:IF_CNT */ +#define VCAP_SUPER_IF_CNT __REG(TARGET_VCAP_SUPER, 0, 1, 924, 0, 1, 40, 36, 0, 1, 4) + /* VCAP_SUPER:RAM_CTRL:RAM_INIT */ #define VCAP_SUPER_RAM_INIT __REG(TARGET_VCAP_SUPER, 0, 1, 1120, 0, 1, 4, 0, 0, 1, 4) diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c index 830da0e5ff27..bb97d27a1da4 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c @@ -138,7 +138,6 @@ const struct phylink_pcs_ops sparx5_phylink_pcs_ops = { }; const struct phylink_mac_ops sparx5_phylink_mac_ops = { - .validate = phylink_generic_validate, .mac_select_pcs = sparx5_phylink_mac_select_pcs, .mac_config = sparx5_phylink_mac_config, .mac_link_down = sparx5_phylink_mac_link_down, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 32709d21ab2f..107b9cd931c0 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -6,6 +6,7 @@ #include <linux/module.h> #include <linux/phy/phy.h> +#include <net/dcbnl.h> #include "sparx5_main_regs.h" #include "sparx5_main.h" @@ -1144,3 +1145,101 @@ void sparx5_port_enable(struct sparx5_port *port, bool enable) sparx5, QFWD_SWITCH_PORT_MODE(port->portno)); } + +int sparx5_port_qos_set(struct sparx5_port *port, + struct sparx5_port_qos *qos) +{ + sparx5_port_qos_dscp_set(port, &qos->dscp); + sparx5_port_qos_pcp_set(port, &qos->pcp); + sparx5_port_qos_default_set(port, qos); + + return 0; +} + +int sparx5_port_qos_pcp_set(const struct sparx5_port *port, + struct sparx5_port_qos_pcp *qos) +{ + struct sparx5 *sparx5 = port->sparx5; + u8 *pcp_itr = qos->map.map; + u8 pcp, dp; + int i; + + /* Enable/disable pcp and dp for qos classification. */ + spx5_rmw(ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA_SET(qos->qos_enable) | + ANA_CL_QOS_CFG_PCP_DEI_DP_ENA_SET(qos->dp_enable), + ANA_CL_QOS_CFG_PCP_DEI_QOS_ENA | ANA_CL_QOS_CFG_PCP_DEI_DP_ENA, + sparx5, ANA_CL_QOS_CFG(port->portno)); + + /* Map each pcp and dei value to priority and dp */ + for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) { + pcp = *(pcp_itr + i); + dp = (i < SPARX5_PORT_QOS_PCP_COUNT) ? 0 : 1; + spx5_rmw(ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL_SET(pcp) | + ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL_SET(dp), + ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_QOS_VAL | + ANA_CL_PCP_DEI_MAP_CFG_PCP_DEI_DP_VAL, sparx5, + ANA_CL_PCP_DEI_MAP_CFG(port->portno, i)); + } + + return 0; +} + +int sparx5_port_qos_dscp_set(const struct sparx5_port *port, + struct sparx5_port_qos_dscp *qos) +{ + struct sparx5 *sparx5 = port->sparx5; + u8 *dscp = qos->map.map; + int i; + + /* Enable/disable dscp and dp for qos classification. + * Disable rewrite of dscp values for now. + */ + spx5_rmw(ANA_CL_QOS_CFG_DSCP_QOS_ENA_SET(qos->qos_enable) | + ANA_CL_QOS_CFG_DSCP_DP_ENA_SET(qos->dp_enable) | + ANA_CL_QOS_CFG_DSCP_KEEP_ENA_SET(1), + ANA_CL_QOS_CFG_DSCP_QOS_ENA | ANA_CL_QOS_CFG_DSCP_DP_ENA | + ANA_CL_QOS_CFG_DSCP_KEEP_ENA, sparx5, + ANA_CL_QOS_CFG(port->portno)); + + /* Map each dscp value to priority and dp */ + for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) { + spx5_rmw(ANA_CL_DSCP_CFG_DSCP_QOS_VAL_SET(*(dscp + i)) | + ANA_CL_DSCP_CFG_DSCP_DP_VAL_SET(0), + ANA_CL_DSCP_CFG_DSCP_QOS_VAL | + ANA_CL_DSCP_CFG_DSCP_DP_VAL, sparx5, + ANA_CL_DSCP_CFG(i)); + } + + /* Set per-dscp trust */ + for (i = 0; i < ARRAY_SIZE(qos->map.map); i++) { + if (qos->qos_enable) { + spx5_rmw(ANA_CL_DSCP_CFG_DSCP_TRUST_ENA_SET(1), + ANA_CL_DSCP_CFG_DSCP_TRUST_ENA, sparx5, + ANA_CL_DSCP_CFG(i)); + } + } + + return 0; +} + +int sparx5_port_qos_default_set(const struct sparx5_port *port, + const struct sparx5_port_qos *qos) +{ + struct sparx5 *sparx5 = port->sparx5; + + /* Set default prio and dp level */ + spx5_rmw(ANA_CL_QOS_CFG_DEFAULT_QOS_VAL_SET(qos->default_prio) | + ANA_CL_QOS_CFG_DEFAULT_DP_VAL_SET(0), + ANA_CL_QOS_CFG_DEFAULT_QOS_VAL | + ANA_CL_QOS_CFG_DEFAULT_DP_VAL, + sparx5, ANA_CL_QOS_CFG(port->portno)); + + /* Set default pcp and dei for untagged frames */ + spx5_rmw(ANA_CL_VLAN_CTRL_PORT_PCP_SET(0) | + ANA_CL_VLAN_CTRL_PORT_DEI_SET(0), + ANA_CL_VLAN_CTRL_PORT_PCP | + ANA_CL_VLAN_CTRL_PORT_DEI, + sparx5, ANA_CL_VLAN_CTRL(port->portno)); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h index 2f8043eac71b..fbafe22e25cc 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.h @@ -91,4 +91,46 @@ int sparx5_get_port_status(struct sparx5 *sparx5, void sparx5_port_enable(struct sparx5_port *port, bool enable); int sparx5_port_fwd_urg(struct sparx5 *sparx5, u32 speed); +#define SPARX5_PORT_QOS_PCP_COUNT 8 +#define SPARX5_PORT_QOS_DEI_COUNT 8 +#define SPARX5_PORT_QOS_PCP_DEI_COUNT \ + (SPARX5_PORT_QOS_PCP_COUNT + SPARX5_PORT_QOS_DEI_COUNT) +struct sparx5_port_qos_pcp_map { + u8 map[SPARX5_PORT_QOS_PCP_DEI_COUNT]; +}; + +#define SPARX5_PORT_QOS_DSCP_COUNT 64 +struct sparx5_port_qos_dscp_map { + u8 map[SPARX5_PORT_QOS_DSCP_COUNT]; +}; + +struct sparx5_port_qos_pcp { + struct sparx5_port_qos_pcp_map map; + bool qos_enable; + bool dp_enable; +}; + +struct sparx5_port_qos_dscp { + struct sparx5_port_qos_dscp_map map; + bool qos_enable; + bool dp_enable; +}; + +struct sparx5_port_qos { + struct sparx5_port_qos_pcp pcp; + struct sparx5_port_qos_dscp dscp; + u8 default_prio; +}; + +int sparx5_port_qos_set(struct sparx5_port *port, struct sparx5_port_qos *qos); + +int sparx5_port_qos_pcp_set(const struct sparx5_port *port, + struct sparx5_port_qos_pcp *qos); + +int sparx5_port_qos_dscp_set(const struct sparx5_port *port, + struct sparx5_port_qos_dscp *qos); + +int sparx5_port_qos_default_set(const struct sparx5_port *port, + const struct sparx5_port_qos *qos); + #endif /* __SPARX5_PORT_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c index 1e79d0ef0cb8..379e540e5e6a 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c @@ -389,6 +389,10 @@ int sparx5_qos_init(struct sparx5 *sparx5) if (ret < 0) return ret; + ret = sparx5_dcb_init(sparx5); + if (ret < 0) + return ret; + return 0; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c index dc2c3756e3a2..205246b5af82 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c @@ -10,6 +10,55 @@ #include "sparx5_main.h" #include "sparx5_qos.h" +/* tc block handling */ +static LIST_HEAD(sparx5_block_cb_list); + +static int sparx5_tc_block_cb(enum tc_setup_type type, + void *type_data, + void *cb_priv, bool ingress) +{ + struct net_device *ndev = cb_priv; + + switch (type) { + case TC_SETUP_CLSMATCHALL: + return sparx5_tc_matchall(ndev, type_data, ingress); + case TC_SETUP_CLSFLOWER: + return sparx5_tc_flower(ndev, type_data, ingress); + default: + return -EOPNOTSUPP; + } +} + +static int sparx5_tc_block_cb_ingress(enum tc_setup_type type, + void *type_data, + void *cb_priv) +{ + return sparx5_tc_block_cb(type, type_data, cb_priv, true); +} + +static int sparx5_tc_block_cb_egress(enum tc_setup_type type, + void *type_data, + void *cb_priv) +{ + return sparx5_tc_block_cb(type, type_data, cb_priv, false); +} + +static int sparx5_tc_setup_block(struct net_device *ndev, + struct flow_block_offload *fbo) +{ + flow_setup_cb_t *cb; + + if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) + cb = sparx5_tc_block_cb_ingress; + else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) + cb = sparx5_tc_block_cb_egress; + else + return -EOPNOTSUPP; + + return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list, + cb, ndev, ndev, false); +} + static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer, u32 *idx) { @@ -108,6 +157,8 @@ int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data) { switch (type) { + case TC_SETUP_BLOCK: + return sparx5_tc_setup_block(ndev, type_data); case TC_SETUP_QDISC_MQPRIO: return sparx5_tc_setup_qdisc_mqprio(ndev, type_data); case TC_SETUP_QDISC_TBF: diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h index 5b55e11b77e1..adab88e6b21f 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.h @@ -7,9 +7,28 @@ #ifndef __SPARX5_TC_H__ #define __SPARX5_TC_H__ +#include <net/flow_offload.h> +#include <net/pkt_cls.h> #include <linux/netdevice.h> +/* Controls how PORT_MASK is applied */ +enum SPX5_PORT_MASK_MODE { + SPX5_PMM_OR_DSTMASK, + SPX5_PMM_AND_VLANMASK, + SPX5_PMM_REPLACE_PGID, + SPX5_PMM_REPLACE_ALL, + SPX5_PMM_REDIR_PGID, + SPX5_PMM_OR_PGID_MASK, +}; + int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type, void *type_data); +int sparx5_tc_matchall(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress); + +int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, + bool ingress); + #endif /* __SPARX5_TC_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c new file mode 100644 index 000000000000..1ed304a816cc --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c @@ -0,0 +1,1016 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include <net/tcp.h> + +#include "sparx5_tc.h" +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "sparx5_main.h" +#include "sparx5_vcap_impl.h" + +#define SPX5_MAX_RULE_SIZE 13 /* allows X1, X2, X4, X6 and X12 rules */ + +/* Collect keysets and type ids for multiple rules per size */ +struct sparx5_wildcard_rule { + bool selected; + u8 value; + u8 mask; + enum vcap_keyfield_set keyset; +}; + +struct sparx5_multiple_rules { + struct sparx5_wildcard_rule rule[SPX5_MAX_RULE_SIZE]; +}; + +struct sparx5_tc_flower_parse_usage { + struct flow_cls_offload *fco; + struct flow_rule *frule; + struct vcap_rule *vrule; + u16 l3_proto; + u8 l4_proto; + unsigned int used_keys; +}; + +struct sparx5_tc_rule_pkt_cnt { + u64 cookie; + u32 pkts; +}; + +/* These protocols have dedicated keysets in IS2 and a TC dissector + * ETH_P_ARP does not have a TC dissector + */ +static u16 sparx5_tc_known_etypes[] = { + ETH_P_ALL, + ETH_P_ARP, + ETH_P_IP, + ETH_P_IPV6, +}; + +enum sparx5_is2_arp_opcode { + SPX5_IS2_ARP_REQUEST, + SPX5_IS2_ARP_REPLY, + SPX5_IS2_RARP_REQUEST, + SPX5_IS2_RARP_REPLY, +}; + +enum tc_arp_opcode { + TC_ARP_OP_RESERVED, + TC_ARP_OP_REQUEST, + TC_ARP_OP_REPLY, +}; + +static bool sparx5_tc_is_known_etype(u16 etype) +{ + int idx; + + /* For now this only knows about IS2 traffic classification */ + for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_known_etypes); ++idx) + if (sparx5_tc_known_etypes[idx] == etype) + return true; + + return false; +} + +static int sparx5_tc_flower_handler_ethaddr_usage(struct sparx5_tc_flower_parse_usage *st) +{ + enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; + enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; + struct flow_match_eth_addrs match; + struct vcap_u48_key smac, dmac; + int err = 0; + + flow_rule_match_eth_addrs(st->frule, &match); + + if (!is_zero_ether_addr(match.mask->src)) { + vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); + vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); + if (err) + goto out; + } + + if (!is_zero_ether_addr(match.mask->dst)) { + vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); + vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); + err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_ipv4_usage(struct sparx5_tc_flower_parse_usage *st) +{ + int err = 0; + + if (st->l3_proto == ETH_P_IP) { + struct flow_match_ipv4_addrs mt; + + flow_rule_match_ipv4_addrs(st->frule, &mt); + if (mt.mask->src) { + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_IP4_SIP, + be32_to_cpu(mt.key->src), + be32_to_cpu(mt.mask->src)); + if (err) + goto out; + } + if (mt.mask->dst) { + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_IP4_DIP, + be32_to_cpu(mt.key->dst), + be32_to_cpu(mt.mask->dst)); + if (err) + goto out; + } + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_ipv6_usage(struct sparx5_tc_flower_parse_usage *st) +{ + int err = 0; + + if (st->l3_proto == ETH_P_IPV6) { + struct flow_match_ipv6_addrs mt; + struct vcap_u128_key sip; + struct vcap_u128_key dip; + + flow_rule_match_ipv6_addrs(st->frule, &mt); + /* Check if address masks are non-zero */ + if (!ipv6_addr_any(&mt.mask->src)) { + vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16); + vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16); + err = vcap_rule_add_key_u128(st->vrule, + VCAP_KF_L3_IP6_SIP, &sip); + if (err) + goto out; + } + if (!ipv6_addr_any(&mt.mask->dst)) { + vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16); + vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16); + err = vcap_rule_add_key_u128(st->vrule, + VCAP_KF_L3_IP6_DIP, &dip); + if (err) + goto out; + } + } + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS); + return err; +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_control_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_control mt; + u32 value, mask; + int err = 0; + + flow_rule_match_control(st->frule, &mt); + + if (mt.mask->flags) { + if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) { + if (mt.key->flags & FLOW_DIS_FIRST_FRAG) { + value = 1; /* initial fragment */ + mask = 0x3; + } else { + if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { + value = 3; /* follow up fragment */ + mask = 0x3; + } else { + value = 0; /* no fragment */ + mask = 0x3; + } + } + } else { + if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { + value = 3; /* follow up fragment */ + mask = 0x3; + } else { + value = 0; /* no fragment */ + mask = 0x3; + } + } + + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_FRAGMENT_TYPE, + value, mask); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_portnum_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_ports mt; + u16 value, mask; + int err = 0; + + flow_rule_match_ports(st->frule, &mt); + + if (mt.mask->src) { + value = be16_to_cpu(mt.key->src); + mask = be16_to_cpu(mt.mask->src); + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value, + mask); + if (err) + goto out; + } + + if (mt.mask->dst) { + value = be16_to_cpu(mt.key->dst); + mask = be16_to_cpu(mt.mask->dst); + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value, + mask); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_PORTS); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_basic_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_basic mt; + int err = 0; + + flow_rule_match_basic(st->frule, &mt); + + if (mt.mask->n_proto) { + st->l3_proto = be16_to_cpu(mt.key->n_proto); + if (!sparx5_tc_is_known_etype(st->l3_proto)) { + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, + st->l3_proto, ~0); + if (err) + goto out; + } else if (st->l3_proto == ETH_P_IP) { + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, + VCAP_BIT_1); + if (err) + goto out; + } else if (st->l3_proto == ETH_P_IPV6) { + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, + VCAP_BIT_0); + if (err) + goto out; + } + } + + if (mt.mask->ip_proto) { + st->l4_proto = mt.key->ip_proto; + if (st->l4_proto == IPPROTO_TCP) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_IS, + VCAP_BIT_1); + if (err) + goto out; + } else if (st->l4_proto == IPPROTO_UDP) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_IS, + VCAP_BIT_0); + if (err) + goto out; + } else { + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_IP_PROTO, + st->l4_proto, ~0); + if (err) + goto out; + } + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_vlan_usage(struct sparx5_tc_flower_parse_usage *st) +{ + enum vcap_key_field vid_key = VCAP_KF_8021Q_VID_CLS; + enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP_CLS; + struct flow_match_vlan mt; + int err; + + flow_rule_match_vlan(st->frule, &mt); + + if (mt.mask->vlan_id) { + err = vcap_rule_add_key_u32(st->vrule, vid_key, + mt.key->vlan_id, + mt.mask->vlan_id); + if (err) + goto out; + } + + if (mt.mask->vlan_priority) { + err = vcap_rule_add_key_u32(st->vrule, pcp_key, + mt.key->vlan_priority, + mt.mask->vlan_priority); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); + + return 0; +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_tcp_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_tcp mt; + u16 tcp_flags_mask; + u16 tcp_flags_key; + enum vcap_bit val; + int err = 0; + + flow_rule_match_tcp(st->frule, &mt); + tcp_flags_key = be16_to_cpu(mt.key->flags); + tcp_flags_mask = be16_to_cpu(mt.mask->flags); + + if (tcp_flags_mask & TCPHDR_FIN) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_FIN) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_SYN) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_SYN) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_RST) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_RST) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_PSH) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_PSH) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_ACK) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_ACK) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val); + if (err) + goto out; + } + + if (tcp_flags_mask & TCPHDR_URG) { + val = VCAP_BIT_0; + if (tcp_flags_key & TCPHDR_URG) + val = VCAP_BIT_1; + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_TCP); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_arp_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_arp mt; + u16 value, mask; + u32 ipval, ipmsk; + int err; + + flow_rule_match_arp(st->frule, &mt); + + if (mt.mask->op) { + mask = 0x3; + if (st->l3_proto == ETH_P_ARP) { + value = mt.key->op == TC_ARP_OP_REQUEST ? + SPX5_IS2_ARP_REQUEST : + SPX5_IS2_ARP_REPLY; + } else { /* RARP */ + value = mt.key->op == TC_ARP_OP_REQUEST ? + SPX5_IS2_RARP_REQUEST : + SPX5_IS2_RARP_REPLY; + } + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE, + value, mask); + if (err) + goto out; + } + + /* The IS2 ARP keyset does not support ARP hardware addresses */ + if (!is_zero_ether_addr(mt.mask->sha) || + !is_zero_ether_addr(mt.mask->tha)) { + err = -EINVAL; + goto out; + } + + if (mt.mask->sip) { + ipval = be32_to_cpu((__force __be32)mt.key->sip); + ipmsk = be32_to_cpu((__force __be32)mt.mask->sip); + + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP, + ipval, ipmsk); + if (err) + goto out; + } + + if (mt.mask->tip) { + ipval = be32_to_cpu((__force __be32)mt.key->tip); + ipmsk = be32_to_cpu((__force __be32)mt.mask->tip); + + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP, + ipval, ipmsk); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ARP); + + return 0; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error"); + return err; +} + +static int +sparx5_tc_flower_handler_ip_usage(struct sparx5_tc_flower_parse_usage *st) +{ + struct flow_match_ip mt; + int err = 0; + + flow_rule_match_ip(st->frule, &mt); + + if (mt.mask->tos) { + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS, + mt.key->tos, + mt.mask->tos); + if (err) + goto out; + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_IP); + + return err; + +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error"); + return err; +} + +static int (*sparx5_tc_flower_usage_handlers[])(struct sparx5_tc_flower_parse_usage *st) = { + [FLOW_DISSECTOR_KEY_ETH_ADDRS] = sparx5_tc_flower_handler_ethaddr_usage, + [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = sparx5_tc_flower_handler_ipv4_usage, + [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = sparx5_tc_flower_handler_ipv6_usage, + [FLOW_DISSECTOR_KEY_CONTROL] = sparx5_tc_flower_handler_control_usage, + [FLOW_DISSECTOR_KEY_PORTS] = sparx5_tc_flower_handler_portnum_usage, + [FLOW_DISSECTOR_KEY_BASIC] = sparx5_tc_flower_handler_basic_usage, + [FLOW_DISSECTOR_KEY_VLAN] = sparx5_tc_flower_handler_vlan_usage, + [FLOW_DISSECTOR_KEY_TCP] = sparx5_tc_flower_handler_tcp_usage, + [FLOW_DISSECTOR_KEY_ARP] = sparx5_tc_flower_handler_arp_usage, + [FLOW_DISSECTOR_KEY_IP] = sparx5_tc_flower_handler_ip_usage, +}; + +static int sparx5_tc_use_dissectors(struct flow_cls_offload *fco, + struct vcap_admin *admin, + struct vcap_rule *vrule, + u16 *l3_proto) +{ + struct sparx5_tc_flower_parse_usage state = { + .fco = fco, + .vrule = vrule, + .l3_proto = ETH_P_ALL, + }; + int idx, err = 0; + + state.frule = flow_cls_offload_flow_rule(fco); + for (idx = 0; idx < ARRAY_SIZE(sparx5_tc_flower_usage_handlers); ++idx) { + if (!flow_rule_match_key(state.frule, idx)) + continue; + if (!sparx5_tc_flower_usage_handlers[idx]) + continue; + err = sparx5_tc_flower_usage_handlers[idx](&state); + if (err) + return err; + } + + if (state.frule->match.dissector->used_keys ^ state.used_keys) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Unsupported match item"); + return -ENOENT; + } + + if (l3_proto) + *l3_proto = state.l3_proto; + return err; +} + +static int sparx5_tc_flower_action_check(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct flow_rule *rule = flow_cls_offload_flow_rule(fco); + struct flow_action_entry *actent, *last_actent = NULL; + struct flow_action *act = &rule->action; + u64 action_mask = 0; + int idx; + + if (!flow_action_has_entries(act)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions"); + return -EINVAL; + } + + if (!flow_action_basic_hw_stats_check(act, fco->common.extack)) + return -EOPNOTSUPP; + + flow_action_for_each(idx, actent, act) { + if (action_mask & BIT(actent->id)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "More actions of the same type"); + return -EINVAL; + } + action_mask |= BIT(actent->id); + last_actent = actent; /* Save last action for later check */ + } + + /* Check that last action is a goto */ + if (last_actent->id != FLOW_ACTION_GOTO) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Last action must be 'goto'"); + return -EINVAL; + } + + /* Check if the goto chain is in the next lookup */ + if (!vcap_is_next_lookup(vctrl, fco->common.chain_index, + last_actent->chain_index)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Invalid goto chain"); + return -EINVAL; + } + + /* Catch unsupported combinations of actions */ + if (action_mask & BIT(FLOW_ACTION_TRAP) && + action_mask & BIT(FLOW_ACTION_ACCEPT)) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Cannot combine pass and trap action"); + return -EOPNOTSUPP; + } + + return 0; +} + +/* Add a rule counter action - only IS2 is considered for now */ +static int sparx5_tc_add_rule_counter(struct vcap_admin *admin, + struct vcap_rule *vrule) +{ + int err; + + err = vcap_rule_mod_action_u32(vrule, VCAP_AF_CNT_ID, vrule->id); + if (err) + return err; + + vcap_rule_set_counter_id(vrule, vrule->id); + return err; +} + +/* Collect all port keysets and apply the first of them, possibly wildcarded */ +static int sparx5_tc_select_protocol_keyset(struct net_device *ndev, + struct vcap_rule *vrule, + struct vcap_admin *admin, + u16 l3_proto, + struct sparx5_multiple_rules *multi) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_keyset_list portkeysetlist = {}; + enum vcap_keyfield_set portkeysets[10] = {}; + struct vcap_keyset_list matches = {}; + enum vcap_keyfield_set keysets[10]; + int idx, jdx, err = 0, count = 0; + struct sparx5_wildcard_rule *mru; + const struct vcap_set *kinfo; + struct vcap_control *vctrl; + + vctrl = port->sparx5->vcap_ctrl; + + /* Find the keysets that the rule can use */ + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + if (vcap_rule_find_keysets(vrule, &matches) == 0) + return -EINVAL; + + /* Find the keysets that the port configuration supports */ + portkeysetlist.max = ARRAY_SIZE(portkeysets); + portkeysetlist.keysets = portkeysets; + err = sparx5_vcap_get_port_keyset(ndev, + admin, vrule->vcap_chain_id, + l3_proto, + &portkeysetlist); + if (err) + return err; + + /* Find the intersection of the two sets of keyset */ + for (idx = 0; idx < portkeysetlist.cnt; ++idx) { + kinfo = vcap_keyfieldset(vctrl, admin->vtype, + portkeysetlist.keysets[idx]); + if (!kinfo) + continue; + + /* Find a port keyset that matches the required keys + * If there are multiple keysets then compose a type id mask + */ + for (jdx = 0; jdx < matches.cnt; ++jdx) { + if (portkeysetlist.keysets[idx] != matches.keysets[jdx]) + continue; + + mru = &multi->rule[kinfo->sw_per_item]; + if (!mru->selected) { + mru->selected = true; + mru->keyset = portkeysetlist.keysets[idx]; + mru->value = kinfo->type_id; + } + mru->value &= kinfo->type_id; + mru->mask |= kinfo->type_id; + ++count; + } + } + if (count == 0) + return -EPROTO; + + if (l3_proto == ETH_P_ALL && count < portkeysetlist.cnt) + return -ENOENT; + + for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { + mru = &multi->rule[idx]; + if (!mru->selected) + continue; + + /* Align the mask to the combined value */ + mru->mask ^= mru->value; + } + + /* Set the chosen keyset on the rule and set a wildcarded type if there + * are more than one keyset + */ + for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { + mru = &multi->rule[idx]; + if (!mru->selected) + continue; + + vcap_set_rule_set_keyset(vrule, mru->keyset); + if (count > 1) + /* Some keysets do not have a type field */ + vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, + mru->value, + ~mru->mask); + mru->selected = false; /* mark as done */ + break; /* Stop here and add more rules later */ + } + return err; +} + +static int sparx5_tc_add_rule_copy(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_rule *erule, + struct vcap_admin *admin, + struct sparx5_wildcard_rule *rule) +{ + enum vcap_key_field keylist[] = { + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_MASK_RNG, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_TYPE, + }; + struct vcap_rule *vrule; + int err; + + /* Add an extra rule with a special user and the new keyset */ + erule->user = VCAP_USER_TC_EXTRA; + vrule = vcap_copy_rule(erule); + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + /* Link the new rule to the existing rule with the cookie */ + vrule->cookie = erule->cookie; + vcap_filter_rule_keys(vrule, keylist, ARRAY_SIZE(keylist), true); + err = vcap_set_rule_set_keyset(vrule, rule->keyset); + if (err) { + pr_err("%s:%d: could not set keyset %s in rule: %u\n", + __func__, __LINE__, + vcap_keyset_name(vctrl, rule->keyset), + vrule->id); + goto out; + } + + /* Some keysets do not have a type field, so ignore return value */ + vcap_rule_mod_key_u32(vrule, VCAP_KF_TYPE, rule->value, ~rule->mask); + + err = vcap_set_rule_set_actionset(vrule, erule->actionset); + if (err) + goto out; + + err = sparx5_tc_add_rule_counter(admin, vrule); + if (err) + goto out; + + err = vcap_val_rule(vrule, ETH_P_ALL); + if (err) { + pr_err("%s:%d: could not validate rule: %u\n", + __func__, __LINE__, vrule->id); + vcap_set_tc_exterr(fco, vrule); + goto out; + } + err = vcap_add_rule(vrule); + if (err) { + pr_err("%s:%d: could not add rule: %u\n", + __func__, __LINE__, vrule->id); + goto out; + } +out: + vcap_free_rule(vrule); + return err; +} + +static int sparx5_tc_add_remaining_rules(struct vcap_control *vctrl, + struct flow_cls_offload *fco, + struct vcap_rule *erule, + struct vcap_admin *admin, + struct sparx5_multiple_rules *multi) +{ + int idx, err = 0; + + for (idx = 0; idx < SPX5_MAX_RULE_SIZE; ++idx) { + if (!multi->rule[idx].selected) + continue; + + err = sparx5_tc_add_rule_copy(vctrl, fco, erule, admin, + &multi->rule[idx]); + if (err) + break; + } + return err; +} + +static int sparx5_tc_flower_replace(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5_multiple_rules multi = {}; + struct flow_action_entry *act; + struct vcap_control *vctrl; + struct flow_rule *frule; + struct vcap_rule *vrule; + u16 l3_proto; + int err, idx; + + vctrl = port->sparx5->vcap_ctrl; + + err = sparx5_tc_flower_action_check(vctrl, fco, admin); + if (err) + return err; + + vrule = vcap_alloc_rule(vctrl, ndev, fco->common.chain_index, VCAP_USER_TC, + fco->common.prio, 0); + if (IS_ERR(vrule)) + return PTR_ERR(vrule); + + vrule->cookie = fco->cookie; + + l3_proto = ETH_P_ALL; + err = sparx5_tc_use_dissectors(fco, admin, vrule, &l3_proto); + if (err) + goto out; + + err = sparx5_tc_add_rule_counter(admin, vrule); + if (err) + goto out; + + frule = flow_cls_offload_flow_rule(fco); + flow_action_for_each(idx, act, &frule->action) { + switch (act->id) { + case FLOW_ACTION_TRAP: + err = vcap_rule_add_action_bit(vrule, + VCAP_AF_CPU_COPY_ENA, + VCAP_BIT_1); + if (err) + goto out; + err = vcap_rule_add_action_u32(vrule, + VCAP_AF_CPU_QUEUE_NUM, 0); + if (err) + goto out; + err = vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE, + SPX5_PMM_REPLACE_ALL); + if (err) + goto out; + /* For now the actionset is hardcoded */ + err = vcap_set_rule_set_actionset(vrule, + VCAP_AFS_BASE_TYPE); + if (err) + goto out; + break; + case FLOW_ACTION_ACCEPT: + /* For now the actionset is hardcoded */ + err = vcap_set_rule_set_actionset(vrule, + VCAP_AFS_BASE_TYPE); + if (err) + goto out; + break; + case FLOW_ACTION_GOTO: + /* Links between VCAPs will be added later */ + break; + default: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Unsupported TC action"); + err = -EOPNOTSUPP; + goto out; + } + } + + err = sparx5_tc_select_protocol_keyset(ndev, vrule, admin, l3_proto, + &multi); + if (err) { + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No matching port keyset for filter protocol and keys"); + goto out; + } + + /* provide the l3 protocol to guide the keyset selection */ + err = vcap_val_rule(vrule, l3_proto); + if (err) { + vcap_set_tc_exterr(fco, vrule); + goto out; + } + err = vcap_add_rule(vrule); + if (err) + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Could not add the filter"); + + if (l3_proto == ETH_P_ALL) + err = sparx5_tc_add_remaining_rules(vctrl, fco, vrule, admin, + &multi); + +out: + vcap_free_rule(vrule); + return err; +} + +static int sparx5_tc_flower_destroy(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_control *vctrl; + int err = -ENOENT, rule_id; + + vctrl = port->sparx5->vcap_ctrl; + while (true) { + rule_id = vcap_lookup_rule_by_cookie(vctrl, fco->cookie); + if (rule_id <= 0) + break; + err = vcap_del_rule(vctrl, ndev, rule_id); + if (err) { + pr_err("%s:%d: could not delete rule %d\n", + __func__, __LINE__, rule_id); + break; + } + } + return err; +} + +/* Collect packet counts from all rules with the same cookie */ +static int sparx5_tc_rule_counter_cb(void *arg, struct vcap_rule *rule) +{ + struct sparx5_tc_rule_pkt_cnt *rinfo = arg; + struct vcap_counter counter; + int err = 0; + + if (rule->cookie == rinfo->cookie) { + err = vcap_rule_get_counter(rule, &counter); + if (err) + return err; + rinfo->pkts += counter.value; + /* Reset the rule counter */ + counter.value = 0; + vcap_rule_set_counter(rule, &counter); + } + return err; +} + +static int sparx5_tc_flower_stats(struct net_device *ndev, + struct flow_cls_offload *fco, + struct vcap_admin *admin) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5_tc_rule_pkt_cnt rinfo = {}; + struct vcap_control *vctrl; + ulong lastused = 0; + u64 drops = 0; + u32 pkts = 0; + int err; + + rinfo.cookie = fco->cookie; + vctrl = port->sparx5->vcap_ctrl; + err = vcap_rule_iter(vctrl, sparx5_tc_rule_counter_cb, &rinfo); + if (err) + return err; + pkts = rinfo.pkts; + flow_stats_update(&fco->stats, 0x0, pkts, drops, lastused, + FLOW_ACTION_HW_STATS_IMMEDIATE); + return err; +} + +int sparx5_tc_flower(struct net_device *ndev, struct flow_cls_offload *fco, + bool ingress) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_control *vctrl; + struct vcap_admin *admin; + int err = -EINVAL; + + /* Get vcap instance from the chain id */ + vctrl = port->sparx5->vcap_ctrl; + admin = vcap_find_admin(vctrl, fco->common.chain_index); + if (!admin) { + NL_SET_ERR_MSG_MOD(fco->common.extack, "Invalid chain"); + return err; + } + + switch (fco->command) { + case FLOW_CLS_REPLACE: + return sparx5_tc_flower_replace(ndev, fco, admin); + case FLOW_CLS_DESTROY: + return sparx5_tc_flower_destroy(ndev, fco, admin); + case FLOW_CLS_STATS: + return sparx5_tc_flower_stats(ndev, fco, admin); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c new file mode 100644 index 000000000000..30dd61e5d150 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include "sparx5_tc.h" +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "sparx5_main_regs.h" +#include "sparx5_main.h" +#include "sparx5_vcap_impl.h" + +static int sparx5_tc_matchall_replace(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct flow_action_entry *action; + struct sparx5 *sparx5; + int err; + + if (!flow_offload_has_one_action(&tmo->rule->action)) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Only one action per filter is supported"); + return -EOPNOTSUPP; + } + action = &tmo->rule->action.entries[0]; + + sparx5 = port->sparx5; + switch (action->id) { + case FLOW_ACTION_GOTO: + err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, + action->chain_index, tmo->cookie, + true); + if (err == -EFAULT) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Unsupported goto chain"); + return -EOPNOTSUPP; + } + if (err == -EADDRINUSE) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "VCAP already enabled"); + return -EOPNOTSUPP; + } + if (err) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Could not enable VCAP lookups"); + return err; + } + break; + default: + NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action"); + return -EOPNOTSUPP; + } + return 0; +} + +static int sparx5_tc_matchall_destroy(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5; + int err; + + sparx5 = port->sparx5; + if (!tmo->rule && tmo->cookie) { + err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev, 0, + tmo->cookie, false); + if (err) + return err; + return 0; + } + NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action"); + return -EOPNOTSUPP; +} + +int sparx5_tc_matchall(struct net_device *ndev, + struct tc_cls_matchall_offload *tmo, + bool ingress) +{ + if (!tc_cls_can_offload_and_chain0(ndev, &tmo->common)) { + NL_SET_ERR_MSG_MOD(tmo->common.extack, + "Only chain zero is supported"); + return -EOPNOTSUPP; + } + + switch (tmo->command) { + case TC_CLSMATCHALL_REPLACE: + return sparx5_tc_matchall_replace(ndev, tmo, ingress); + case TC_CLSMATCHALL_DESTROY: + return sparx5_tc_matchall_destroy(ndev, tmo, ingress); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c new file mode 100644 index 000000000000..1bd987c664e8 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.c @@ -0,0 +1,1351 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +/* This file is autogenerated by cml-utils 2022-10-13 10:04:41 +0200. + * Commit ID: fd7cafd175899f0672c73afb3a30fc872500ae86 + */ + +#include <linux/types.h> +#include <linux/kernel.h> + +#include "vcap_api.h" +#include "sparx5_vcap_ag_api.h" + +/* keyfields */ +static const struct vcap_field is2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 90, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 138, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 186, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 187, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD_ETYPE] = { + .type = VCAP_FIELD_U64, + .offset = 203, + .width = 64, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 267, + .width = 16, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 283, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 284, + .width = 1, + }, +}; + +static const struct vcap_field is2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 86, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 134, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 135, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 136, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 137, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 138, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 139, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 140, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 142, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 174, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 206, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 207, + .width = 16, + }, +}; + +static const struct vcap_field is2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 169, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 186, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 202, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 220, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 221, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 222, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 223, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 224, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 225, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 226, + .width = 64, + }, +}; + +static const struct vcap_field is2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 169, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 177, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U112, + .offset = 193, + .width = 96, + }, +}; + +static const struct vcap_field is2_ip6_std_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 91, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 220, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 228, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U48, + .offset = 244, + .width = 40, + }, +}; + +static const struct vcap_field is2_ip_7tuple_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 18, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 112, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 113, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 116, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 119, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 121, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 169, + .width = 48, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 217, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 219, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 227, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 355, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 483, + .width = 1, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 484, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 485, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 486, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 502, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 518, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 534, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 535, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 536, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 537, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 538, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 539, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 540, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 541, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 542, + .width = 64, + }, +}; + +/* keyfield_set */ +static const struct vcap_set is2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 3, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 4, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 5, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_STD] = { + .type_id = 6, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP_7TUPLE] = { + .type_id = 1, + .sw_per_item = 12, + .sw_cnt = 1, + }, +}; + +/* keyfield_set map */ +static const struct vcap_field *is2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = is2_mac_etype_keyfield, + [VCAP_KFS_ARP] = is2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = is2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = is2_ip4_other_keyfield, + [VCAP_KFS_IP6_STD] = is2_ip6_std_keyfield, + [VCAP_KFS_IP_7TUPLE] = is2_ip_7tuple_keyfield, +}; + +/* keyfield_set map sizes */ +static int is2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(is2_mac_etype_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(is2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(is2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(is2_ip4_other_keyfield), + [VCAP_KFS_IP6_STD] = ARRAY_SIZE(is2_ip6_std_keyfield), + [VCAP_KFS_IP_7TUPLE] = ARRAY_SIZE(is2_ip_7tuple_keyfield), +}; + +/* actionfields */ +static const struct vcap_field is2_base_type_actionfield[] = { + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 5, + }, + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_INTR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_AF_LRN_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_AF_RT_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 6, + }, + [VCAP_AF_IGNORE_PIPELINE_CTRL] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 30, + .width = 68, + }, + [VCAP_AF_MIRROR_PROBE] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 2, + }, + [VCAP_AF_MATCH_ID] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 16, + }, + [VCAP_AF_MATCH_ID_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 16, + }, + [VCAP_AF_CNT_ID] = { + .type = VCAP_FIELD_U32, + .offset = 191, + .width = 12, + }, +}; + +/* actionfield_set */ +static const struct vcap_set is2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, +}; + +/* actionfield_set map */ +static const struct vcap_field *is2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = is2_base_type_actionfield, +}; + +/* actionfield_set map size */ +static int is2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(is2_base_type_actionfield), +}; + +/* Type Groups */ +static const struct vcap_typegroup is2_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x3_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { + [12] = is2_x12_keyfield_set_typegroups, + [6] = is2_x6_keyfield_set_typegroups, + [3] = is2_x3_keyfield_set_typegroups, + [1] = is2_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup is2_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 110, + .width = 1, + .value = 0, + }, + { + .offset = 220, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { + [3] = is2_x3_actionfield_set_typegroups, + [1] = is2_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +/* Keyfieldset names */ +static const char * const vcap_keyfield_set_names[] = { + [VCAP_KFS_NO_VALUE] = "(None)", + [VCAP_KFS_ARP] = "VCAP_KFS_ARP", + [VCAP_KFS_IP4_OTHER] = "VCAP_KFS_IP4_OTHER", + [VCAP_KFS_IP4_TCP_UDP] = "VCAP_KFS_IP4_TCP_UDP", + [VCAP_KFS_IP6_STD] = "VCAP_KFS_IP6_STD", + [VCAP_KFS_IP_7TUPLE] = "VCAP_KFS_IP_7TUPLE", + [VCAP_KFS_MAC_ETYPE] = "VCAP_KFS_MAC_ETYPE", +}; + +/* Actionfieldset names */ +static const char * const vcap_actionfield_set_names[] = { + [VCAP_AFS_NO_VALUE] = "(None)", + [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", +}; + +/* Keyfield names */ +static const char * const vcap_keyfield_names[] = { + [VCAP_KF_NO_VALUE] = "(None)", + [VCAP_KF_8021Q_DEI_CLS] = "8021Q_DEI_CLS", + [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_VID_CLS] = "8021Q_VID_CLS", + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = "8021Q_VLAN_TAGGED_IS", + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = "ARP_ADDR_SPACE_OK_IS", + [VCAP_KF_ARP_LEN_OK_IS] = "ARP_LEN_OK_IS", + [VCAP_KF_ARP_OPCODE] = "ARP_OPCODE", + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = "ARP_OPCODE_UNKNOWN_IS", + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = "ARP_PROTO_SPACE_OK_IS", + [VCAP_KF_ARP_SENDER_MATCH_IS] = "ARP_SENDER_MATCH_IS", + [VCAP_KF_ARP_TGT_MATCH_IS] = "ARP_TGT_MATCH_IS", + [VCAP_KF_ETYPE] = "ETYPE", + [VCAP_KF_ETYPE_LEN_IS] = "ETYPE_LEN_IS", + [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", + [VCAP_KF_IF_IGR_PORT_MASK_L3] = "IF_IGR_PORT_MASK_L3", + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = "IF_IGR_PORT_MASK_RNG", + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = "IF_IGR_PORT_MASK_SEL", + [VCAP_KF_IP4_IS] = "IP4_IS", + [VCAP_KF_ISDX_CLS] = "ISDX_CLS", + [VCAP_KF_ISDX_GT0_IS] = "ISDX_GT0_IS", + [VCAP_KF_L2_BC_IS] = "L2_BC_IS", + [VCAP_KF_L2_DMAC] = "L2_DMAC", + [VCAP_KF_L2_FWD_IS] = "L2_FWD_IS", + [VCAP_KF_L2_MC_IS] = "L2_MC_IS", + [VCAP_KF_L2_PAYLOAD_ETYPE] = "L2_PAYLOAD_ETYPE", + [VCAP_KF_L2_SMAC] = "L2_SMAC", + [VCAP_KF_L3_DIP_EQ_SIP_IS] = "L3_DIP_EQ_SIP_IS", + [VCAP_KF_L3_DST_IS] = "L3_DST_IS", + [VCAP_KF_L3_FRAGMENT_TYPE] = "L3_FRAGMENT_TYPE", + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = "L3_FRAG_INVLD_L4_LEN", + [VCAP_KF_L3_IP4_DIP] = "L3_IP4_DIP", + [VCAP_KF_L3_IP4_SIP] = "L3_IP4_SIP", + [VCAP_KF_L3_IP6_DIP] = "L3_IP6_DIP", + [VCAP_KF_L3_IP6_SIP] = "L3_IP6_SIP", + [VCAP_KF_L3_IP_PROTO] = "L3_IP_PROTO", + [VCAP_KF_L3_OPTIONS_IS] = "L3_OPTIONS_IS", + [VCAP_KF_L3_PAYLOAD] = "L3_PAYLOAD", + [VCAP_KF_L3_RT_IS] = "L3_RT_IS", + [VCAP_KF_L3_TOS] = "L3_TOS", + [VCAP_KF_L3_TTL_GT0] = "L3_TTL_GT0", + [VCAP_KF_L4_ACK] = "L4_ACK", + [VCAP_KF_L4_DPORT] = "L4_DPORT", + [VCAP_KF_L4_FIN] = "L4_FIN", + [VCAP_KF_L4_PAYLOAD] = "L4_PAYLOAD", + [VCAP_KF_L4_PSH] = "L4_PSH", + [VCAP_KF_L4_RNG] = "L4_RNG", + [VCAP_KF_L4_RST] = "L4_RST", + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = "L4_SEQUENCE_EQ0_IS", + [VCAP_KF_L4_SPORT] = "L4_SPORT", + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = "L4_SPORT_EQ_DPORT_IS", + [VCAP_KF_L4_SYN] = "L4_SYN", + [VCAP_KF_L4_URG] = "L4_URG", + [VCAP_KF_LOOKUP_FIRST_IS] = "LOOKUP_FIRST_IS", + [VCAP_KF_LOOKUP_PAG] = "LOOKUP_PAG", + [VCAP_KF_OAM_CCM_CNTS_EQ0] = "OAM_CCM_CNTS_EQ0", + [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_TCP_IS] = "TCP_IS", + [VCAP_KF_TCP_UDP_IS] = "TCP_UDP_IS", + [VCAP_KF_TYPE] = "TYPE", +}; + +/* Actionfield names */ +static const char * const vcap_actionfield_names[] = { + [VCAP_AF_NO_VALUE] = "(None)", + [VCAP_AF_CNT_ID] = "CNT_ID", + [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", + [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", + [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", + [VCAP_AF_IGNORE_PIPELINE_CTRL] = "IGNORE_PIPELINE_CTRL", + [VCAP_AF_INTR_ENA] = "INTR_ENA", + [VCAP_AF_LRN_DIS] = "LRN_DIS", + [VCAP_AF_MASK_MODE] = "MASK_MODE", + [VCAP_AF_MATCH_ID] = "MATCH_ID", + [VCAP_AF_MATCH_ID_MASK] = "MATCH_ID_MASK", + [VCAP_AF_MIRROR_PROBE] = "MIRROR_PROBE", + [VCAP_AF_PIPELINE_FORCE_ENA] = "PIPELINE_FORCE_ENA", + [VCAP_AF_PIPELINE_PT] = "PIPELINE_PT", + [VCAP_AF_POLICE_ENA] = "POLICE_ENA", + [VCAP_AF_POLICE_IDX] = "POLICE_IDX", + [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_RT_DIS] = "RT_DIS", +}; + +/* VCAPs */ +const struct vcap_info sparx5_vcaps[] = { + [VCAP_TYPE_IS2] = { + .name = "is2", + .rows = 256, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 110, + .default_cnt = 73, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = is2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is2_keyfield_set), + .actionfield_set = is2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is2_actionfield_set), + .keyfield_set_map = is2_keyfield_set_map, + .keyfield_set_map_size = is2_keyfield_set_map_size, + .actionfield_set_map = is2_actionfield_set_map, + .actionfield_set_map_size = is2_actionfield_set_map_size, + .keyfield_set_typegroups = is2_keyfield_set_typegroups, + .actionfield_set_typegroups = is2_actionfield_set_typegroups, + }, +}; + +const struct vcap_statistics sparx5_vcap_stats = { + .name = "sparx5", + .count = 1, + .keyfield_set_names = vcap_keyfield_set_names, + .actionfield_set_names = vcap_actionfield_set_names, + .keyfield_names = vcap_keyfield_names, + .actionfield_names = vcap_actionfield_names, +}; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h new file mode 100644 index 000000000000..7d106f1276fe --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_ag_api.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +/* This file is autogenerated by cml-utils 2022-10-13 10:04:41 +0200. + * Commit ID: fd7cafd175899f0672c73afb3a30fc872500ae86 + */ + +#ifndef __SPARX5_VCAP_AG_API_H__ +#define __SPARX5_VCAP_AG_API_H__ + +/* VCAPs */ +extern const struct vcap_info sparx5_vcaps[]; +extern const struct vcap_statistics sparx5_vcap_stats; + +#endif /* __SPARX5_VCAP_AG_API_H__ */ + diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c new file mode 100644 index 000000000000..b91e05ffe2f4 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver VCAP debugFS implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include <linux/types.h> +#include <linux/list.h> + +#include "sparx5_vcap_debugfs.h" +#include "sparx5_main_regs.h" +#include "sparx5_main.h" +#include "sparx5_vcap_impl.h" +#include "sparx5_vcap_ag_api.h" + +static void sparx5_vcap_port_keys(struct sparx5 *sparx5, + struct vcap_admin *admin, + struct sparx5_port *port, + struct vcap_output_print *out) +{ + int lookup; + u32 value; + + out->prf(out->dst, " port[%02d] (%s): ", port->portno, + netdev_name(port->ndev)); + for (lookup = 0; lookup < admin->lookups; ++lookup) { + out->prf(out->dst, "\n Lookup %d: ", lookup); + + /* Get lookup state */ + value = spx5_rd(sparx5, ANA_ACL_VCAP_S2_CFG(port->portno)); + out->prf(out->dst, "\n state: "); + if (ANA_ACL_VCAP_S2_CFG_SEC_ENA_GET(value)) + out->prf(out->dst, "on"); + else + out->prf(out->dst, "off"); + + /* Get key selection state */ + value = spx5_rd(sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(port->portno, lookup)); + + out->prf(out->dst, "\n noneth: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_NONETH_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_NONETH_CUSTOM_1: + out->prf(out->dst, "custom1"); + break; + case VCAP_IS2_PS_NONETH_CUSTOM_2: + out->prf(out->dst, "custom2"); + break; + case VCAP_IS2_PS_NONETH_NO_LOOKUP: + out->prf(out->dst, "none"); + break; + } + out->prf(out->dst, "\n ipv4_mc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_MC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ip4_other"); + break; + case VCAP_IS2_PS_IPV4_MC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + case VCAP_IS2_PS_IPV4_MC_IP4_VID: + out->prf(out->dst, "ip4_vid"); + break; + } + out->prf(out->dst, "\n ipv4_uc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_UC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ip4_other"); + break; + case VCAP_IS2_PS_IPV4_UC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + } + out->prf(out->dst, "\n ipv6_mc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_MC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV6_MC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_VID: + out->prf(out->dst, "ip6_vid"); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_STD: + out->prf(out->dst, "ip6_std"); + break; + case VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ipv4_other"); + break; + } + out->prf(out->dst, "\n ipv6_uc: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_UC_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_IPV6_UC_IP_7TUPLE: + out->prf(out->dst, "ip_7tuple"); + break; + case VCAP_IS2_PS_IPV6_UC_IP6_STD: + out->prf(out->dst, "ip6_std"); + break; + case VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER: + out->prf(out->dst, "ip4_tcp_udp ip4_other"); + break; + } + out->prf(out->dst, "\n arp: "); + switch (ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_ARP_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + case VCAP_IS2_PS_ARP_ARP: + out->prf(out->dst, "arp"); + break; + } + } + out->prf(out->dst, "\n"); +} + +static void sparx5_vcap_port_stickies(struct sparx5 *sparx5, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + int lookup; + u32 value; + + out->prf(out->dst, " Sticky bits: "); + for (lookup = 0; lookup < admin->lookups; ++lookup) { + out->prf(out->dst, "\n Lookup %d: ", lookup); + /* Get lookup sticky bits */ + value = spx5_rd(sparx5, ANA_ACL_SEC_LOOKUP_STICKY(lookup)); + + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_CLM_STICKY_GET(value)) + out->prf(out->dst, " sel_clm"); + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_IRLEG_STICKY_GET(value)) + out->prf(out->dst, " sel_irleg"); + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_ERLEG_STICKY_GET(value)) + out->prf(out->dst, " sel_erleg"); + if (ANA_ACL_SEC_LOOKUP_STICKY_KEY_SEL_PORT_STICKY_GET(value)) + out->prf(out->dst, " sel_port"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM2_STICKY_GET(value)) + out->prf(out->dst, " custom2"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_CUSTOM1_STICKY_GET(value)) + out->prf(out->dst, " custom1"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_OAM_STICKY_GET(value)) + out->prf(out->dst, " oam"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_VID_STICKY_GET(value)) + out->prf(out->dst, " ip6_vid"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_STD_STICKY_GET(value)) + out->prf(out->dst, " ip6_std"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP6_TCPUDP_STICKY_GET(value)) + out->prf(out->dst, " ip6_tcpudp"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP_7TUPLE_STICKY_GET(value)) + out->prf(out->dst, " ip_7tuple"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_VID_STICKY_GET(value)) + out->prf(out->dst, " ip4_vid"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_TCPUDP_STICKY_GET(value)) + out->prf(out->dst, " ip4_tcpudp"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_IP4_OTHER_STICKY_GET(value)) + out->prf(out->dst, " ip4_other"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_ARP_STICKY_GET(value)) + out->prf(out->dst, " arp"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_SNAP_STICKY_GET(value)) + out->prf(out->dst, " mac_snap"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_LLC_STICKY_GET(value)) + out->prf(out->dst, " mac_llc"); + if (ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY_GET(value)) + out->prf(out->dst, " mac_etype"); + /* Clear stickies */ + spx5_wr(value, sparx5, ANA_ACL_SEC_LOOKUP_STICKY(lookup)); + } + out->prf(out->dst, "\n"); +} + +/* Provide port information via a callback interface */ +int sparx5_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + const struct vcap_info *vcap; + struct vcap_control *vctrl; + + vctrl = sparx5->vcap_ctrl; + vcap = &vctrl->vcaps[admin->vtype]; + out->prf(out->dst, "%s:\n", vcap->name); + sparx5_vcap_port_keys(sparx5, admin, port, out); + sparx5_vcap_port_stickies(sparx5, admin, out); + return 0; +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h new file mode 100644 index 000000000000..f9ede03441f2 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_debugfs.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Microchip Sparx5 Switch driver VCAP implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#ifndef __SPARX5_VCAP_DEBUGFS_H__ +#define __SPARX5_VCAP_DEBUGFS_H__ + +#include <linux/netdevice.h> + +#include <vcap_api.h> +#include <vcap_api_client.h> + +#if defined(CONFIG_DEBUG_FS) + +/* Provide port information via a callback interface */ +int sparx5_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out); + +#else + +static inline int sparx5_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} + +#endif + +#endif /* __SPARX5_VCAP_DEBUGFS_H__ */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c new file mode 100644 index 000000000000..a0c126ba9a87 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c @@ -0,0 +1,723 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip Sparx5 Switch driver VCAP implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + * + * The Sparx5 Chip Register Model can be browsed at this location: + * https://github.com/microchip-ung/sparx-5_reginfo + */ + +#include "vcap_api_debugfs.h" +#include "sparx5_main_regs.h" +#include "sparx5_main.h" +#include "sparx5_vcap_impl.h" +#include "sparx5_vcap_ag_api.h" +#include "sparx5_vcap_debugfs.h" + +#define SUPER_VCAP_BLK_SIZE 3072 /* addresses per Super VCAP block */ +#define STREAMSIZE (64 * 4) /* bytes in the VCAP cache area */ + +#define SPARX5_IS2_LOOKUPS 4 +#define VCAP_IS2_KEYSEL(_ena, _noneth, _v4_mc, _v4_uc, _v6_mc, _v6_uc, _arp) \ + (ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_SET(_ena) | \ + ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_SET(_noneth) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_SET(_v4_mc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_SET(_v4_uc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_SET(_v6_mc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_SET(_v6_uc) | \ + ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_SET(_arp)) + +static struct sparx5_vcap_inst { + enum vcap_type vtype; /* type of vcap */ + int vinst; /* instance number within the same type */ + int lookups; /* number of lookups in this vcap type */ + int lookups_per_instance; /* number of lookups in this instance */ + int first_cid; /* first chain id in this vcap */ + int last_cid; /* last chain id in this vcap */ + int count; /* number of available addresses, not in super vcap */ + int map_id; /* id in the super vcap block mapping (if applicable) */ + int blockno; /* starting block in super vcap (if applicable) */ + int blocks; /* number of blocks in super vcap (if applicable) */ +} sparx5_vcap_inst_cfg[] = { + { + .vtype = VCAP_TYPE_IS2, /* IS2-0 */ + .vinst = 0, + .map_id = 4, + .lookups = SPARX5_IS2_LOOKUPS, + .lookups_per_instance = SPARX5_IS2_LOOKUPS / 2, + .first_cid = SPARX5_VCAP_CID_IS2_L0, + .last_cid = SPARX5_VCAP_CID_IS2_L2 - 1, + .blockno = 0, /* Maps block 0-1 */ + .blocks = 2, + }, + { + .vtype = VCAP_TYPE_IS2, /* IS2-1 */ + .vinst = 1, + .map_id = 5, + .lookups = SPARX5_IS2_LOOKUPS, + .lookups_per_instance = SPARX5_IS2_LOOKUPS / 2, + .first_cid = SPARX5_VCAP_CID_IS2_L2, + .last_cid = SPARX5_VCAP_CID_IS2_MAX, + .blockno = 2, /* Maps block 2-3 */ + .blocks = 2, + }, +}; + +/* Await the super VCAP completion of the current operation */ +static void sparx5_vcap_wait_super_update(struct sparx5 *sparx5) +{ + u32 value; + + read_poll_timeout(spx5_rd, value, + !VCAP_SUPER_CTRL_UPDATE_SHOT_GET(value), 500, 10000, + false, sparx5, VCAP_SUPER_CTRL); +} + +/* Initializing a VCAP address range: only IS2 for now */ +static void _sparx5_vcap_range_init(struct sparx5 *sparx5, + struct vcap_admin *admin, + u32 addr, u32 count) +{ + u32 size = count - 1; + + spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) | + VCAP_SUPER_CFG_MV_SIZE_SET(size), + sparx5, VCAP_SUPER_CFG); + spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(VCAP_CMD_INITIALIZE) | + VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_SUPER_CTRL_CLEAR_CACHE_SET(true) | + VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true), + sparx5, VCAP_SUPER_CTRL); + sparx5_vcap_wait_super_update(sparx5); +} + +/* Initializing VCAP rule data area */ +static void sparx5_vcap_block_init(struct sparx5 *sparx5, + struct vcap_admin *admin) +{ + _sparx5_vcap_range_init(sparx5, admin, admin->first_valid_addr, + admin->last_valid_addr - + admin->first_valid_addr); +} + +/* Get the keyset name from the sparx5 VCAP model */ +static const char *sparx5_vcap_keyset_name(struct net_device *ndev, + enum vcap_keyfield_set keyset) +{ + struct sparx5_port *port = netdev_priv(ndev); + + return vcap_keyset_name(port->sparx5->vcap_ctrl, keyset); +} + +/* Check if this is the first lookup of IS2 */ +static bool sparx5_vcap_is2_is_first_chain(struct vcap_rule *rule) +{ + return (rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L0 && + rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L1) || + ((rule->vcap_chain_id >= SPARX5_VCAP_CID_IS2_L2 && + rule->vcap_chain_id < SPARX5_VCAP_CID_IS2_L3)); +} + +/* Set the narrow range ingress port mask on a rule */ +static void sparx5_vcap_add_range_port_mask(struct vcap_rule *rule, + struct net_device *ndev) +{ + struct sparx5_port *port = netdev_priv(ndev); + u32 port_mask; + u32 range; + + range = port->portno / BITS_PER_TYPE(u32); + /* Port bit set to match-any */ + port_mask = ~BIT(port->portno % BITS_PER_TYPE(u32)); + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_SEL, 0, 0xf); + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_RNG, range, 0xf); + vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, 0, port_mask); +} + +/* Set the wide range ingress port mask on a rule */ +static void sparx5_vcap_add_wide_port_mask(struct vcap_rule *rule, + struct net_device *ndev) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct vcap_u72_key port_mask; + u32 range; + + /* Port bit set to match-any */ + memset(port_mask.value, 0, sizeof(port_mask.value)); + memset(port_mask.mask, 0xff, sizeof(port_mask.mask)); + range = port->portno / BITS_PER_BYTE; + port_mask.mask[range] = ~BIT(port->portno % BITS_PER_BYTE); + vcap_rule_add_key_u72(rule, VCAP_KF_IF_IGR_PORT_MASK, &port_mask); +} + +/* Convert chain id to vcap lookup id */ +static int sparx5_vcap_cid_to_lookup(int cid) +{ + int lookup = 0; + + /* For now only handle IS2 */ + if (cid >= SPARX5_VCAP_CID_IS2_L1 && cid < SPARX5_VCAP_CID_IS2_L2) + lookup = 1; + else if (cid >= SPARX5_VCAP_CID_IS2_L2 && cid < SPARX5_VCAP_CID_IS2_L3) + lookup = 2; + else if (cid >= SPARX5_VCAP_CID_IS2_L3 && cid < SPARX5_VCAP_CID_IS2_MAX) + lookup = 3; + + return lookup; +} + +/* Return the list of keysets for the vcap port configuration */ +static int sparx5_vcap_is2_get_port_keysets(struct net_device *ndev, + int lookup, + struct vcap_keyset_list *keysetlist, + u16 l3_proto) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + int portno = port->portno; + u32 value; + + /* Check if the port keyset selection is enabled */ + value = spx5_rd(sparx5, ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + if (!ANA_ACL_VCAP_S2_KEY_SEL_KEY_SEL_ENA_GET(value)) + return -ENOENT; + + /* Collect all keysets for the port in a list */ + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_ARP) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_ARP_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_ARP_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_ARP_ARP: + vcap_keyset_list_add(keysetlist, VCAP_KFS_ARP); + break; + } + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IP) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_UC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + case VCAP_IS2_PS_IPV4_UC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + } + + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP4_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV4_MC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + case VCAP_IS2_PS_IPV4_MC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + } + } + + if (l3_proto == ETH_P_ALL || l3_proto == ETH_P_IPV6) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_UC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_UC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV6_UC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + case VCAP_IS2_PS_IPV6_UC_IP6_STD: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); + break; + case VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + } + + switch (ANA_ACL_VCAP_S2_KEY_SEL_IP6_MC_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_IPV6_MC_MAC_ETYPE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + case VCAP_IS2_PS_IPV6_MC_IP_7TUPLE: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP_7TUPLE); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_STD: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP6_STD); + break; + case VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER: + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_TCP_UDP); + vcap_keyset_list_add(keysetlist, VCAP_KFS_IP4_OTHER); + break; + case VCAP_IS2_PS_IPV6_MC_IP6_VID: + /* Not used */ + break; + } + } + + if (l3_proto != ETH_P_ARP && l3_proto != ETH_P_IP && + l3_proto != ETH_P_IPV6) { + switch (ANA_ACL_VCAP_S2_KEY_SEL_NON_ETH_KEY_SEL_GET(value)) { + case VCAP_IS2_PS_NONETH_MAC_ETYPE: + /* IS2 non-classified frames generate MAC_ETYPE */ + vcap_keyset_list_add(keysetlist, VCAP_KFS_MAC_ETYPE); + break; + } + } + return 0; +} + +/* Get the port keyset for the vcap lookup */ +int sparx5_vcap_get_port_keyset(struct net_device *ndev, + struct vcap_admin *admin, + int cid, + u16 l3_proto, + struct vcap_keyset_list *kslist) +{ + int lookup; + + lookup = sparx5_vcap_cid_to_lookup(cid); + return sparx5_vcap_is2_get_port_keysets(ndev, lookup, kslist, l3_proto); +} + +/* API callback used for validating a field keyset (check the port keysets) */ +static enum vcap_keyfield_set +sparx5_vcap_validate_keyset(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + struct vcap_keyset_list keysetlist = {}; + enum vcap_keyfield_set keysets[10] = {}; + int idx, jdx, lookup; + + if (!kslist || kslist->cnt == 0) + return VCAP_KFS_NO_VALUE; + + /* Get a list of currently configured keysets in the lookups */ + lookup = sparx5_vcap_cid_to_lookup(rule->vcap_chain_id); + keysetlist.max = ARRAY_SIZE(keysets); + keysetlist.keysets = keysets; + sparx5_vcap_is2_get_port_keysets(ndev, lookup, &keysetlist, l3_proto); + + /* Check if there is a match and return the match */ + for (idx = 0; idx < kslist->cnt; ++idx) + for (jdx = 0; jdx < keysetlist.cnt; ++jdx) + if (kslist->keysets[idx] == keysets[jdx]) + return kslist->keysets[idx]; + + pr_err("%s:%d: %s not supported in port key selection\n", + __func__, __LINE__, + sparx5_vcap_keyset_name(ndev, kslist->keysets[0])); + + return -ENOENT; +} + +/* API callback used for adding default fields to a rule */ +static void sparx5_vcap_add_default_fields(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + const struct vcap_field *field; + + field = vcap_lookup_keyfield(rule, VCAP_KF_IF_IGR_PORT_MASK); + if (field && field->width == SPX5_PORTS) + sparx5_vcap_add_wide_port_mask(rule, ndev); + else if (field && field->width == BITS_PER_TYPE(u32)) + sparx5_vcap_add_range_port_mask(rule, ndev); + else + pr_err("%s:%d: %s: could not add an ingress port mask for: %s\n", + __func__, __LINE__, netdev_name(ndev), + sparx5_vcap_keyset_name(ndev, rule->keyset)); + /* add the lookup bit */ + if (sparx5_vcap_is2_is_first_chain(rule)) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0); +} + +/* API callback used for erasing the vcap cache area (not the register area) */ +static void sparx5_vcap_cache_erase(struct vcap_admin *admin) +{ + memset(admin->cache.keystream, 0, STREAMSIZE); + memset(admin->cache.maskstream, 0, STREAMSIZE); + memset(admin->cache.actionstream, 0, STREAMSIZE); + memset(&admin->cache.counter, 0, sizeof(admin->cache.counter)); +} + +/* API callback used for writing to the VCAP cache */ +static void sparx5_vcap_cache_write(struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 start, + u32 count) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + u32 *keystr, *mskstr, *actstr; + int idx; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + switch (sel) { + case VCAP_SEL_ENTRY: + for (idx = 0; idx < count; ++idx) { + /* Avoid 'match-off' by setting value & mask */ + spx5_wr(keystr[idx] & mskstr[idx], sparx5, + VCAP_SUPER_VCAP_ENTRY_DAT(idx)); + spx5_wr(~mskstr[idx], sparx5, + VCAP_SUPER_VCAP_MASK_DAT(idx)); + } + break; + case VCAP_SEL_ACTION: + for (idx = 0; idx < count; ++idx) + spx5_wr(actstr[idx], sparx5, + VCAP_SUPER_VCAP_ACTION_DAT(idx)); + break; + case VCAP_SEL_ALL: + pr_err("%s:%d: cannot write all streams at once\n", + __func__, __LINE__); + break; + default: + break; + } + if (sel & VCAP_SEL_COUNTER) { + start = start & 0xfff; /* counter limit */ + if (admin->vinst == 0) + spx5_wr(admin->cache.counter, sparx5, + ANA_ACL_CNT_A(start)); + else + spx5_wr(admin->cache.counter, sparx5, + ANA_ACL_CNT_B(start)); + spx5_wr(admin->cache.sticky, sparx5, + VCAP_SUPER_VCAP_CNT_DAT(0)); + } +} + +/* API callback used for reading from the VCAP into the VCAP cache */ +static void sparx5_vcap_cache_read(struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 start, + u32 count) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + u32 *keystr, *mskstr, *actstr; + int idx; + + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + actstr = &admin->cache.actionstream[start]; + if (sel & VCAP_SEL_ENTRY) { + for (idx = 0; idx < count; ++idx) { + keystr[idx] = spx5_rd(sparx5, + VCAP_SUPER_VCAP_ENTRY_DAT(idx)); + mskstr[idx] = ~spx5_rd(sparx5, + VCAP_SUPER_VCAP_MASK_DAT(idx)); + } + } + if (sel & VCAP_SEL_ACTION) { + for (idx = 0; idx < count; ++idx) + actstr[idx] = spx5_rd(sparx5, + VCAP_SUPER_VCAP_ACTION_DAT(idx)); + } + if (sel & VCAP_SEL_COUNTER) { + start = start & 0xfff; /* counter limit */ + if (admin->vinst == 0) + admin->cache.counter = + spx5_rd(sparx5, ANA_ACL_CNT_A(start)); + else + admin->cache.counter = + spx5_rd(sparx5, ANA_ACL_CNT_B(start)); + admin->cache.sticky = + spx5_rd(sparx5, VCAP_SUPER_VCAP_CNT_DAT(0)); + } +} + +/* API callback used for initializing a VCAP address range */ +static void sparx5_vcap_range_init(struct net_device *ndev, + struct vcap_admin *admin, u32 addr, + u32 count) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + + _sparx5_vcap_range_init(sparx5, admin, addr, count); +} + +/* API callback used for updating the VCAP cache */ +static void sparx5_vcap_update(struct net_device *ndev, + struct vcap_admin *admin, enum vcap_command cmd, + enum vcap_selection sel, u32 addr) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + bool clear; + + clear = (cmd == VCAP_CMD_INITIALIZE); + spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(0) | + VCAP_SUPER_CFG_MV_SIZE_SET(0), sparx5, VCAP_SUPER_CFG); + spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET((VCAP_SEL_ENTRY & sel) == 0) | + VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET((VCAP_SEL_ACTION & sel) == 0) | + VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET((VCAP_SEL_COUNTER & sel) == 0) | + VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_SUPER_CTRL_CLEAR_CACHE_SET(clear) | + VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true), + sparx5, VCAP_SUPER_CTRL); + sparx5_vcap_wait_super_update(sparx5); +} + +/* API callback used for moving a block of rules in the VCAP */ +static void sparx5_vcap_move(struct net_device *ndev, struct vcap_admin *admin, + u32 addr, int offset, int count) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5 = port->sparx5; + enum vcap_command cmd; + u16 mv_num_pos; + u16 mv_size; + + mv_size = count - 1; + if (offset > 0) { + mv_num_pos = offset - 1; + cmd = VCAP_CMD_MOVE_DOWN; + } else { + mv_num_pos = -offset - 1; + cmd = VCAP_CMD_MOVE_UP; + } + spx5_wr(VCAP_SUPER_CFG_MV_NUM_POS_SET(mv_num_pos) | + VCAP_SUPER_CFG_MV_SIZE_SET(mv_size), + sparx5, VCAP_SUPER_CFG); + spx5_wr(VCAP_SUPER_CTRL_UPDATE_CMD_SET(cmd) | + VCAP_SUPER_CTRL_UPDATE_ENTRY_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ACTION_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_CNT_DIS_SET(0) | + VCAP_SUPER_CTRL_UPDATE_ADDR_SET(addr) | + VCAP_SUPER_CTRL_CLEAR_CACHE_SET(false) | + VCAP_SUPER_CTRL_UPDATE_SHOT_SET(true), + sparx5, VCAP_SUPER_CTRL); + sparx5_vcap_wait_super_update(sparx5); +} + +/* Enable all lookups in the VCAP instance */ +static int sparx5_vcap_enable(struct net_device *ndev, + struct vcap_admin *admin, + bool enable) +{ + struct sparx5_port *port = netdev_priv(ndev); + struct sparx5 *sparx5; + int portno; + + sparx5 = port->sparx5; + portno = port->portno; + + /* For now we only consider IS2 */ + if (enable) + spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0xf), sparx5, + ANA_ACL_VCAP_S2_CFG(portno)); + else + spx5_wr(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0), sparx5, + ANA_ACL_VCAP_S2_CFG(portno)); + return 0; +} + +/* API callback operations: only IS2 is supported for now */ +static struct vcap_operations sparx5_vcap_ops = { + .validate_keyset = sparx5_vcap_validate_keyset, + .add_default_fields = sparx5_vcap_add_default_fields, + .cache_erase = sparx5_vcap_cache_erase, + .cache_write = sparx5_vcap_cache_write, + .cache_read = sparx5_vcap_cache_read, + .init = sparx5_vcap_range_init, + .update = sparx5_vcap_update, + .move = sparx5_vcap_move, + .port_info = sparx5_port_info, + .enable = sparx5_vcap_enable, +}; + +/* Enable lookups per port and set the keyset generation: only IS2 for now */ +static void sparx5_vcap_port_key_selection(struct sparx5 *sparx5, + struct vcap_admin *admin) +{ + int portno, lookup; + u32 keysel; + + /* all traffic types generate the MAC_ETYPE keyset for now in all + * lookups on all ports + */ + keysel = VCAP_IS2_KEYSEL(true, VCAP_IS2_PS_NONETH_MAC_ETYPE, + VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_UC_IP_7TUPLE, + VCAP_IS2_PS_ARP_ARP); + for (lookup = 0; lookup < admin->lookups; ++lookup) { + for (portno = 0; portno < SPX5_PORTS; ++portno) { + spx5_wr(keysel, sparx5, + ANA_ACL_VCAP_S2_KEY_SEL(portno, lookup)); + } + } +} + +/* Disable lookups per port and set the keyset generation: only IS2 for now */ +static void sparx5_vcap_port_key_deselection(struct sparx5 *sparx5, + struct vcap_admin *admin) +{ + int portno; + + for (portno = 0; portno < SPX5_PORTS; ++portno) + spx5_rmw(ANA_ACL_VCAP_S2_CFG_SEC_ENA_SET(0), + ANA_ACL_VCAP_S2_CFG_SEC_ENA, + sparx5, + ANA_ACL_VCAP_S2_CFG(portno)); +} + +static void sparx5_vcap_admin_free(struct vcap_admin *admin) +{ + if (!admin) + return; + mutex_destroy(&admin->lock); + kfree(admin->cache.keystream); + kfree(admin->cache.maskstream); + kfree(admin->cache.actionstream); + kfree(admin); +} + +/* Allocate a vcap instance with a rule list and a cache area */ +static struct vcap_admin * +sparx5_vcap_admin_alloc(struct sparx5 *sparx5, struct vcap_control *ctrl, + const struct sparx5_vcap_inst *cfg) +{ + struct vcap_admin *admin; + + admin = kzalloc(sizeof(*admin), GFP_KERNEL); + if (!admin) + return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + INIT_LIST_HEAD(&admin->enabled); + mutex_init(&admin->lock); + admin->vtype = cfg->vtype; + admin->vinst = cfg->vinst; + admin->lookups = cfg->lookups; + admin->lookups_per_instance = cfg->lookups_per_instance; + admin->first_cid = cfg->first_cid; + admin->last_cid = cfg->last_cid; + admin->cache.keystream = + kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.maskstream = + kzalloc(STREAMSIZE, GFP_KERNEL); + admin->cache.actionstream = + kzalloc(STREAMSIZE, GFP_KERNEL); + if (!admin->cache.keystream || !admin->cache.maskstream || + !admin->cache.actionstream) { + sparx5_vcap_admin_free(admin); + return ERR_PTR(-ENOMEM); + } + return admin; +} + +/* Do block allocations and provide addresses for VCAP instances */ +static void sparx5_vcap_block_alloc(struct sparx5 *sparx5, + struct vcap_admin *admin, + const struct sparx5_vcap_inst *cfg) +{ + int idx; + + /* Super VCAP block mapping and address configuration. Block 0 + * is assigned addresses 0 through 3071, block 1 is assigned + * addresses 3072 though 6143, and so on. + */ + for (idx = cfg->blockno; idx < cfg->blockno + cfg->blocks; ++idx) { + spx5_wr(VCAP_SUPER_IDX_CORE_IDX_SET(idx), sparx5, + VCAP_SUPER_IDX); + spx5_wr(VCAP_SUPER_MAP_CORE_MAP_SET(cfg->map_id), sparx5, + VCAP_SUPER_MAP); + } + admin->first_valid_addr = cfg->blockno * SUPER_VCAP_BLK_SIZE; + admin->last_used_addr = admin->first_valid_addr + + cfg->blocks * SUPER_VCAP_BLK_SIZE; + admin->last_valid_addr = admin->last_used_addr - 1; +} + +/* Allocate a vcap control and vcap instances and configure the system */ +int sparx5_vcap_init(struct sparx5 *sparx5) +{ + const struct sparx5_vcap_inst *cfg; + struct vcap_control *ctrl; + struct vcap_admin *admin; + struct dentry *dir; + int err = 0, idx; + + /* Create a VCAP control instance that owns the platform specific VCAP + * model with VCAP instances and information about keysets, keys, + * actionsets and actions + * - Create administrative state for each available VCAP + * - Lists of rules + * - Address information + * - Initialize VCAP blocks + * - Configure port keysets + */ + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + sparx5->vcap_ctrl = ctrl; + /* select the sparx5 VCAP model */ + ctrl->vcaps = sparx5_vcaps; + ctrl->stats = &sparx5_vcap_stats; + /* Setup callbacks to allow the API to use the VCAP HW */ + ctrl->ops = &sparx5_vcap_ops; + + INIT_LIST_HEAD(&ctrl->list); + for (idx = 0; idx < ARRAY_SIZE(sparx5_vcap_inst_cfg); ++idx) { + cfg = &sparx5_vcap_inst_cfg[idx]; + admin = sparx5_vcap_admin_alloc(sparx5, ctrl, cfg); + if (IS_ERR(admin)) { + err = PTR_ERR(admin); + pr_err("%s:%d: vcap allocation failed: %d\n", + __func__, __LINE__, err); + return err; + } + sparx5_vcap_block_alloc(sparx5, admin, cfg); + sparx5_vcap_block_init(sparx5, admin); + if (cfg->vinst == 0) + sparx5_vcap_port_key_selection(sparx5, admin); + list_add_tail(&admin->list, &ctrl->list); + } + dir = vcap_debugfs(sparx5->dev, sparx5->debugfs_root, ctrl); + for (idx = 0; idx < SPX5_PORTS; ++idx) + if (sparx5->ports[idx]) + vcap_port_debugfs(sparx5->dev, dir, ctrl, + sparx5->ports[idx]->ndev); + + return err; +} + +void sparx5_vcap_destroy(struct sparx5 *sparx5) +{ + struct vcap_control *ctrl = sparx5->vcap_ctrl; + struct vcap_admin *admin, *admin_next; + + if (!ctrl) + return; + + list_for_each_entry_safe(admin, admin_next, &ctrl->list, list) { + sparx5_vcap_port_key_deselection(sparx5, admin); + vcap_del_rules(ctrl, admin); + list_del(&admin->list); + sparx5_vcap_admin_free(admin); + } + kfree(ctrl); +} diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h new file mode 100644 index 000000000000..0a0f2412c980 --- /dev/null +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Microchip Sparx5 Switch driver VCAP implementation + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + * + * The Sparx5 Chip Register Model can be browsed at this location: + * https://github.com/microchip-ung/sparx-5_reginfo + */ + +#ifndef __SPARX5_VCAP_IMPL_H__ +#define __SPARX5_VCAP_IMPL_H__ + +#include <linux/types.h> +#include <linux/list.h> + +#include "vcap_api.h" +#include "vcap_api_client.h" + +#define SPARX5_VCAP_CID_IS2_L0 VCAP_CID_INGRESS_STAGE2_L0 /* IS2 lookup 0 */ +#define SPARX5_VCAP_CID_IS2_L1 VCAP_CID_INGRESS_STAGE2_L1 /* IS2 lookup 1 */ +#define SPARX5_VCAP_CID_IS2_L2 VCAP_CID_INGRESS_STAGE2_L2 /* IS2 lookup 2 */ +#define SPARX5_VCAP_CID_IS2_L3 VCAP_CID_INGRESS_STAGE2_L3 /* IS2 lookup 3 */ +#define SPARX5_VCAP_CID_IS2_MAX \ + (VCAP_CID_INGRESS_STAGE2_L3 + VCAP_CID_LOOKUP_SIZE - 1) /* IS2 Max */ + +/* IS2 port keyset selection control */ + +/* IS2 non-ethernet traffic type keyset generation */ +enum vcap_is2_port_sel_noneth { + VCAP_IS2_PS_NONETH_MAC_ETYPE, + VCAP_IS2_PS_NONETH_CUSTOM_1, + VCAP_IS2_PS_NONETH_CUSTOM_2, + VCAP_IS2_PS_NONETH_NO_LOOKUP +}; + +/* IS2 IPv4 unicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv4_uc { + VCAP_IS2_PS_IPV4_UC_MAC_ETYPE, + VCAP_IS2_PS_IPV4_UC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_UC_IP_7TUPLE, +}; + +/* IS2 IPv4 multicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv4_mc { + VCAP_IS2_PS_IPV4_MC_MAC_ETYPE, + VCAP_IS2_PS_IPV4_MC_IP4_TCP_UDP_OTHER, + VCAP_IS2_PS_IPV4_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV4_MC_IP4_VID, +}; + +/* IS2 IPv6 unicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv6_uc { + VCAP_IS2_PS_IPV6_UC_MAC_ETYPE, + VCAP_IS2_PS_IPV6_UC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_UC_IP6_STD, + VCAP_IS2_PS_IPV6_UC_IP4_TCP_UDP_OTHER, +}; + +/* IS2 IPv6 multicast traffic type keyset generation */ +enum vcap_is2_port_sel_ipv6_mc { + VCAP_IS2_PS_IPV6_MC_MAC_ETYPE, + VCAP_IS2_PS_IPV6_MC_IP_7TUPLE, + VCAP_IS2_PS_IPV6_MC_IP6_VID, + VCAP_IS2_PS_IPV6_MC_IP6_STD, + VCAP_IS2_PS_IPV6_MC_IP4_TCP_UDP_OTHER, +}; + +/* IS2 ARP traffic type keyset generation */ +enum vcap_is2_port_sel_arp { + VCAP_IS2_PS_ARP_MAC_ETYPE, + VCAP_IS2_PS_ARP_ARP, +}; + +/* Get the port keyset for the vcap lookup */ +int sparx5_vcap_get_port_keyset(struct net_device *ndev, + struct vcap_admin *admin, + int cid, + u16 l3_proto, + struct vcap_keyset_list *kslist); + +#endif /* __SPARX5_VCAP_IMPL_H__ */ diff --git a/drivers/net/ethernet/microchip/vcap/Kconfig b/drivers/net/ethernet/microchip/vcap/Kconfig new file mode 100644 index 000000000000..97f43fd4473f --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/Kconfig @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Microchip VCAP API configuration +# + +if NET_VENDOR_MICROCHIP + +config VCAP + bool "VCAP (Versatile Content-Aware Processor) library" + help + Provides the basic VCAP functionality for multiple Microchip switchcores + + A VCAP is essentially a TCAM with rules consisting of + + - Programmable key fields + - Programmable action fields + - A counter (which may be only one bit wide) + + Besides this each VCAP has: + + - A number of lookups + - A keyset configuration per port per lookup + + The VCAP implementation provides switchcore independent handling of rules + and supports: + + - Creating and deleting rules + - Updating and getting rules + + The platform specific configuration as well as the platform specific model + of the VCAP instances are attached to the VCAP API and a client can then + access rules via the API in a platform independent way, with the + limitations that each VCAP has in terms of its supported keys and actions. + + Different switchcores will have different VCAP instances with different + characteristics. Look in the datasheet for the VCAP specifications for the + specific switchcore. + +config VCAP_KUNIT_TEST + bool "KUnit test for VCAP library" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on KUNIT=y && VCAP=y && y + select DEBUG_FS + default KUNIT_ALL_TESTS + help + This builds unit tests for the VCAP library. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + +endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/vcap/Makefile b/drivers/net/ethernet/microchip/vcap/Makefile new file mode 100644 index 000000000000..0adb8f5a8735 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Microchip VCAP API +# + +obj-$(CONFIG_VCAP) += vcap.o +obj-$(CONFIG_VCAP_KUNIT_TEST) += vcap_model_kunit.o +vcap-$(CONFIG_DEBUG_FS) += vcap_api_debugfs.o + +vcap-y += vcap_api.o diff --git a/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h new file mode 100644 index 000000000000..84de2aee4169 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_ag_api.h @@ -0,0 +1,735 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +/* This file is autogenerated by cml-utils 2022-10-13 10:04:41 +0200. + * Commit ID: fd7cafd175899f0672c73afb3a30fc872500ae86 + */ + +#ifndef __VCAP_AG_API__ +#define __VCAP_AG_API__ + +enum vcap_type { + VCAP_TYPE_ES2, + VCAP_TYPE_IS0, + VCAP_TYPE_IS2, + VCAP_TYPE_MAX +}; + +/* Keyfieldset names with origin information */ +enum vcap_keyfield_set { + VCAP_KFS_NO_VALUE, /* initial value */ + VCAP_KFS_ARP, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_ETAG, /* sparx5 is0 X2 */ + VCAP_KFS_IP4_OTHER, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP4_TCP_UDP, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP4_VID, /* sparx5 es2 X3 */ + VCAP_KFS_IP6_STD, /* sparx5 is2 X6 */ + VCAP_KFS_IP6_VID, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_IP_7TUPLE, /* sparx5 is2 X12, sparx5 es2 X12 */ + VCAP_KFS_LL_FULL, /* sparx5 is0 X6 */ + VCAP_KFS_MAC_ETYPE, /* sparx5 is2 X6, sparx5 es2 X6 */ + VCAP_KFS_MLL, /* sparx5 is0 X3 */ + VCAP_KFS_NORMAL, /* sparx5 is0 X6 */ + VCAP_KFS_NORMAL_5TUPLE_IP4, /* sparx5 is0 X6 */ + VCAP_KFS_NORMAL_7TUPLE, /* sparx5 is0 X12 */ + VCAP_KFS_PURE_5TUPLE_IP4, /* sparx5 is0 X3 */ + VCAP_KFS_TRI_VID, /* sparx5 is0 X2 */ + VCAP_KFS_MAC_LLC, /* lan966x is2 X2 */ + VCAP_KFS_MAC_SNAP, /* lan966x is2 X2 */ + VCAP_KFS_OAM, /* lan966x is2 X2 */ + VCAP_KFS_IP6_TCP_UDP, /* lan966x is2 X4 */ + VCAP_KFS_IP6_OTHER, /* lan966x is2 X4 */ + VCAP_KFS_SMAC_SIP4, /* lan966x is2 X1 */ + VCAP_KFS_SMAC_SIP6, /* lan966x is2 X2 */ +}; + +/* List of keyfields with description + * + * Keys ending in _IS are booleans derived from frame data + * Keys ending in _CLS are classified frame data + * + * VCAP_KF_8021BR_ECID_BASE: W12, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_ECID_EXT: W8, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_E_TAGGED: W1, sparx5: is0 + * Set for frames containing an E-TAG (802.1BR Ethertype 893f) + * VCAP_KF_8021BR_GRP: W2, sparx5: is0 + * E-Tag group bits in 802.1BR Bridge Port Extension + * VCAP_KF_8021BR_IGR_ECID_BASE: W12, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021BR_IGR_ECID_EXT: W8, sparx5: is0 + * Used by 802.1BR Bridge Port Extension in an E-Tag + * VCAP_KF_8021Q_DEI0: W1, sparx5: is0 + * First DEI in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_DEI1: W1, sparx5: is0 + * Second DEI in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_DEI2: W1, sparx5: is0 + * Third DEI in multiple vlan tags (not always available) + * VCAP_KF_8021Q_DEI_CLS: W1, sparx5: is2/es2, lan966x: is2 + * Classified DEI + * VCAP_KF_8021Q_PCP0: W3, sparx5: is0 + * First PCP in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_PCP1: W3, sparx5: is0 + * Second PCP in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_PCP2: W3, sparx5: is0 + * Third PCP in multiple vlan tags (not always available) + * VCAP_KF_8021Q_PCP_CLS: W3, sparx5: is2/es2, lan966x: is2 + * Classified PCP + * VCAP_KF_8021Q_TPID0: W3, sparx5: is0 + * First TPIC in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_TPID1: W3, sparx5: is0 + * Second TPID in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_TPID2: W3, sparx5: is0 + * Third TPID in multiple vlan tags (not always available) + * VCAP_KF_8021Q_VID0: W12, sparx5: is0 + * First VID in multiple vlan tags (outer tag or default port tag) + * VCAP_KF_8021Q_VID1: W12, sparx5: is0 + * Second VID in multiple vlan tags (inner tag) + * VCAP_KF_8021Q_VID2: W12, sparx5: is0 + * Third VID in multiple vlan tags (not always available) + * VCAP_KF_8021Q_VID_CLS: W13, sparx5: is2/es2, lan966x is2 W12 + * Classified VID + * VCAP_KF_8021Q_VLAN_TAGGED_IS: W1, sparx5: is2/es2, lan966x: is2 + * Sparx5: Set if frame was received with a VLAN tag, LAN966x: Set if frame has + * one or more Q-tags. Independent of port VLAN awareness + * VCAP_KF_8021Q_VLAN_TAGS: W3, sparx5: is0 + * Number of VLAN tags in frame: 0: Untagged, 1: Single tagged, 3: Double + * tagged, 7: Triple tagged + * VCAP_KF_ACL_GRP_ID: W8, sparx5: es2 + * Used in interface map table + * VCAP_KF_ARP_ADDR_SPACE_OK_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if hardware address is Ethernet + * VCAP_KF_ARP_LEN_OK_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if hardware address length = 6 (Ethernet) and IP address length = 4 (IP). + * VCAP_KF_ARP_OPCODE: W2, sparx5: is2/es2, lan966x: i2 + * ARP opcode + * VCAP_KF_ARP_OPCODE_UNKNOWN_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if not one of the codes defined in VCAP_KF_ARP_OPCODE + * VCAP_KF_ARP_PROTO_SPACE_OK_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if protocol address space is 0x0800 + * VCAP_KF_ARP_SENDER_MATCH_IS: W1, sparx5: is2/es2, lan966x: is2 + * Sender Hardware Address = SMAC (ARP) + * VCAP_KF_ARP_TGT_MATCH_IS: W1, sparx5: is2/es2, lan966x: is2 + * Target Hardware Address = SMAC (RARP) + * VCAP_KF_COSID_CLS: W3, sparx5: es2 + * Class of service + * VCAP_KF_DST_ENTRY: W1, sparx5: is0 + * Selects whether the frame’s destination or source information is used for + * fields L2_SMAC and L3_IP4_SIP + * VCAP_KF_ES0_ISDX_KEY_ENA: W1, sparx5: es2 + * The value taken from the IFH .FWD.ES0_ISDX_KEY_ENA + * VCAP_KF_ETYPE: W16, sparx5: is0/is2/es2, lan966x: is2 + * Ethernet type + * VCAP_KF_ETYPE_LEN_IS: W1, sparx5: is0/is2/es2 + * Set if frame has EtherType >= 0x600 + * VCAP_KF_ETYPE_MPLS: W2, sparx5: is0 + * Type of MPLS Ethertype (or not) + * VCAP_KF_IF_EGR_PORT_MASK: W32, sparx5: es2 + * Egress port mask, one bit per port + * VCAP_KF_IF_EGR_PORT_MASK_RNG: W3, sparx5: es2 + * Select which 32 port group is available in IF_EGR_PORT (or virtual ports or + * CPU queue) + * VCAP_KF_IF_IGR_PORT: sparx5 is0 W7, sparx5 es2 W9 + * Sparx5: Logical ingress port number retrieved from + * ANA_CL::PORT_ID_CFG.LPORT_NUM or ERLEG, LAN966x: ingress port nunmber + * VCAP_KF_IF_IGR_PORT_MASK: sparx5 is0 W65, sparx5 is2 W32, sparx5 is2 W65, + * lan966x is2 W9 + * Ingress port mask, one bit per port/erleg + * VCAP_KF_IF_IGR_PORT_MASK_L3: W1, sparx5: is2 + * If set, IF_IGR_PORT_MASK, IF_IGR_PORT_MASK_RNG, and IF_IGR_PORT_MASK_SEL are + * used to specify L3 interfaces + * VCAP_KF_IF_IGR_PORT_MASK_RNG: W4, sparx5: is2 + * Range selector for IF_IGR_PORT_MASK. Specifies which group of 32 ports are + * available in IF_IGR_PORT_MASK + * VCAP_KF_IF_IGR_PORT_MASK_SEL: W2, sparx5: is0/is2 + * Mode selector for IF_IGR_PORT_MASK, applicable when IF_IGR_PORT_MASK_L3 == 0. + * Mapping: 0: DEFAULT 1: LOOPBACK 2: MASQUERADE 3: CPU_VD + * VCAP_KF_IF_IGR_PORT_SEL: W1, sparx5: es2 + * Selector for IF_IGR_PORT: physical port number or ERLEG + * VCAP_KF_IP4_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * Set if frame has EtherType = 0x800 and IP version = 4 + * VCAP_KF_IP_MC_IS: W1, sparx5: is0 + * Set if frame is IPv4 frame and frame’s destination MAC address is an IPv4 + * multicast address (0x01005E0 /25). Set if frame is IPv6 frame and frame’s + * destination MAC address is an IPv6 multicast address (0x3333/16). + * VCAP_KF_IP_PAYLOAD_5TUPLE: W32, sparx5: is0 + * Payload bytes after IP header + * VCAP_KF_IP_SNAP_IS: W1, sparx5: is0 + * Set if frame is IPv4, IPv6, or SNAP frame + * VCAP_KF_ISDX_CLS: W12, sparx5: is2/es2 + * Classified ISDX + * VCAP_KF_ISDX_GT0_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if classified ISDX > 0 + * VCAP_KF_L2_BC_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * Set if frame’s destination MAC address is the broadcast address + * (FF-FF-FF-FF-FF-FF). + * VCAP_KF_L2_DMAC: W48, sparx5: is0/is2/es2, lan966x: is2 + * Destination MAC address + * VCAP_KF_L2_FWD_IS: W1, sparx5: is2 + * Set if the frame is allowed to be forwarded to front ports + * VCAP_KF_L2_MC_IS: W1, sparx5: is0/is2/es2, lan9966x is2 + * Set if frame’s destination MAC address is a multicast address (bit 40 = 1). + * VCAP_KF_L2_PAYLOAD_ETYPE: W64, sparx5: is2/es2 + * Byte 0-7 of L2 payload after Type/Len field and overloading for OAM + * VCAP_KF_L2_SMAC: W48, sparx5: is0/is2/es2, lan966x is2 + * Source MAC address + * VCAP_KF_L3_DIP_EQ_SIP_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if Src IP matches Dst IP address + * VCAP_KF_L3_DMAC_DIP_MATCH: W1, sparx5: is2 + * Match found in DIP security lookup in ANA_L3 + * VCAP_KF_L3_DPL_CLS: W1, sparx5: es2 + * The frames drop precedence level + * VCAP_KF_L3_DSCP: W6, sparx5: is0 + * Frame’s DSCP value + * VCAP_KF_L3_DST_IS: W1, sparx5: is2 + * Set if lookup is done for egress router leg + * VCAP_KF_L3_FRAGMENT_TYPE: W2, sparx5: is0/is2/es2 + * L3 Fragmentation type (none, initial, suspicious, valid follow up) + * VCAP_KF_L3_FRAG_INVLD_L4_LEN: W1, sparx5: is0/is2 + * Set if frame's L4 length is less than ANA_CL:COMMON:CLM_FRAGMENT_CFG.L4_MIN_L + * EN + * VCAP_KF_L3_IP4_DIP: W32, sparx5: is0/is2/es2, lan966x: is2 + * Destination IPv4 Address + * VCAP_KF_L3_IP4_SIP: W32, sparx5: is0/is2/es2, lan966x: is2 + * Source IPv4 Address + * VCAP_KF_L3_IP6_DIP: W128, sparx5: is0/is2/es2, lan966x: is2 + * Sparx5: Full IPv6 DIP, LAN966x: Either Full IPv6 DIP or a subset depending on + * frame type + * VCAP_KF_L3_IP6_SIP: W128, sparx5: is0/is2/es2, lan966x: is2 + * Sparx5: Full IPv6 SIP, LAN966x: Either Full IPv6 SIP or a subset depending on + * frame type + * VCAP_KF_L3_IP_PROTO: W8, sparx5: is0/is2/es2, lan966x: is2 + * IPv4 frames: IP protocol. IPv6 frames: Next header, same as for IPV4 + * VCAP_KF_L3_OPTIONS_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * Set if IPv4 frame contains options (IP len > 5) + * VCAP_KF_L3_PAYLOAD: sparx5 is2 W96, sparx5 is2 W40, sparx5 es2 W96, + * lan966x is2 W56 + * Sparx5: Payload bytes after IP header. IPv4: IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header, LAN966x: + * Bytes 0-6 after IP header + * VCAP_KF_L3_RT_IS: W1, sparx5: is2/es2 + * Set if frame has hit a router leg + * VCAP_KF_L3_SMAC_SIP_MATCH: W1, sparx5: is2 + * Match found in SIP security lookup in ANA_L3 + * VCAP_KF_L3_TOS: W8, sparx5: is2/es2, lan966x: is2 + * Sparx5: Frame's IPv4/IPv6 DSCP and ECN fields, LAN966x: IP TOS field + * VCAP_KF_L3_TTL_GT0: W1, sparx5: is2/es2, lan966x: is2 + * Set if IPv4 TTL / IPv6 hop limit is greater than 0 + * VCAP_KF_L4_ACK: W1, sparx5: is2/es2, lan966x: is2 + * Sparx5 and LAN966x: TCP flag ACK, LAN966x only: PTP over UDP: flagField bit 2 + * (unicastFlag) + * VCAP_KF_L4_DPORT: W16, sparx5: is2/es2, lan966x: is2 + * Sparx5: TCP/UDP destination port. Overloading for IP_7TUPLE: Non-TCP/UDP IP + * frames: L4_DPORT = L3_IP_PROTO, LAN966x: TCP/UDP destination port + * VCAP_KF_L4_FIN: W1, sparx5: is2/es2 + * TCP flag FIN, LAN966x: TCP flag FIN, and for PTP over UDP: messageType bit 1 + * VCAP_KF_L4_PAYLOAD: W64, sparx5: is2/es2 + * Payload bytes after TCP/UDP header Overloading for IP_7TUPLE: Non TCP/UDP + * frames: Payload bytes 0–7 after IP header. IPv4 options are not parsed so + * payload is always taken 20 bytes after the start of the IPv4 header for non + * TCP/UDP IPv4 frames + * VCAP_KF_L4_PSH: W1, sparx5: is2/es2, lan966x: is2 + * Sparx5: TCP flag PSH, LAN966x: TCP: TCP flag PSH. PTP over UDP: flagField bit + * 1 (twoStepFlag) + * VCAP_KF_L4_RNG: sparx5 is0 W8, sparx5 is2 W16, sparx5 es2 W16, lan966x: is2 + * Range checker bitmask (one for each range checker). Input into range checkers + * is taken from classified results (VID, DSCP) and frame (SPORT, DPORT, ETYPE, + * outer VID, inner VID) + * VCAP_KF_L4_RST: W1, sparx5: is2/es2, lan966x: is2 + * Sparx5: TCP flag RST , LAN966x: TCP: TCP flag RST. PTP over UDP: messageType + * bit 3 + * VCAP_KF_L4_SEQUENCE_EQ0_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if TCP sequence number is 0, LAN966x: Overlayed with PTP over UDP: + * messageType bit 0 + * VCAP_KF_L4_SPORT: W16, sparx5: is0/is2/es2, lan966x: is2 + * TCP/UDP source port + * VCAP_KF_L4_SPORT_EQ_DPORT_IS: W1, sparx5: is2/es2, lan966x: is2 + * Set if UDP or TCP source port equals UDP or TCP destination port + * VCAP_KF_L4_SYN: W1, sparx5: is2/es2, lan966x: is2 + * Sparx5: TCP flag SYN, LAN966x: TCP: TCP flag SYN. PTP over UDP: messageType + * bit 2 + * VCAP_KF_L4_URG: W1, sparx5: is2/es2, lan966x: is2 + * Sparx5: TCP flag URG, LAN966x: TCP: TCP flag URG. PTP over UDP: flagField bit + * 7 (reserved) + * VCAP_KF_LOOKUP_FIRST_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * Selects between entries relevant for first and second lookup. Set for first + * lookup, cleared for second lookup. + * VCAP_KF_LOOKUP_GEN_IDX: W12, sparx5: is0 + * Generic index - for chaining CLM instances + * VCAP_KF_LOOKUP_GEN_IDX_SEL: W2, sparx5: is0 + * Select the mode of the Generic Index + * VCAP_KF_LOOKUP_PAG: W8, sparx5: is2, lan966x: is2 + * Classified Policy Association Group: chains rules from IS1/CLM to IS2 + * VCAP_KF_OAM_CCM_CNTS_EQ0: W1, sparx5: is2/es2, lan966x: is2 + * Dual-ended loss measurement counters in CCM frames are all zero + * VCAP_KF_OAM_MEL_FLAGS: W7, sparx5: is0, lan966x: is2 + * Encoding of MD level/MEG level (MEL) + * VCAP_KF_OAM_Y1731_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * Set if frame’s EtherType = 0x8902 + * VCAP_KF_PROT_ACTIVE: W1, sparx5: es2 + * Protection is active + * VCAP_KF_TCP_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * Set if frame is IPv4 TCP frame (IP protocol = 6) or IPv6 TCP frames (Next + * header = 6) + * VCAP_KF_TCP_UDP_IS: W1, sparx5: is0/is2/es2, lan966x: is2 + * Set if frame is IPv4/IPv6 TCP or UDP frame (IP protocol/next header equals 6 + * or 17) + * VCAP_KF_TYPE: sparx5 is0 W2, sparx5 is0 W1, sparx5 is2 W4, sparx5 is2 W2, + * sparx5 es2 W3, lan966x: is2 + * Keyset type id - set by the API + * VCAP_KF_HOST_MATCH: W1, lan966x: is2 + * The action from the SMAC_SIP4 or SMAC_SIP6 lookups. Used for IP source + * guarding. + * VCAP_KF_L2_FRM_TYPE: W4, lan966x: is2 + * Frame subtype for specific EtherTypes (MRP, DLR) + * VCAP_KF_L2_PAYLOAD0: W16, lan966x: is2 + * Payload bytes 0-1 after the frame’s EtherType + * VCAP_KF_L2_PAYLOAD1: W8, lan966x: is2 + * Payload byte 4 after the frame’s EtherType. This is specifically for PTP + * frames. + * VCAP_KF_L2_PAYLOAD2: W3, lan966x: is2 + * Bits 7, 2, and 1 from payload byte 6 after the frame’s EtherType. This is + * specifically for PTP frames. + * VCAP_KF_L2_LLC: W40, lan966x: is2 + * LLC header and data after up to two VLAN tags and the type/length field + * VCAP_KF_L3_FRAGMENT: W1, lan966x: is2 + * Set if IPv4 frame is fragmented + * VCAP_KF_L3_FRAG_OFS_GT0: W1, lan966x: is2 + * Set if IPv4 frame is fragmented and it is not the first fragment + * VCAP_KF_L2_SNAP: W40, lan966x: is2 + * SNAP header after LLC header (AA-AA-03) + * VCAP_KF_L4_1588_DOM: W8, lan966x: is2 + * PTP over UDP: domainNumber + * VCAP_KF_L4_1588_VER: W4, lan966x: is2 + * PTP over UDP: version + * VCAP_KF_OAM_MEPID: W16, lan966x: is2 + * CCM frame’s OAM MEP ID + * VCAP_KF_OAM_OPCODE: W8, lan966x: is2 + * Frame’s OAM opcode + * VCAP_KF_OAM_VER: W5, lan966x: is2 + * Frame’s OAM version + * VCAP_KF_OAM_FLAGS: W8, lan966x: is2 + * Frame’s OAM flags + * VCAP_KF_OAM_DETECTED: W1, lan966x: is2 + * This is missing in the datasheet, but present in the OAM keyset in XML + */ + +/* Keyfield names */ +enum vcap_key_field { + VCAP_KF_NO_VALUE, /* initial value */ + VCAP_KF_8021BR_ECID_BASE, + VCAP_KF_8021BR_ECID_EXT, + VCAP_KF_8021BR_E_TAGGED, + VCAP_KF_8021BR_GRP, + VCAP_KF_8021BR_IGR_ECID_BASE, + VCAP_KF_8021BR_IGR_ECID_EXT, + VCAP_KF_8021Q_DEI0, + VCAP_KF_8021Q_DEI1, + VCAP_KF_8021Q_DEI2, + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP0, + VCAP_KF_8021Q_PCP1, + VCAP_KF_8021Q_PCP2, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_TPID0, + VCAP_KF_8021Q_TPID1, + VCAP_KF_8021Q_TPID2, + VCAP_KF_8021Q_VID0, + VCAP_KF_8021Q_VID1, + VCAP_KF_8021Q_VID2, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_8021Q_VLAN_TAGGED_IS, + VCAP_KF_8021Q_VLAN_TAGS, + VCAP_KF_ACL_GRP_ID, + VCAP_KF_ARP_ADDR_SPACE_OK_IS, + VCAP_KF_ARP_LEN_OK_IS, + VCAP_KF_ARP_OPCODE, + VCAP_KF_ARP_OPCODE_UNKNOWN_IS, + VCAP_KF_ARP_PROTO_SPACE_OK_IS, + VCAP_KF_ARP_SENDER_MATCH_IS, + VCAP_KF_ARP_TGT_MATCH_IS, + VCAP_KF_COSID_CLS, + VCAP_KF_DST_ENTRY, + VCAP_KF_ES0_ISDX_KEY_ENA, + VCAP_KF_ETYPE, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_ETYPE_MPLS, + VCAP_KF_IF_EGR_PORT_MASK, + VCAP_KF_IF_EGR_PORT_MASK_RNG, + VCAP_KF_IF_IGR_PORT, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_IF_IGR_PORT_MASK_L3, + VCAP_KF_IF_IGR_PORT_MASK_RNG, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_SEL, + VCAP_KF_IP4_IS, + VCAP_KF_IP_MC_IS, + VCAP_KF_IP_PAYLOAD_5TUPLE, + VCAP_KF_IP_SNAP_IS, + VCAP_KF_ISDX_CLS, + VCAP_KF_ISDX_GT0_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_FWD_IS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_PAYLOAD_ETYPE, + VCAP_KF_L2_SMAC, + VCAP_KF_L3_DIP_EQ_SIP_IS, + VCAP_KF_L3_DMAC_DIP_MATCH, + VCAP_KF_L3_DPL_CLS, + VCAP_KF_L3_DSCP, + VCAP_KF_L3_DST_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_IP4_DIP, + VCAP_KF_L3_IP4_SIP, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_L3_IP_PROTO, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_PAYLOAD, + VCAP_KF_L3_RT_IS, + VCAP_KF_L3_SMAC_SIP_MATCH, + VCAP_KF_L3_TOS, + VCAP_KF_L3_TTL_GT0, + VCAP_KF_L4_ACK, + VCAP_KF_L4_DPORT, + VCAP_KF_L4_FIN, + VCAP_KF_L4_PAYLOAD, + VCAP_KF_L4_PSH, + VCAP_KF_L4_RNG, + VCAP_KF_L4_RST, + VCAP_KF_L4_SEQUENCE_EQ0_IS, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_SPORT_EQ_DPORT_IS, + VCAP_KF_L4_SYN, + VCAP_KF_L4_URG, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_GEN_IDX, + VCAP_KF_LOOKUP_GEN_IDX_SEL, + VCAP_KF_LOOKUP_PAG, + VCAP_KF_MIRROR_ENA, + VCAP_KF_OAM_CCM_CNTS_EQ0, + VCAP_KF_OAM_MEL_FLAGS, + VCAP_KF_OAM_Y1731_IS, + VCAP_KF_PROT_ACTIVE, + VCAP_KF_TCP_IS, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TYPE, + VCAP_KF_HOST_MATCH, + VCAP_KF_L2_FRM_TYPE, + VCAP_KF_L2_PAYLOAD0, + VCAP_KF_L2_PAYLOAD1, + VCAP_KF_L2_PAYLOAD2, + VCAP_KF_L2_LLC, + VCAP_KF_L3_FRAGMENT, + VCAP_KF_L3_FRAG_OFS_GT0, + VCAP_KF_L2_SNAP, + VCAP_KF_L4_1588_DOM, + VCAP_KF_L4_1588_VER, + VCAP_KF_OAM_MEPID, + VCAP_KF_OAM_OPCODE, + VCAP_KF_OAM_VER, + VCAP_KF_OAM_FLAGS, + VCAP_KF_OAM_DETECTED, +}; + +/* Actionset names with origin information */ +enum vcap_actionfield_set { + VCAP_AFS_NO_VALUE, /* initial value */ + VCAP_AFS_BASE_TYPE, /* sparx5 is2 X3, sparx5 es2 X3, lan966x is2 X2 */ + VCAP_AFS_CLASSIFICATION, /* sparx5 is0 X2 */ + VCAP_AFS_CLASS_REDUCED, /* sparx5 is0 X1 */ + VCAP_AFS_FULL, /* sparx5 is0 X3 */ + VCAP_AFS_MLBS, /* sparx5 is0 X2 */ + VCAP_AFS_MLBS_REDUCED, /* sparx5 is0 X1 */ + VCAP_AFS_SMAC_SIP, /* lan966x is2 x1 */ +}; + +/* List of actionfields with description + * + * VCAP_AF_CLS_VID_SEL: W3, sparx5: is0 + * Controls the classified VID: 0: VID_NONE: No action. 1: VID_ADD: New VID = + * old VID + VID_VAL. 2: VID_REPLACE: New VID = VID_VAL. 3: VID_FIRST_TAG: New + * VID = VID from frame's first tag (outer tag) if available, otherwise VID_VAL. + * 4: VID_SECOND_TAG: New VID = VID from frame's second tag (middle tag) if + * available, otherwise VID_VAL. 5: VID_THIRD_TAG: New VID = VID from frame's + * third tag (inner tag) if available, otherwise VID_VAL. + * VCAP_AF_CNT_ID: sparx5 is2 W12, sparx5 es2 W11 + * Counter ID, used per lookup to index the 4K frame counters (ANA_ACL:CNT_TBL). + * Multiple VCAP IS2 entries can use the same counter. + * VCAP_AF_COPY_PORT_NUM: W7, sparx5: es2 + * QSYS port number when FWD_MODE is redirect or copy + * VCAP_AF_COPY_QUEUE_NUM: W16, sparx5: es2 + * QSYS queue number when FWD_MODE is redirect or copy + * VCAP_AF_CPU_COPY_ENA: W1, sparx5: is2/es2, lan966x: is2 + * Setting this bit to 1 causes all frames that hit this action to be copied to + * the CPU extraction queue specified in CPU_QUEUE_NUM. + * VCAP_AF_CPU_QUEUE_NUM: W3, sparx5: is2/es2, lan966x: is2 + * CPU queue number. Used when CPU_COPY_ENA is set. + * VCAP_AF_DEI_ENA: W1, sparx5: is0 + * If set, use DEI_VAL as classified DEI value. Otherwise, DEI from basic + * classification is used + * VCAP_AF_DEI_VAL: W1, sparx5: is0 + * See DEI_ENA + * VCAP_AF_DP_ENA: W1, sparx5: is0 + * If set, use DP_VAL as classified drop precedence level. Otherwise, drop + * precedence level from basic classification is used. + * VCAP_AF_DP_VAL: W2, sparx5: is0 + * See DP_ENA. + * VCAP_AF_DSCP_ENA: W1, sparx5: is0 + * If set, use DSCP_VAL as classified DSCP value. Otherwise, DSCP value from + * basic classification is used. + * VCAP_AF_DSCP_VAL: W6, sparx5: is0 + * See DSCP_ENA. + * VCAP_AF_ES2_REW_CMD: W3, sparx5: es2 + * Command forwarded to REW: 0: No action. 1: SWAP MAC addresses. 2: Do L2CP + * DMAC translation when entering or leaving a tunnel. + * VCAP_AF_FWD_MODE: W2, sparx5: es2 + * Forward selector: 0: Forward. 1: Discard. 2: Redirect. 3: Copy. + * VCAP_AF_HIT_ME_ONCE: W1, sparx5: is2/es2, lan966x: is2 + * Setting this bit to 1 causes the first frame that hits this action where the + * HIT_CNT counter is zero to be copied to the CPU extraction queue specified in + * CPU_QUEUE_NUM. The HIT_CNT counter is then incremented and any frames that + * hit this action later are not copied to the CPU. To re-enable the HIT_ME_ONCE + * functionality, the HIT_CNT counter must be cleared. + * VCAP_AF_IGNORE_PIPELINE_CTRL: W1, sparx5: is2/es2 + * Ignore ingress pipeline control. This enforces the use of the VCAP IS2 action + * even when the pipeline control has terminated the frame before VCAP IS2. + * VCAP_AF_INTR_ENA: W1, sparx5: is2/es2 + * If set, an interrupt is triggered when this rule is hit + * VCAP_AF_ISDX_ADD_REPLACE_SEL: W1, sparx5: is0 + * Controls the classified ISDX. 0: New ISDX = old ISDX + ISDX_VAL. 1: New ISDX + * = ISDX_VAL. + * VCAP_AF_ISDX_VAL: W12, sparx5: is0 + * See isdx_add_replace_sel + * VCAP_AF_LRN_DIS: W1, sparx5: is2, lan966x: is2 + * Setting this bit to 1 disables learning of frames hitting this action. + * VCAP_AF_MAP_IDX: W9, sparx5: is0 + * Index for QoS mapping table lookup + * VCAP_AF_MAP_KEY: W3, sparx5: is0 + * Key type for QoS mapping table lookup. 0: DEI0, PCP0 (outer tag). 1: DEI1, + * PCP1 (middle tag). 2: DEI2, PCP2 (inner tag). 3: MPLS TC. 4: PCP0 (outer + * tag). 5: E-DEI, E-PCP (E-TAG). 6: DSCP if available, otherwise none. 7: DSCP + * if available, otherwise DEI0, PCP0 (outer tag) if available using MAP_IDX+8, + * otherwise none + * VCAP_AF_MAP_LOOKUP_SEL: W2, sparx5: is0 + * Selects which of the two QoS Mapping Table lookups that MAP_KEY and MAP_IDX + * are applied to. 0: No changes to the QoS Mapping Table lookup. 1: Update key + * type and index for QoS Mapping Table lookup #0. 2: Update key type and index + * for QoS Mapping Table lookup #1. 3: Reserved. + * VCAP_AF_MASK_MODE: W3, sparx5: is0/is2, lan966x is2 W2 + * Controls the PORT_MASK use. Sparx5: 0: OR_DSTMASK, 1: AND_VLANMASK, 2: + * REPLACE_PGID, 3: REPLACE_ALL, 4: REDIR_PGID, 5: OR_PGID_MASK, 6: VSTAX, 7: + * Not applicable. LAN966X: 0: No action, 1: Permit/deny (AND), 2: Policy + * forwarding (DMAC lookup), 3: Redirect. The CPU port is untouched by + * MASK_MODE. + * VCAP_AF_MATCH_ID: W16, sparx5: is0/is2 + * Logical ID for the entry. The MATCH_ID is extracted together with the frame + * if the frame is forwarded to the CPU (CPU_COPY_ENA). The result is placed in + * IFH.CL_RSLT. + * VCAP_AF_MATCH_ID_MASK: W16, sparx5: is0/is2 + * Mask used by MATCH_ID. + * VCAP_AF_MIRROR_PROBE: W2, sparx5: is2 + * Mirroring performed according to configuration of a mirror probe. 0: No + * mirroring. 1: Mirror probe 0. 2: Mirror probe 1. 3: Mirror probe 2 + * VCAP_AF_MIRROR_PROBE_ID: W2, sparx5: es2 + * Signals a mirror probe to be placed in the IFH. Only possible when FWD_MODE + * is copy. 0: No mirroring. 1–3: Use mirror probe 0-2. + * VCAP_AF_NXT_IDX: W12, sparx5: is0 + * Index used as part of key (field G_IDX) in the next lookup. + * VCAP_AF_NXT_IDX_CTRL: W3, sparx5: is0 + * Controls the generation of the G_IDX used in the VCAP CLM next lookup + * VCAP_AF_PAG_OVERRIDE_MASK: W8, sparx5: is0 + * Bits set in this mask will override PAG_VAL from port profile. ï€ New PAG = + * (PAG (input) AND ~PAG_OVERRIDE_MASK) OR (PAG_VAL AND PAG_OVERRIDE_MASK) + * VCAP_AF_PAG_VAL: W8, sparx5: is0 + * See PAG_OVERRIDE_MASK. + * VCAP_AF_PCP_ENA: W1, sparx5: is0 + * If set, use PCP_VAL as classified PCP value. Otherwise, PCP from basic + * classification is used. + * VCAP_AF_PCP_VAL: W3, sparx5: is0 + * See PCP_ENA. + * VCAP_AF_PIPELINE_FORCE_ENA: sparx5 is0 W2, sparx5 is2 W1 + * If set, use PIPELINE_PT unconditionally and set PIPELINE_ACT = NONE if + * PIPELINE_PT == NONE. Overrules previous settings of pipeline point. + * VCAP_AF_PIPELINE_PT: W5, sparx5: is0/is2 + * Pipeline point used if PIPELINE_FORCE_ENA is set + * VCAP_AF_POLICE_ENA: W1, sparx5: is2/es2, lan966x: is2 + * Setting this bit to 1 causes frames that hit this action to be policed by the + * ACL policer specified in POLICE_IDX. Only applies to the first lookup. + * VCAP_AF_POLICE_IDX: W6, sparx5: is2/es2, lan966x: is2 W9 + * Selects VCAP policer used when policing frames (POLICE_ENA) + * VCAP_AF_POLICE_REMARK: W1, sparx5: es2 + * If set, frames exceeding policer rates are marked as yellow but not + * discarded. + * VCAP_AF_PORT_MASK: sparx5 is0 W65, sparx5 is2 W68, lan966x is2 W8 + * Port mask applied to the forwarding decision based on MASK_MODE. + * VCAP_AF_QOS_ENA: W1, sparx5: is0 + * If set, use QOS_VAL as classified QoS class. Otherwise, QoS class from basic + * classification is used. + * VCAP_AF_QOS_VAL: W3, sparx5: is0 + * See QOS_ENA. + * VCAP_AF_RT_DIS: W1, sparx5: is2 + * If set, routing is disallowed. Only applies when IS_INNER_ACL is 0. See also + * IGR_ACL_ENA, EGR_ACL_ENA, and RLEG_STAT_IDX. + * VCAP_AF_TYPE: W1, sparx5: is0 + * Actionset type id - Set by the API + * VCAP_AF_VID_VAL: W13, sparx5: is0 + * New VID Value + * VCAP_AF_MIRROR_ENA: W1, lan966x: is2 + * Setting this bit to 1 causes frames to be mirrored to the mirror target + * port (ANA::MIRRPORPORTS). + * VCAP_AF_POLICE_VCAP_ONLY: W1, lan966x: is2 + * Disable policing from QoS, and port policers. Only the VCAP policer + * selected by POLICE_IDX is active. Only applies to the second lookup. + * VCAP_AF_REW_OP: W16, lan966x: is2 + * Rewriter operation command. + * VCAP_AF_ISDX_ENA: W1, lan966x: is2 + * Setting this bit to 1 causes the classified ISDX to be set to the value of + * POLICE_IDX[8:0]. + * VCAP_AF_ACL_ID: W6, lan966x: is2 + * Logical ID for the entry. This ID is extracted together with the frame in + * the CPU extraction header. Only applicable to actions with CPU_COPY_ENA or + * HIT_ME_ONCE set. + * VCAP_AF_FWD_KILL_ENA: W1, lan966x: is2 + * Setting this bit to 1 denies forwarding of the frame forwarding to any + * front port. The frame can still be copied to the CPU by other actions. + * VCAP_AF_HOST_MATCH: W1, lan966x: is2 + * Used for IP source guarding. If set, it signals that the host is a valid + * (for instance a valid combination of source MAC address and source IP + * address). HOST_MATCH is input to the IS2 keys. + */ + +/* Actionfield names */ +enum vcap_action_field { + VCAP_AF_NO_VALUE, /* initial value */ + VCAP_AF_ACL_MAC, + VCAP_AF_ACL_RT_MODE, + VCAP_AF_CLS_VID_SEL, + VCAP_AF_CNT_ID, + VCAP_AF_COPY_PORT_NUM, + VCAP_AF_COPY_QUEUE_NUM, + VCAP_AF_COSID_ENA, + VCAP_AF_COSID_VAL, + VCAP_AF_CPU_COPY_ENA, + VCAP_AF_CPU_DIS, + VCAP_AF_CPU_ENA, + VCAP_AF_CPU_Q, + VCAP_AF_CPU_QUEUE_NUM, + VCAP_AF_CUSTOM_ACE_ENA, + VCAP_AF_CUSTOM_ACE_OFFSET, + VCAP_AF_DEI_ENA, + VCAP_AF_DEI_VAL, + VCAP_AF_DLB_OFFSET, + VCAP_AF_DMAC_OFFSET_ENA, + VCAP_AF_DP_ENA, + VCAP_AF_DP_VAL, + VCAP_AF_DSCP_ENA, + VCAP_AF_DSCP_VAL, + VCAP_AF_EGR_ACL_ENA, + VCAP_AF_ES2_REW_CMD, + VCAP_AF_FWD_DIS, + VCAP_AF_FWD_MODE, + VCAP_AF_FWD_TYPE, + VCAP_AF_GVID_ADD_REPLACE_SEL, + VCAP_AF_HIT_ME_ONCE, + VCAP_AF_IGNORE_PIPELINE_CTRL, + VCAP_AF_IGR_ACL_ENA, + VCAP_AF_INJ_MASQ_ENA, + VCAP_AF_INJ_MASQ_LPORT, + VCAP_AF_INJ_MASQ_PORT, + VCAP_AF_INTR_ENA, + VCAP_AF_ISDX_ADD_REPLACE_SEL, + VCAP_AF_ISDX_VAL, + VCAP_AF_IS_INNER_ACL, + VCAP_AF_L3_MAC_UPDATE_DIS, + VCAP_AF_LOG_MSG_INTERVAL, + VCAP_AF_LPM_AFFIX_ENA, + VCAP_AF_LPM_AFFIX_VAL, + VCAP_AF_LPORT_ENA, + VCAP_AF_LRN_DIS, + VCAP_AF_MAP_IDX, + VCAP_AF_MAP_KEY, + VCAP_AF_MAP_LOOKUP_SEL, + VCAP_AF_MASK_MODE, + VCAP_AF_MATCH_ID, + VCAP_AF_MATCH_ID_MASK, + VCAP_AF_MIP_SEL, + VCAP_AF_MIRROR_PROBE, + VCAP_AF_MIRROR_PROBE_ID, + VCAP_AF_MPLS_IP_CTRL_ENA, + VCAP_AF_MPLS_MEP_ENA, + VCAP_AF_MPLS_MIP_ENA, + VCAP_AF_MPLS_OAM_FLAVOR, + VCAP_AF_MPLS_OAM_TYPE, + VCAP_AF_NUM_VLD_LABELS, + VCAP_AF_NXT_IDX, + VCAP_AF_NXT_IDX_CTRL, + VCAP_AF_NXT_KEY_TYPE, + VCAP_AF_NXT_NORMALIZE, + VCAP_AF_NXT_NORM_W16_OFFSET, + VCAP_AF_NXT_NORM_W32_OFFSET, + VCAP_AF_NXT_OFFSET_FROM_TYPE, + VCAP_AF_NXT_TYPE_AFTER_OFFSET, + VCAP_AF_OAM_IP_BFD_ENA, + VCAP_AF_OAM_TWAMP_ENA, + VCAP_AF_OAM_Y1731_SEL, + VCAP_AF_PAG_OVERRIDE_MASK, + VCAP_AF_PAG_VAL, + VCAP_AF_PCP_ENA, + VCAP_AF_PCP_VAL, + VCAP_AF_PIPELINE_ACT_SEL, + VCAP_AF_PIPELINE_FORCE_ENA, + VCAP_AF_PIPELINE_PT, + VCAP_AF_PIPELINE_PT_REDUCED, + VCAP_AF_POLICE_ENA, + VCAP_AF_POLICE_IDX, + VCAP_AF_POLICE_REMARK, + VCAP_AF_PORT_MASK, + VCAP_AF_PTP_MASTER_SEL, + VCAP_AF_QOS_ENA, + VCAP_AF_QOS_VAL, + VCAP_AF_REW_CMD, + VCAP_AF_RLEG_DMAC_CHK_DIS, + VCAP_AF_RLEG_STAT_IDX, + VCAP_AF_RSDX_ENA, + VCAP_AF_RSDX_VAL, + VCAP_AF_RSVD_LBL_VAL, + VCAP_AF_RT_DIS, + VCAP_AF_RT_SEL, + VCAP_AF_S2_KEY_SEL_ENA, + VCAP_AF_S2_KEY_SEL_IDX, + VCAP_AF_SAM_SEQ_ENA, + VCAP_AF_SIP_IDX, + VCAP_AF_SWAP_MAC_ENA, + VCAP_AF_TCP_UDP_DPORT, + VCAP_AF_TCP_UDP_ENA, + VCAP_AF_TCP_UDP_SPORT, + VCAP_AF_TC_ENA, + VCAP_AF_TC_LABEL, + VCAP_AF_TPID_SEL, + VCAP_AF_TTL_DECR_DIS, + VCAP_AF_TTL_ENA, + VCAP_AF_TTL_LABEL, + VCAP_AF_TTL_UPDATE_ENA, + VCAP_AF_TYPE, + VCAP_AF_VID_VAL, + VCAP_AF_VLAN_POP_CNT, + VCAP_AF_VLAN_POP_CNT_ENA, + VCAP_AF_VLAN_PUSH_CNT, + VCAP_AF_VLAN_PUSH_CNT_ENA, + VCAP_AF_VLAN_WAS_TAGGED, + VCAP_AF_MIRROR_ENA, + VCAP_AF_POLICE_VCAP_ONLY, + VCAP_AF_REW_OP, + VCAP_AF_ISDX_ENA, + VCAP_AF_ACL_ID, + VCAP_AF_FWD_KILL_ENA, + VCAP_AF_HOST_MATCH, +}; + +#endif /* __VCAP_AG_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c new file mode 100644 index 000000000000..664aae3e2acd --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -0,0 +1,2883 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + */ + +#include <linux/types.h> + +#include "vcap_api_private.h" + +static int keyfield_size_table[] = { + [VCAP_FIELD_BIT] = sizeof(struct vcap_u1_key), + [VCAP_FIELD_U32] = sizeof(struct vcap_u32_key), + [VCAP_FIELD_U48] = sizeof(struct vcap_u48_key), + [VCAP_FIELD_U56] = sizeof(struct vcap_u56_key), + [VCAP_FIELD_U64] = sizeof(struct vcap_u64_key), + [VCAP_FIELD_U72] = sizeof(struct vcap_u72_key), + [VCAP_FIELD_U112] = sizeof(struct vcap_u112_key), + [VCAP_FIELD_U128] = sizeof(struct vcap_u128_key), +}; + +static int actionfield_size_table[] = { + [VCAP_FIELD_BIT] = sizeof(struct vcap_u1_action), + [VCAP_FIELD_U32] = sizeof(struct vcap_u32_action), + [VCAP_FIELD_U48] = sizeof(struct vcap_u48_action), + [VCAP_FIELD_U56] = sizeof(struct vcap_u56_action), + [VCAP_FIELD_U64] = sizeof(struct vcap_u64_action), + [VCAP_FIELD_U72] = sizeof(struct vcap_u72_action), + [VCAP_FIELD_U112] = sizeof(struct vcap_u112_action), + [VCAP_FIELD_U128] = sizeof(struct vcap_u128_action), +}; + +/* Moving a rule in the VCAP address space */ +struct vcap_rule_move { + int addr; /* address to move */ + int offset; /* change in address */ + int count; /* blocksize of addresses to move */ +}; + +/* Stores the filter cookie that enabled the port */ +struct vcap_enabled_port { + struct list_head list; /* for insertion in enabled ports list */ + struct net_device *ndev; /* the enabled port */ + unsigned long cookie; /* filter that enabled the port */ +}; + +void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset) +{ + memset(itr, 0, sizeof(*itr)); + itr->offset = offset; + itr->sw_width = sw_width; + itr->regs_per_sw = DIV_ROUND_UP(sw_width, 32); + itr->tg = tg; +} + +static void vcap_iter_skip_tg(struct vcap_stream_iter *itr) +{ + /* Compensate the field offset for preceding typegroups. + * A typegroup table ends with an all-zero terminator. + */ + while (itr->tg->width && itr->offset >= itr->tg->offset) { + itr->offset += itr->tg->width; + itr->tg++; /* next typegroup */ + } +} + +void vcap_iter_update(struct vcap_stream_iter *itr) +{ + int sw_idx, sw_bitpos; + + /* Calculate the subword index and bitposition for current bit */ + sw_idx = itr->offset / itr->sw_width; + sw_bitpos = itr->offset % itr->sw_width; + /* Calculate the register index and bitposition for current bit */ + itr->reg_idx = (sw_idx * itr->regs_per_sw) + (sw_bitpos / 32); + itr->reg_bitpos = sw_bitpos % 32; +} + +void vcap_iter_init(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset) +{ + vcap_iter_set(itr, sw_width, tg, offset); + vcap_iter_skip_tg(itr); + vcap_iter_update(itr); +} + +void vcap_iter_next(struct vcap_stream_iter *itr) +{ + itr->offset++; + vcap_iter_skip_tg(itr); + vcap_iter_update(itr); +} + +static void vcap_set_bit(u32 *stream, struct vcap_stream_iter *itr, bool value) +{ + u32 mask = BIT(itr->reg_bitpos); + u32 *p = &stream[itr->reg_idx]; + + if (value) + *p |= mask; + else + *p &= ~mask; +} + +static void vcap_encode_bit(u32 *stream, struct vcap_stream_iter *itr, bool val) +{ + /* When intersected by a type group field, stream the type group bits + * before continuing with the value bit + */ + while (itr->tg->width && + itr->offset >= itr->tg->offset && + itr->offset < itr->tg->offset + itr->tg->width) { + int tg_bitpos = itr->tg->offset - itr->offset; + + vcap_set_bit(stream, itr, (itr->tg->value >> tg_bitpos) & 0x1); + itr->offset++; + vcap_iter_update(itr); + } + vcap_set_bit(stream, itr, val); +} + +static void vcap_encode_field(u32 *stream, struct vcap_stream_iter *itr, + int width, const u8 *value) +{ + int idx; + + /* Loop over the field value bits and add the value bits one by one to + * the output stream. + */ + for (idx = 0; idx < width; idx++) { + u8 bidx = idx & GENMASK(2, 0); + + /* Encode one field value bit */ + vcap_encode_bit(stream, itr, (value[idx / 8] >> bidx) & 0x1); + vcap_iter_next(itr); + } +} + +static void vcap_encode_typegroups(u32 *stream, int sw_width, + const struct vcap_typegroup *tg, + bool mask) +{ + struct vcap_stream_iter iter; + int idx; + + /* Mask bits must be set to zeros (inverted later when writing to the + * mask cache register), so that the mask typegroup bits consist of + * match-1 or match-0, or both + */ + vcap_iter_set(&iter, sw_width, tg, 0); + while (iter.tg->width) { + /* Set position to current typegroup bit */ + iter.offset = iter.tg->offset; + vcap_iter_update(&iter); + for (idx = 0; idx < iter.tg->width; idx++) { + /* Iterate over current typegroup bits. Mask typegroup + * bits are always set + */ + if (mask) + vcap_set_bit(stream, &iter, 0x1); + else + vcap_set_bit(stream, &iter, + (iter.tg->value >> idx) & 0x1); + iter.offset++; + vcap_iter_update(&iter); + } + iter.tg++; /* next typegroup */ + } +} + +static bool vcap_bitarray_zero(int width, u8 *value) +{ + int bytes = DIV_ROUND_UP(width, BITS_PER_BYTE); + u8 total = 0, bmask = 0xff; + int rwidth = width; + int idx; + + for (idx = 0; idx < bytes; ++idx, rwidth -= BITS_PER_BYTE) { + if (rwidth && rwidth < BITS_PER_BYTE) + bmask = (1 << rwidth) - 1; + total += value[idx] & bmask; + } + return total == 0; +} + +static bool vcap_get_bit(u32 *stream, struct vcap_stream_iter *itr) +{ + u32 mask = BIT(itr->reg_bitpos); + u32 *p = &stream[itr->reg_idx]; + + return !!(*p & mask); +} + +static void vcap_decode_field(u32 *stream, struct vcap_stream_iter *itr, + int width, u8 *value) +{ + int idx; + + /* Loop over the field value bits and get the field bits and + * set them in the output value byte array + */ + for (idx = 0; idx < width; idx++) { + u8 bidx = idx & 0x7; + + /* Decode one field value bit */ + if (vcap_get_bit(stream, itr)) + *value |= 1 << bidx; + vcap_iter_next(itr); + if (bidx == 7) + value++; + } +} + +/* Verify that the type id in the stream matches the type id of the keyset */ +static bool vcap_verify_keystream_keyset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *keystream, + u32 *mskstream, + enum vcap_keyfield_set keyset) +{ + const struct vcap_info *vcap = &vctrl->vcaps[vt]; + const struct vcap_field *typefld; + const struct vcap_typegroup *tgt; + const struct vcap_field *fields; + struct vcap_stream_iter iter; + const struct vcap_set *info; + u32 value = 0; + u32 mask = 0; + + if (vcap_keyfield_count(vctrl, vt, keyset) == 0) + return false; + + info = vcap_keyfieldset(vctrl, vt, keyset); + /* Check that the keyset is valid */ + if (!info) + return false; + + /* a type_id of value -1 means that there is no type field */ + if (info->type_id == (u8)-1) + return true; + + /* Get a valid typegroup for the specific keyset */ + tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); + if (!tgt) + return false; + + fields = vcap_keyfields(vctrl, vt, keyset); + if (!fields) + return false; + + typefld = &fields[VCAP_KF_TYPE]; + vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); + vcap_decode_field(mskstream, &iter, typefld->width, (u8 *)&mask); + /* no type info if there are no mask bits */ + if (vcap_bitarray_zero(typefld->width, (u8 *)&mask)) + return false; + + /* Get the value of the type field in the stream and compare to the + * one define in the vcap keyset + */ + vcap_iter_init(&iter, vcap->sw_width, tgt, typefld->offset); + vcap_decode_field(keystream, &iter, typefld->width, (u8 *)&value); + + return (value & mask) == (info->type_id & mask); +} + +/* Verify that the typegroup bits have the correct values */ +static int vcap_verify_typegroups(u32 *stream, int sw_width, + const struct vcap_typegroup *tgt, bool mask, + int sw_max) +{ + struct vcap_stream_iter iter; + int sw_cnt, idx; + + vcap_iter_set(&iter, sw_width, tgt, 0); + sw_cnt = 0; + while (iter.tg->width) { + u32 value = 0; + u32 tg_value = iter.tg->value; + + if (mask) + tg_value = (1 << iter.tg->width) - 1; + /* Set position to current typegroup bit */ + iter.offset = iter.tg->offset; + vcap_iter_update(&iter); + for (idx = 0; idx < iter.tg->width; idx++) { + /* Decode one typegroup bit */ + if (vcap_get_bit(stream, &iter)) + value |= 1 << idx; + iter.offset++; + vcap_iter_update(&iter); + } + if (value != tg_value) + return -EINVAL; + iter.tg++; /* next typegroup */ + sw_cnt++; + /* Stop checking more typegroups */ + if (sw_max && sw_cnt >= sw_max) + break; + } + return 0; +} + +/* Find the subword width of the key typegroup that matches the stream data */ +static int vcap_find_keystream_typegroup_sw(struct vcap_control *vctrl, + enum vcap_type vt, u32 *stream, + bool mask, int sw_max) +{ + const struct vcap_typegroup **tgt; + int sw_idx, res; + + tgt = vctrl->vcaps[vt].keyfield_set_typegroups; + /* Try the longest subword match first */ + for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { + if (!tgt[sw_idx]) + continue; + + res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].sw_width, + tgt[sw_idx], mask, sw_max); + if (res == 0) + return sw_idx; + } + return -EINVAL; +} + +/* Verify that the typegroup information, subword count, keyset and type id + * are in sync and correct, return the list of matchin keysets + */ +int +vcap_find_keystream_keysets(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *keystream, + u32 *mskstream, + bool mask, int sw_max, + struct vcap_keyset_list *kslist) +{ + const struct vcap_set *keyfield_set; + int sw_count, idx; + + sw_count = vcap_find_keystream_typegroup_sw(vctrl, vt, keystream, mask, + sw_max); + if (sw_count < 0) + return sw_count; + + keyfield_set = vctrl->vcaps[vt].keyfield_set; + for (idx = 0; idx < vctrl->vcaps[vt].keyfield_set_size; ++idx) { + if (keyfield_set[idx].sw_per_item != sw_count) + continue; + + if (vcap_verify_keystream_keyset(vctrl, vt, keystream, + mskstream, idx)) + vcap_keyset_list_add(kslist, idx); + } + if (kslist->cnt > 0) + return 0; + return -EINVAL; +} +EXPORT_SYMBOL_GPL(vcap_find_keystream_keysets); + +/* Read key data from a VCAP address and discover if there are any rule keysets + * here + */ +int vcap_addr_keysets(struct vcap_control *vctrl, + struct net_device *ndev, + struct vcap_admin *admin, + int addr, + struct vcap_keyset_list *kslist) +{ + enum vcap_type vt = admin->vtype; + int keyset_sw_regs, idx; + u32 key = 0, mask = 0; + + /* Read the cache at the specified address */ + keyset_sw_regs = DIV_ROUND_UP(vctrl->vcaps[vt].sw_width, 32); + vctrl->ops->update(ndev, admin, VCAP_CMD_READ, VCAP_SEL_ALL, addr); + vctrl->ops->cache_read(ndev, admin, VCAP_SEL_ENTRY, 0, + keyset_sw_regs); + /* Skip uninitialized key/mask entries */ + for (idx = 0; idx < keyset_sw_regs; ++idx) { + key |= ~admin->cache.keystream[idx]; + mask |= admin->cache.maskstream[idx]; + } + if (key == 0 && mask == 0) + return -EINVAL; + /* Decode and locate the keysets */ + return vcap_find_keystream_keysets(vctrl, vt, admin->cache.keystream, + admin->cache.maskstream, false, 0, + kslist); +} +EXPORT_SYMBOL_GPL(vcap_addr_keysets); + +/* Return the list of keyfields for the keyset */ +const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset) +{ + /* Check that the keyset exists in the vcap keyset list */ + if (keyset >= vctrl->vcaps[vt].keyfield_set_size) + return NULL; + return vctrl->vcaps[vt].keyfield_set_map[keyset]; +} + +/* Return the keyset information for the keyset */ +const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset) +{ + const struct vcap_set *kset; + + /* Check that the keyset exists in the vcap keyset list */ + if (keyset >= vctrl->vcaps[vt].keyfield_set_size) + return NULL; + kset = &vctrl->vcaps[vt].keyfield_set[keyset]; + if (kset->sw_per_item == 0 || kset->sw_per_item > vctrl->vcaps[vt].sw_count) + return NULL; + return kset; +} +EXPORT_SYMBOL_GPL(vcap_keyfieldset); + +/* Return the typegroup table for the matching keyset (using subword size) */ +const struct vcap_typegroup * +vcap_keyfield_typegroup(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset) +{ + const struct vcap_set *kset = vcap_keyfieldset(vctrl, vt, keyset); + + /* Check that the keyset is valid */ + if (!kset) + return NULL; + return vctrl->vcaps[vt].keyfield_set_typegroups[kset->sw_per_item]; +} + +/* Return the number of keyfields in the keyset */ +int vcap_keyfield_count(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset) +{ + /* Check that the keyset exists in the vcap keyset list */ + if (keyset >= vctrl->vcaps[vt].keyfield_set_size) + return 0; + return vctrl->vcaps[vt].keyfield_set_map_size[keyset]; +} + +static void vcap_encode_keyfield(struct vcap_rule_internal *ri, + const struct vcap_client_keyfield *kf, + const struct vcap_field *rf, + const struct vcap_typegroup *tgt) +{ + int sw_width = ri->vctrl->vcaps[ri->admin->vtype].sw_width; + struct vcap_cache_data *cache = &ri->admin->cache; + struct vcap_stream_iter iter; + const u8 *value, *mask; + + /* Encode the fields for the key and the mask in their respective + * streams, respecting the subword width. + */ + switch (kf->ctrl.type) { + case VCAP_FIELD_BIT: + value = &kf->data.u1.value; + mask = &kf->data.u1.mask; + break; + case VCAP_FIELD_U32: + value = (const u8 *)&kf->data.u32.value; + mask = (const u8 *)&kf->data.u32.mask; + break; + case VCAP_FIELD_U48: + value = kf->data.u48.value; + mask = kf->data.u48.mask; + break; + case VCAP_FIELD_U56: + value = kf->data.u56.value; + mask = kf->data.u56.mask; + break; + case VCAP_FIELD_U64: + value = kf->data.u64.value; + mask = kf->data.u64.mask; + break; + case VCAP_FIELD_U72: + value = kf->data.u72.value; + mask = kf->data.u72.mask; + break; + case VCAP_FIELD_U112: + value = kf->data.u112.value; + mask = kf->data.u112.mask; + break; + case VCAP_FIELD_U128: + value = kf->data.u128.value; + mask = kf->data.u128.mask; + break; + } + vcap_iter_init(&iter, sw_width, tgt, rf->offset); + vcap_encode_field(cache->keystream, &iter, rf->width, value); + vcap_iter_init(&iter, sw_width, tgt, rf->offset); + vcap_encode_field(cache->maskstream, &iter, rf->width, mask); +} + +static void vcap_encode_keyfield_typegroups(struct vcap_control *vctrl, + struct vcap_rule_internal *ri, + const struct vcap_typegroup *tgt) +{ + int sw_width = vctrl->vcaps[ri->admin->vtype].sw_width; + struct vcap_cache_data *cache = &ri->admin->cache; + + /* Encode the typegroup bits for the key and the mask in their streams, + * respecting the subword width. + */ + vcap_encode_typegroups(cache->keystream, sw_width, tgt, false); + vcap_encode_typegroups(cache->maskstream, sw_width, tgt, true); +} + +static int vcap_encode_rule_keyset(struct vcap_rule_internal *ri) +{ + const struct vcap_client_keyfield *ckf; + const struct vcap_typegroup *tg_table; + const struct vcap_field *kf_table; + int keyset_size; + + /* Get a valid set of fields for the specific keyset */ + kf_table = vcap_keyfields(ri->vctrl, ri->admin->vtype, ri->data.keyset); + if (!kf_table) { + pr_err("%s:%d: no fields available for this keyset: %d\n", + __func__, __LINE__, ri->data.keyset); + return -EINVAL; + } + /* Get a valid typegroup for the specific keyset */ + tg_table = vcap_keyfield_typegroup(ri->vctrl, ri->admin->vtype, + ri->data.keyset); + if (!tg_table) { + pr_err("%s:%d: no typegroups available for this keyset: %d\n", + __func__, __LINE__, ri->data.keyset); + return -EINVAL; + } + /* Get a valid size for the specific keyset */ + keyset_size = vcap_keyfield_count(ri->vctrl, ri->admin->vtype, + ri->data.keyset); + if (keyset_size == 0) { + pr_err("%s:%d: zero field count for this keyset: %d\n", + __func__, __LINE__, ri->data.keyset); + return -EINVAL; + } + /* Iterate over the keyfields (key, mask) in the rule + * and encode these bits + */ + if (list_empty(&ri->data.keyfields)) { + pr_err("%s:%d: no keyfields in the rule\n", __func__, __LINE__); + return -EINVAL; + } + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { + /* Check that the client entry exists in the keyset */ + if (ckf->ctrl.key >= keyset_size) { + pr_err("%s:%d: key %d is not in vcap\n", + __func__, __LINE__, ckf->ctrl.key); + return -EINVAL; + } + vcap_encode_keyfield(ri, ckf, &kf_table[ckf->ctrl.key], tg_table); + } + /* Add typegroup bits to the key/mask bitstreams */ + vcap_encode_keyfield_typegroups(ri->vctrl, ri, tg_table); + return 0; +} + +/* Return the list of actionfields for the actionset */ +const struct vcap_field * +vcap_actionfields(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset) +{ + /* Check that the actionset exists in the vcap actionset list */ + if (actionset >= vctrl->vcaps[vt].actionfield_set_size) + return NULL; + return vctrl->vcaps[vt].actionfield_set_map[actionset]; +} + +const struct vcap_set * +vcap_actionfieldset(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset) +{ + const struct vcap_set *aset; + + /* Check that the actionset exists in the vcap actionset list */ + if (actionset >= vctrl->vcaps[vt].actionfield_set_size) + return NULL; + aset = &vctrl->vcaps[vt].actionfield_set[actionset]; + if (aset->sw_per_item == 0 || aset->sw_per_item > vctrl->vcaps[vt].sw_count) + return NULL; + return aset; +} + +/* Return the typegroup table for the matching actionset (using subword size) */ +const struct vcap_typegroup * +vcap_actionfield_typegroup(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset) +{ + const struct vcap_set *aset = vcap_actionfieldset(vctrl, vt, actionset); + + /* Check that the actionset is valid */ + if (!aset) + return NULL; + return vctrl->vcaps[vt].actionfield_set_typegroups[aset->sw_per_item]; +} + +/* Return the number of actionfields in the actionset */ +int vcap_actionfield_count(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_actionfield_set actionset) +{ + /* Check that the actionset exists in the vcap actionset list */ + if (actionset >= vctrl->vcaps[vt].actionfield_set_size) + return 0; + return vctrl->vcaps[vt].actionfield_set_map_size[actionset]; +} + +static void vcap_encode_actionfield(struct vcap_rule_internal *ri, + const struct vcap_client_actionfield *af, + const struct vcap_field *rf, + const struct vcap_typegroup *tgt) +{ + int act_width = ri->vctrl->vcaps[ri->admin->vtype].act_width; + + struct vcap_cache_data *cache = &ri->admin->cache; + struct vcap_stream_iter iter; + const u8 *value; + + /* Encode the action field in the stream, respecting the subword width */ + switch (af->ctrl.type) { + case VCAP_FIELD_BIT: + value = &af->data.u1.value; + break; + case VCAP_FIELD_U32: + value = (const u8 *)&af->data.u32.value; + break; + case VCAP_FIELD_U48: + value = af->data.u48.value; + break; + case VCAP_FIELD_U56: + value = af->data.u56.value; + break; + case VCAP_FIELD_U64: + value = af->data.u64.value; + break; + case VCAP_FIELD_U72: + value = af->data.u72.value; + break; + case VCAP_FIELD_U112: + value = af->data.u112.value; + break; + case VCAP_FIELD_U128: + value = af->data.u128.value; + break; + } + vcap_iter_init(&iter, act_width, tgt, rf->offset); + vcap_encode_field(cache->actionstream, &iter, rf->width, value); +} + +static void vcap_encode_actionfield_typegroups(struct vcap_rule_internal *ri, + const struct vcap_typegroup *tgt) +{ + int sw_width = ri->vctrl->vcaps[ri->admin->vtype].act_width; + struct vcap_cache_data *cache = &ri->admin->cache; + + /* Encode the typegroup bits for the actionstream respecting the subword + * width. + */ + vcap_encode_typegroups(cache->actionstream, sw_width, tgt, false); +} + +static int vcap_encode_rule_actionset(struct vcap_rule_internal *ri) +{ + const struct vcap_client_actionfield *caf; + const struct vcap_typegroup *tg_table; + const struct vcap_field *af_table; + int actionset_size; + + /* Get a valid set of actionset fields for the specific actionset */ + af_table = vcap_actionfields(ri->vctrl, ri->admin->vtype, + ri->data.actionset); + if (!af_table) { + pr_err("%s:%d: no fields available for this actionset: %d\n", + __func__, __LINE__, ri->data.actionset); + return -EINVAL; + } + /* Get a valid typegroup for the specific actionset */ + tg_table = vcap_actionfield_typegroup(ri->vctrl, ri->admin->vtype, + ri->data.actionset); + if (!tg_table) { + pr_err("%s:%d: no typegroups available for this actionset: %d\n", + __func__, __LINE__, ri->data.actionset); + return -EINVAL; + } + /* Get a valid actionset size for the specific actionset */ + actionset_size = vcap_actionfield_count(ri->vctrl, ri->admin->vtype, + ri->data.actionset); + if (actionset_size == 0) { + pr_err("%s:%d: zero field count for this actionset: %d\n", + __func__, __LINE__, ri->data.actionset); + return -EINVAL; + } + /* Iterate over the actionfields in the rule + * and encode these bits + */ + if (list_empty(&ri->data.actionfields)) + pr_warn("%s:%d: no actionfields in the rule\n", + __func__, __LINE__); + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { + /* Check that the client action exists in the actionset */ + if (caf->ctrl.action >= actionset_size) { + pr_err("%s:%d: action %d is not in vcap\n", + __func__, __LINE__, caf->ctrl.action); + return -EINVAL; + } + vcap_encode_actionfield(ri, caf, &af_table[caf->ctrl.action], + tg_table); + } + /* Add typegroup bits to the entry bitstreams */ + vcap_encode_actionfield_typegroups(ri, tg_table); + return 0; +} + +static int vcap_encode_rule(struct vcap_rule_internal *ri) +{ + int err; + + err = vcap_encode_rule_keyset(ri); + if (err) + return err; + err = vcap_encode_rule_actionset(ri); + if (err) + return err; + return 0; +} + +int vcap_api_check(struct vcap_control *ctrl) +{ + if (!ctrl) { + pr_err("%s:%d: vcap control is missing\n", __func__, __LINE__); + return -EINVAL; + } + if (!ctrl->ops || !ctrl->ops->validate_keyset || + !ctrl->ops->add_default_fields || !ctrl->ops->cache_erase || + !ctrl->ops->cache_write || !ctrl->ops->cache_read || + !ctrl->ops->init || !ctrl->ops->update || !ctrl->ops->move || + !ctrl->ops->port_info || !ctrl->ops->enable) { + pr_err("%s:%d: client operations are missing\n", + __func__, __LINE__); + return -ENOENT; + } + return 0; +} + +void vcap_erase_cache(struct vcap_rule_internal *ri) +{ + ri->vctrl->ops->cache_erase(ri->admin); +} + +/* Update the keyset for the rule */ +int vcap_set_rule_set_keyset(struct vcap_rule *rule, + enum vcap_keyfield_set keyset) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_set *kset; + int sw_width; + + kset = vcap_keyfieldset(ri->vctrl, ri->admin->vtype, keyset); + /* Check that the keyset is valid */ + if (!kset) + return -EINVAL; + ri->keyset_sw = kset->sw_per_item; + sw_width = ri->vctrl->vcaps[ri->admin->vtype].sw_width; + ri->keyset_sw_regs = DIV_ROUND_UP(sw_width, 32); + ri->data.keyset = keyset; + return 0; +} +EXPORT_SYMBOL_GPL(vcap_set_rule_set_keyset); + +/* Update the actionset for the rule */ +int vcap_set_rule_set_actionset(struct vcap_rule *rule, + enum vcap_actionfield_set actionset) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_set *aset; + int act_width; + + aset = vcap_actionfieldset(ri->vctrl, ri->admin->vtype, actionset); + /* Check that the actionset is valid */ + if (!aset) + return -EINVAL; + ri->actionset_sw = aset->sw_per_item; + act_width = ri->vctrl->vcaps[ri->admin->vtype].act_width; + ri->actionset_sw_regs = DIV_ROUND_UP(act_width, 32); + ri->data.actionset = actionset; + return 0; +} +EXPORT_SYMBOL_GPL(vcap_set_rule_set_actionset); + +/* Find a rule with a provided rule id */ +static struct vcap_rule_internal *vcap_lookup_rule(struct vcap_control *vctrl, + u32 id) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + + /* Look for the rule id in all vcaps */ + list_for_each_entry(admin, &vctrl->list, list) + list_for_each_entry(ri, &admin->rules, list) + if (ri->data.id == id) + return ri; + return NULL; +} + +/* Find a rule id with a provided cookie */ +int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + + /* Look for the rule id in all vcaps */ + list_for_each_entry(admin, &vctrl->list, list) + list_for_each_entry(ri, &admin->rules, list) + if (ri->data.cookie == cookie) + return ri->data.id; + return -ENOENT; +} +EXPORT_SYMBOL_GPL(vcap_lookup_rule_by_cookie); + +/* Make a shallow copy of the rule without the fields */ +struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri) +{ + struct vcap_rule_internal *duprule; + + /* Allocate the client part */ + duprule = kzalloc(sizeof(*duprule), GFP_KERNEL); + if (!duprule) + return ERR_PTR(-ENOMEM); + *duprule = *ri; + /* Not inserted in the VCAP */ + INIT_LIST_HEAD(&duprule->list); + /* No elements in these lists */ + INIT_LIST_HEAD(&duprule->data.keyfields); + INIT_LIST_HEAD(&duprule->data.actionfields); + return duprule; +} + +static void vcap_apply_width(u8 *dst, int width, int bytes) +{ + u8 bmask; + int idx; + + for (idx = 0; idx < bytes; idx++) { + if (width > 0) + if (width < 8) + bmask = (1 << width) - 1; + else + bmask = ~0; + else + bmask = 0; + dst[idx] &= bmask; + width -= 8; + } +} + +static void vcap_copy_from_w32be(u8 *dst, u8 *src, int size, int width) +{ + int idx, ridx, wstart, nidx; + int tail_bytes = (((size + 4) >> 2) << 2) - size; + + for (idx = 0, ridx = size - 1; idx < size; ++idx, --ridx) { + wstart = (idx >> 2) << 2; + nidx = wstart + 3 - (idx & 0x3); + if (nidx >= size) + nidx -= tail_bytes; + dst[nidx] = src[ridx]; + } + + vcap_apply_width(dst, width, size); +} + +static void vcap_copy_action_bit_field(struct vcap_u1_action *field, u8 *value) +{ + field->value = (*value) & 0x1; +} + +static void vcap_copy_limited_actionfield(u8 *dstvalue, u8 *srcvalue, + int width, int bytes) +{ + memcpy(dstvalue, srcvalue, bytes); + vcap_apply_width(dstvalue, width, bytes); +} + +static void vcap_copy_to_client_actionfield(struct vcap_rule_internal *ri, + struct vcap_client_actionfield *field, + u8 *value, u16 width) +{ + int field_size = actionfield_size_table[field->ctrl.type]; + + if (ri->admin->w32be) { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_action_bit_field(&field->data.u1, value); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_actionfield((u8 *)&field->data.u32.value, + value, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_from_w32be(field->data.u48.value, value, + field_size, width); + break; + case VCAP_FIELD_U56: + vcap_copy_from_w32be(field->data.u56.value, value, + field_size, width); + break; + case VCAP_FIELD_U64: + vcap_copy_from_w32be(field->data.u64.value, value, + field_size, width); + break; + case VCAP_FIELD_U72: + vcap_copy_from_w32be(field->data.u72.value, value, + field_size, width); + break; + case VCAP_FIELD_U112: + vcap_copy_from_w32be(field->data.u112.value, value, + field_size, width); + break; + case VCAP_FIELD_U128: + vcap_copy_from_w32be(field->data.u128.value, value, + field_size, width); + break; + }; + } else { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_action_bit_field(&field->data.u1, value); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_actionfield((u8 *)&field->data.u32.value, + value, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_limited_actionfield(field->data.u48.value, + value, + width, field_size); + break; + case VCAP_FIELD_U56: + vcap_copy_limited_actionfield(field->data.u56.value, + value, + width, field_size); + break; + case VCAP_FIELD_U64: + vcap_copy_limited_actionfield(field->data.u64.value, + value, + width, field_size); + break; + case VCAP_FIELD_U72: + vcap_copy_limited_actionfield(field->data.u72.value, + value, + width, field_size); + break; + case VCAP_FIELD_U112: + vcap_copy_limited_actionfield(field->data.u112.value, + value, + width, field_size); + break; + case VCAP_FIELD_U128: + vcap_copy_limited_actionfield(field->data.u128.value, + value, + width, field_size); + break; + }; + } +} + +static void vcap_copy_key_bit_field(struct vcap_u1_key *field, + u8 *value, u8 *mask) +{ + field->value = (*value) & 0x1; + field->mask = (*mask) & 0x1; +} + +static void vcap_copy_limited_keyfield(u8 *dstvalue, u8 *dstmask, + u8 *srcvalue, u8 *srcmask, + int width, int bytes) +{ + memcpy(dstvalue, srcvalue, bytes); + vcap_apply_width(dstvalue, width, bytes); + memcpy(dstmask, srcmask, bytes); + vcap_apply_width(dstmask, width, bytes); +} + +static void vcap_copy_to_client_keyfield(struct vcap_rule_internal *ri, + struct vcap_client_keyfield *field, + u8 *value, u8 *mask, u16 width) +{ + int field_size = keyfield_size_table[field->ctrl.type] / 2; + + if (ri->admin->w32be) { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_key_bit_field(&field->data.u1, value, mask); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_keyfield((u8 *)&field->data.u32.value, + (u8 *)&field->data.u32.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_from_w32be(field->data.u48.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u48.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U56: + vcap_copy_from_w32be(field->data.u56.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u56.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U64: + vcap_copy_from_w32be(field->data.u64.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u64.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U72: + vcap_copy_from_w32be(field->data.u72.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u72.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U112: + vcap_copy_from_w32be(field->data.u112.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u112.mask, mask, + field_size, width); + break; + case VCAP_FIELD_U128: + vcap_copy_from_w32be(field->data.u128.value, value, + field_size, width); + vcap_copy_from_w32be(field->data.u128.mask, mask, + field_size, width); + break; + }; + } else { + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + vcap_copy_key_bit_field(&field->data.u1, value, mask); + break; + case VCAP_FIELD_U32: + vcap_copy_limited_keyfield((u8 *)&field->data.u32.value, + (u8 *)&field->data.u32.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U48: + vcap_copy_limited_keyfield(field->data.u48.value, + field->data.u48.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U56: + vcap_copy_limited_keyfield(field->data.u56.value, + field->data.u56.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U64: + vcap_copy_limited_keyfield(field->data.u64.value, + field->data.u64.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U72: + vcap_copy_limited_keyfield(field->data.u72.value, + field->data.u72.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U112: + vcap_copy_limited_keyfield(field->data.u112.value, + field->data.u112.mask, + value, mask, + width, field_size); + break; + case VCAP_FIELD_U128: + vcap_copy_limited_keyfield(field->data.u128.value, + field->data.u128.mask, + value, mask, + width, field_size); + break; + }; + } +} + +static void vcap_rule_alloc_keyfield(struct vcap_rule_internal *ri, + const struct vcap_field *keyfield, + enum vcap_key_field key, + u8 *value, u8 *mask) +{ + struct vcap_client_keyfield *field; + + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return; + INIT_LIST_HEAD(&field->ctrl.list); + field->ctrl.key = key; + field->ctrl.type = keyfield->type; + vcap_copy_to_client_keyfield(ri, field, value, mask, keyfield->width); + list_add_tail(&field->ctrl.list, &ri->data.keyfields); +} + +/* Read key data from a VCAP address and discover if there is a rule keyset + * here + */ +static bool +vcap_verify_actionstream_actionset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *actionstream, + enum vcap_actionfield_set actionset) +{ + const struct vcap_typegroup *tgt; + const struct vcap_field *fields; + const struct vcap_set *info; + + if (vcap_actionfield_count(vctrl, vt, actionset) == 0) + return false; + + info = vcap_actionfieldset(vctrl, vt, actionset); + /* Check that the actionset is valid */ + if (!info) + return false; + + /* a type_id of value -1 means that there is no type field */ + if (info->type_id == (u8)-1) + return true; + + /* Get a valid typegroup for the specific actionset */ + tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); + if (!tgt) + return false; + + fields = vcap_actionfields(vctrl, vt, actionset); + if (!fields) + return false; + + /* Later this will be expanded with a check of the type id */ + return true; +} + +/* Find the subword width of the action typegroup that matches the stream data + */ +static int vcap_find_actionstream_typegroup_sw(struct vcap_control *vctrl, + enum vcap_type vt, u32 *stream, + int sw_max) +{ + const struct vcap_typegroup **tgt; + int sw_idx, res; + + tgt = vctrl->vcaps[vt].actionfield_set_typegroups; + /* Try the longest subword match first */ + for (sw_idx = vctrl->vcaps[vt].sw_count; sw_idx >= 0; sw_idx--) { + if (!tgt[sw_idx]) + continue; + res = vcap_verify_typegroups(stream, vctrl->vcaps[vt].act_width, + tgt[sw_idx], false, sw_max); + if (res == 0) + return sw_idx; + } + return -EINVAL; +} + +/* Verify that the typegroup information, subword count, actionset and type id + * are in sync and correct, return the actionset + */ +static enum vcap_actionfield_set +vcap_find_actionstream_actionset(struct vcap_control *vctrl, + enum vcap_type vt, + u32 *stream, + int sw_max) +{ + const struct vcap_set *actionfield_set; + int sw_count, idx; + bool res; + + sw_count = vcap_find_actionstream_typegroup_sw(vctrl, vt, stream, + sw_max); + if (sw_count < 0) + return sw_count; + + actionfield_set = vctrl->vcaps[vt].actionfield_set; + for (idx = 0; idx < vctrl->vcaps[vt].actionfield_set_size; ++idx) { + if (actionfield_set[idx].sw_per_item != sw_count) + continue; + + res = vcap_verify_actionstream_actionset(vctrl, vt, + stream, idx); + if (res) + return idx; + } + return -EINVAL; +} + +/* Store action value in an element in a list for the client */ +static void vcap_rule_alloc_actionfield(struct vcap_rule_internal *ri, + const struct vcap_field *actionfield, + enum vcap_action_field action, + u8 *value) +{ + struct vcap_client_actionfield *field; + + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return; + INIT_LIST_HEAD(&field->ctrl.list); + field->ctrl.action = action; + field->ctrl.type = actionfield->type; + vcap_copy_to_client_actionfield(ri, field, value, actionfield->width); + list_add_tail(&field->ctrl.list, &ri->data.actionfields); +} + +static int vcap_decode_actionset(struct vcap_rule_internal *ri) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_admin *admin = ri->admin; + const struct vcap_field *actionfield; + enum vcap_actionfield_set actionset; + enum vcap_type vt = admin->vtype; + const struct vcap_typegroup *tgt; + struct vcap_stream_iter iter; + int idx, res, actfield_count; + u32 *actstream; + u8 value[16]; + + actstream = admin->cache.actionstream; + res = vcap_find_actionstream_actionset(vctrl, vt, actstream, 0); + if (res < 0) { + pr_err("%s:%d: could not find valid actionset: %d\n", + __func__, __LINE__, res); + return -EINVAL; + } + actionset = res; + actfield_count = vcap_actionfield_count(vctrl, vt, actionset); + actionfield = vcap_actionfields(vctrl, vt, actionset); + tgt = vcap_actionfield_typegroup(vctrl, vt, actionset); + /* Start decoding the stream */ + for (idx = 0; idx < actfield_count; ++idx) { + if (actionfield[idx].width <= 0) + continue; + /* Get the action */ + memset(value, 0, DIV_ROUND_UP(actionfield[idx].width, 8)); + vcap_iter_init(&iter, vctrl->vcaps[vt].act_width, tgt, + actionfield[idx].offset); + vcap_decode_field(actstream, &iter, actionfield[idx].width, + value); + /* Skip if no bits are set */ + if (vcap_bitarray_zero(actionfield[idx].width, value)) + continue; + vcap_rule_alloc_actionfield(ri, &actionfield[idx], idx, value); + /* Later the action id will also be checked */ + } + return vcap_set_rule_set_actionset((struct vcap_rule *)ri, actionset); +} + +static int vcap_decode_keyset(struct vcap_rule_internal *ri) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_stream_iter kiter, miter; + struct vcap_admin *admin = ri->admin; + enum vcap_keyfield_set keysets[10]; + const struct vcap_field *keyfield; + enum vcap_type vt = admin->vtype; + const struct vcap_typegroup *tgt; + struct vcap_keyset_list matches; + enum vcap_keyfield_set keyset; + int idx, res, keyfield_count; + u32 *maskstream; + u32 *keystream; + u8 value[16]; + u8 mask[16]; + + keystream = admin->cache.keystream; + maskstream = admin->cache.maskstream; + matches.keysets = keysets; + matches.cnt = 0; + matches.max = ARRAY_SIZE(keysets); + res = vcap_find_keystream_keysets(vctrl, vt, keystream, maskstream, + false, 0, &matches); + if (res < 0) { + pr_err("%s:%d: could not find valid keysets: %d\n", + __func__, __LINE__, res); + return -EINVAL; + } + keyset = matches.keysets[0]; + keyfield_count = vcap_keyfield_count(vctrl, vt, keyset); + keyfield = vcap_keyfields(vctrl, vt, keyset); + tgt = vcap_keyfield_typegroup(vctrl, vt, keyset); + /* Start decoding the streams */ + for (idx = 0; idx < keyfield_count; ++idx) { + if (keyfield[idx].width <= 0) + continue; + /* First get the mask */ + memset(mask, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); + vcap_iter_init(&miter, vctrl->vcaps[vt].sw_width, tgt, + keyfield[idx].offset); + vcap_decode_field(maskstream, &miter, keyfield[idx].width, + mask); + /* Skip if no mask bits are set */ + if (vcap_bitarray_zero(keyfield[idx].width, mask)) + continue; + /* Get the key */ + memset(value, 0, DIV_ROUND_UP(keyfield[idx].width, 8)); + vcap_iter_init(&kiter, vctrl->vcaps[vt].sw_width, tgt, + keyfield[idx].offset); + vcap_decode_field(keystream, &kiter, keyfield[idx].width, + value); + vcap_rule_alloc_keyfield(ri, &keyfield[idx], idx, value, mask); + } + return vcap_set_rule_set_keyset((struct vcap_rule *)ri, keyset); +} + +/* Read VCAP content into the VCAP cache */ +static int vcap_read_rule(struct vcap_rule_internal *ri) +{ + struct vcap_admin *admin = ri->admin; + int sw_idx, ent_idx = 0, act_idx = 0; + u32 addr = ri->addr; + + if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) { + pr_err("%s:%d: rule is empty\n", __func__, __LINE__); + return -EINVAL; + } + vcap_erase_cache(ri); + /* Use the values in the streams to read the VCAP cache */ + for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) { + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_READ, + VCAP_SEL_ALL, addr); + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_ENTRY, ent_idx, + ri->keyset_sw_regs); + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_ACTION, act_idx, + ri->actionset_sw_regs); + if (sw_idx == 0) + ri->vctrl->ops->cache_read(ri->ndev, admin, + VCAP_SEL_COUNTER, + ri->counter_id, 0); + ent_idx += ri->keyset_sw_regs; + act_idx += ri->actionset_sw_regs; + } + return 0; +} + +/* Write VCAP cache content to the VCAP HW instance */ +static int vcap_write_rule(struct vcap_rule_internal *ri) +{ + struct vcap_admin *admin = ri->admin; + int sw_idx, ent_idx = 0, act_idx = 0; + u32 addr = ri->addr; + + if (!ri->size || !ri->keyset_sw_regs || !ri->actionset_sw_regs) { + pr_err("%s:%d: rule is empty\n", __func__, __LINE__); + return -EINVAL; + } + /* Use the values in the streams to write the VCAP cache */ + for (sw_idx = 0; sw_idx < ri->size; sw_idx++, addr++) { + ri->vctrl->ops->cache_write(ri->ndev, admin, + VCAP_SEL_ENTRY, ent_idx, + ri->keyset_sw_regs); + ri->vctrl->ops->cache_write(ri->ndev, admin, + VCAP_SEL_ACTION, act_idx, + ri->actionset_sw_regs); + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE, + VCAP_SEL_ALL, addr); + ent_idx += ri->keyset_sw_regs; + act_idx += ri->actionset_sw_regs; + } + return 0; +} + +static int vcap_write_counter(struct vcap_rule_internal *ri, + struct vcap_counter *ctr) +{ + struct vcap_admin *admin = ri->admin; + + admin->cache.counter = ctr->value; + admin->cache.sticky = ctr->sticky; + ri->vctrl->ops->cache_write(ri->ndev, admin, VCAP_SEL_COUNTER, + ri->counter_id, 0); + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_WRITE, + VCAP_SEL_COUNTER, ri->addr); + return 0; +} + +/* Convert a chain id to a VCAP lookup index */ +int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid) +{ + int lookup_first = admin->vinst * admin->lookups_per_instance; + int lookup_last = lookup_first + admin->lookups_per_instance; + int cid_next = admin->first_cid + VCAP_CID_LOOKUP_SIZE; + int cid = admin->first_cid; + int lookup; + + for (lookup = lookup_first; lookup < lookup_last; ++lookup, + cid += VCAP_CID_LOOKUP_SIZE, cid_next += VCAP_CID_LOOKUP_SIZE) + if (cur_cid >= cid && cur_cid < cid_next) + return lookup; + return 0; +} +EXPORT_SYMBOL_GPL(vcap_chain_id_to_lookup); + +/* Lookup a vcap instance using chain id */ +struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid) +{ + struct vcap_admin *admin; + + if (vcap_api_check(vctrl)) + return NULL; + + list_for_each_entry(admin, &vctrl->list, list) { + if (cid >= admin->first_cid && cid <= admin->last_cid) + return admin; + } + return NULL; +} +EXPORT_SYMBOL_GPL(vcap_find_admin); + +/* Is the next chain id in the following lookup, possible in another VCAP */ +bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid) +{ + struct vcap_admin *admin, *next_admin; + int lookup, next_lookup; + + /* The offset must be at least one lookup */ + if (next_cid < cur_cid + VCAP_CID_LOOKUP_SIZE) + return false; + + if (vcap_api_check(vctrl)) + return false; + + admin = vcap_find_admin(vctrl, cur_cid); + if (!admin) + return false; + + /* If no VCAP contains the next chain, the next chain must be beyond + * the last chain in the current VCAP + */ + next_admin = vcap_find_admin(vctrl, next_cid); + if (!next_admin) + return next_cid > admin->last_cid; + + lookup = vcap_chain_id_to_lookup(admin, cur_cid); + next_lookup = vcap_chain_id_to_lookup(next_admin, next_cid); + + /* Next lookup must be the following lookup */ + if (admin == next_admin || admin->vtype == next_admin->vtype) + return next_lookup == lookup + 1; + + /* Must be the first lookup in the next VCAP instance */ + return next_lookup == 0; +} +EXPORT_SYMBOL_GPL(vcap_is_next_lookup); + +/* Check if there is room for a new rule */ +static int vcap_rule_space(struct vcap_admin *admin, int size) +{ + if (admin->last_used_addr - size < admin->first_valid_addr) { + pr_err("%s:%d: No room for rule size: %u, %u\n", + __func__, __LINE__, size, admin->first_valid_addr); + return -ENOSPC; + } + return 0; +} + +/* Add the keyset typefield to the list of rule keyfields */ +static int vcap_add_type_keyfield(struct vcap_rule *rule) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_keyfield_set keyset = rule->keyset; + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + const struct vcap_set *kset; + int ret = -EINVAL; + + kset = vcap_keyfieldset(ri->vctrl, vt, keyset); + if (!kset) + return ret; + if (kset->type_id == (u8)-1) /* No type field is needed */ + return 0; + + fields = vcap_keyfields(ri->vctrl, vt, keyset); + if (!fields) + return -EINVAL; + if (fields[VCAP_KF_TYPE].width > 1) { + ret = vcap_rule_add_key_u32(rule, VCAP_KF_TYPE, + kset->type_id, 0xff); + } else { + if (kset->type_id) + ret = vcap_rule_add_key_bit(rule, VCAP_KF_TYPE, + VCAP_BIT_1); + else + ret = vcap_rule_add_key_bit(rule, VCAP_KF_TYPE, + VCAP_BIT_0); + } + return 0; +} + +/* Add a keyset to a keyset list */ +bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist, + enum vcap_keyfield_set keyset) +{ + int idx; + + if (keysetlist->cnt < keysetlist->max) { + /* Avoid duplicates */ + for (idx = 0; idx < keysetlist->cnt; ++idx) + if (keysetlist->keysets[idx] == keyset) + return keysetlist->cnt < keysetlist->max; + keysetlist->keysets[keysetlist->cnt++] = keyset; + } + return keysetlist->cnt < keysetlist->max; +} +EXPORT_SYMBOL_GPL(vcap_keyset_list_add); + +/* map keyset id to a string with the keyset name */ +const char *vcap_keyset_name(struct vcap_control *vctrl, + enum vcap_keyfield_set keyset) +{ + return vctrl->stats->keyfield_set_names[keyset]; +} +EXPORT_SYMBOL_GPL(vcap_keyset_name); + +/* map key field id to a string with the key name */ +const char *vcap_keyfield_name(struct vcap_control *vctrl, + enum vcap_key_field key) +{ + return vctrl->stats->keyfield_names[key]; +} +EXPORT_SYMBOL_GPL(vcap_keyfield_name); + +/* map actionset id to a string with the actionset name */ +const char *vcap_actionset_name(struct vcap_control *vctrl, + enum vcap_actionfield_set actionset) +{ + return vctrl->stats->actionfield_set_names[actionset]; +} + +/* map action field id to a string with the action name */ +const char *vcap_actionfield_name(struct vcap_control *vctrl, + enum vcap_action_field action) +{ + return vctrl->stats->actionfield_names[action]; +} + +/* Return the keyfield that matches a key in a keyset */ +static const struct vcap_field * +vcap_find_keyset_keyfield(struct vcap_control *vctrl, + enum vcap_type vtype, + enum vcap_keyfield_set keyset, + enum vcap_key_field key) +{ + const struct vcap_field *fields; + int idx, count; + + fields = vcap_keyfields(vctrl, vtype, keyset); + if (!fields) + return NULL; + + /* Iterate the keyfields of the keyset */ + count = vcap_keyfield_count(vctrl, vtype, keyset); + for (idx = 0; idx < count; ++idx) { + if (fields[idx].width == 0) + continue; + + if (key == idx) + return &fields[idx]; + } + + return NULL; +} + +/* Match a list of keys against the keysets available in a vcap type */ +static bool _vcap_rule_find_keysets(struct vcap_rule_internal *ri, + struct vcap_keyset_list *matches) +{ + const struct vcap_client_keyfield *ckf; + int keyset, found, keycount, map_size; + const struct vcap_field **map; + enum vcap_type vtype; + + vtype = ri->admin->vtype; + map = ri->vctrl->vcaps[vtype].keyfield_set_map; + map_size = ri->vctrl->vcaps[vtype].keyfield_set_size; + + /* Get a count of the keyfields we want to match */ + keycount = 0; + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + ++keycount; + + matches->cnt = 0; + /* Iterate the keysets of the VCAP */ + for (keyset = 0; keyset < map_size; ++keyset) { + if (!map[keyset]) + continue; + + /* Iterate the keys in the rule */ + found = 0; + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + if (vcap_find_keyset_keyfield(ri->vctrl, vtype, + keyset, ckf->ctrl.key)) + ++found; + + /* Save the keyset if all keyfields were found */ + if (found == keycount) + if (!vcap_keyset_list_add(matches, keyset)) + /* bail out when the quota is filled */ + break; + } + + return matches->cnt > 0; +} + +/* Match a list of keys against the keysets available in a vcap type */ +bool vcap_rule_find_keysets(struct vcap_rule *rule, + struct vcap_keyset_list *matches) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + + return _vcap_rule_find_keysets(ri, matches); +} +EXPORT_SYMBOL_GPL(vcap_rule_find_keysets); + +/* Validate a rule with respect to available port keys */ +int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_keyset_list matches = {}; + enum vcap_keyfield_set keysets[10]; + int ret; + + ret = vcap_api_check(ri->vctrl); + if (ret) + return ret; + if (!ri->admin) { + ri->data.exterr = VCAP_ERR_NO_ADMIN; + return -EINVAL; + } + if (!ri->ndev) { + ri->data.exterr = VCAP_ERR_NO_NETDEV; + return -EINVAL; + } + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + if (ri->data.keyset == VCAP_KFS_NO_VALUE) { + /* Iterate over rule keyfields and select keysets that fits */ + if (!_vcap_rule_find_keysets(ri, &matches)) { + ri->data.exterr = VCAP_ERR_NO_KEYSET_MATCH; + return -EINVAL; + } + } else { + /* prepare for keyset validation */ + keysets[0] = ri->data.keyset; + matches.cnt = 1; + } + + /* Pick a keyset that is supported in the port lookups */ + ret = ri->vctrl->ops->validate_keyset(ri->ndev, ri->admin, rule, + &matches, l3_proto); + if (ret < 0) { + pr_err("%s:%d: keyset validation failed: %d\n", + __func__, __LINE__, ret); + ri->data.exterr = VCAP_ERR_NO_PORT_KEYSET_MATCH; + return ret; + } + /* use the keyset that is supported in the port lookups */ + ret = vcap_set_rule_set_keyset(rule, ret); + if (ret < 0) { + pr_err("%s:%d: keyset was not updated: %d\n", + __func__, __LINE__, ret); + return ret; + } + if (ri->data.actionset == VCAP_AFS_NO_VALUE) { + /* Later also actionsets will be matched against actions in + * the rule, and the type will be set accordingly + */ + ri->data.exterr = VCAP_ERR_NO_ACTIONSET_MATCH; + return -EINVAL; + } + vcap_add_type_keyfield(rule); + /* Add default fields to this rule */ + ri->vctrl->ops->add_default_fields(ri->ndev, ri->admin, rule); + + /* Rule size is the maximum of the entry and action subword count */ + ri->size = max(ri->keyset_sw, ri->actionset_sw); + + /* Finally check if there is room for the rule in the VCAP */ + return vcap_rule_space(ri->admin, ri->size); +} +EXPORT_SYMBOL_GPL(vcap_val_rule); + +/* Entries are sorted with increasing values of sort_key. + * I.e. Lowest numerical sort_key is first in list. + * In order to locate largest keys first in list we negate the key size with + * (max_size - size). + */ +static u32 vcap_sort_key(u32 max_size, u32 size, u8 user, u16 prio) +{ + return ((max_size - size) << 24) | (user << 16) | prio; +} + +/* calculate the address of the next rule after this (lower address and prio) */ +static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri) +{ + return ((addr - ri->size) / ri->size) * ri->size; +} + +/* Assign a unique rule id and autogenerate one if id == 0 */ +static u32 vcap_set_rule_id(struct vcap_rule_internal *ri) +{ + if (ri->data.id != 0) + return ri->data.id; + + for (u32 next_id = 1; next_id < ~0; ++next_id) { + if (!vcap_lookup_rule(ri->vctrl, next_id)) { + ri->data.id = next_id; + break; + } + } + return ri->data.id; +} + +static int vcap_insert_rule(struct vcap_rule_internal *ri, + struct vcap_rule_move *move) +{ + int sw_count = ri->vctrl->vcaps[ri->admin->vtype].sw_count; + struct vcap_rule_internal *duprule, *iter, *elem = NULL; + struct vcap_admin *admin = ri->admin; + u32 addr; + + ri->sort_key = vcap_sort_key(sw_count, ri->size, ri->data.user, + ri->data.priority); + + /* Insert the new rule in the list of rule based on the sort key + * If the rule needs to be inserted between existing rules then move + * these rules to make room for the new rule and update their start + * address. + */ + list_for_each_entry(iter, &admin->rules, list) { + if (ri->sort_key < iter->sort_key) { + elem = iter; + break; + } + } + + if (!elem) { + ri->addr = vcap_next_rule_addr(admin->last_used_addr, ri); + admin->last_used_addr = ri->addr; + + /* Add a shallow copy of the rule to the VCAP list */ + duprule = vcap_dup_rule(ri); + if (IS_ERR(duprule)) + return PTR_ERR(duprule); + + list_add_tail(&duprule->list, &admin->rules); + return 0; + } + + /* Reuse the space of the current rule */ + addr = elem->addr + elem->size; + ri->addr = vcap_next_rule_addr(addr, ri); + addr = ri->addr; + + /* Add a shallow copy of the rule to the VCAP list */ + duprule = vcap_dup_rule(ri); + if (IS_ERR(duprule)) + return PTR_ERR(duprule); + + /* Add before the current entry */ + list_add_tail(&duprule->list, &elem->list); + + /* Update the current rule */ + elem->addr = vcap_next_rule_addr(addr, elem); + addr = elem->addr; + + /* Update the address in the remaining rules in the list */ + list_for_each_entry_continue(elem, &admin->rules, list) { + elem->addr = vcap_next_rule_addr(addr, elem); + addr = elem->addr; + } + + /* Update the move info */ + move->addr = admin->last_used_addr; + move->count = ri->addr - addr; + move->offset = admin->last_used_addr - addr; + admin->last_used_addr = addr; + return 0; +} + +static void vcap_move_rules(struct vcap_rule_internal *ri, + struct vcap_rule_move *move) +{ + ri->vctrl->ops->move(ri->ndev, ri->admin, move->addr, + move->offset, move->count); +} + +/* Encode and write a validated rule to the VCAP */ +int vcap_add_rule(struct vcap_rule *rule) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_rule_move move = {0}; + int ret; + + ret = vcap_api_check(ri->vctrl); + if (ret) + return ret; + /* Insert the new rule in the list of vcap rules */ + mutex_lock(&ri->admin->lock); + ret = vcap_insert_rule(ri, &move); + if (ret < 0) { + pr_err("%s:%d: could not insert rule in vcap list: %d\n", + __func__, __LINE__, ret); + goto out; + } + if (move.count > 0) + vcap_move_rules(ri, &move); + ret = vcap_encode_rule(ri); + if (ret) { + pr_err("%s:%d: rule encoding error: %d\n", __func__, __LINE__, ret); + goto out; + } + + ret = vcap_write_rule(ri); + if (ret) + pr_err("%s:%d: rule write error: %d\n", __func__, __LINE__, ret); +out: + mutex_unlock(&ri->admin->lock); + return ret; +} +EXPORT_SYMBOL_GPL(vcap_add_rule); + +/* Allocate a new rule with the provided arguments */ +struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, + struct net_device *ndev, int vcap_chain_id, + enum vcap_user user, u16 priority, + u32 id) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + int err, maxsize; + + err = vcap_api_check(vctrl); + if (err) + return ERR_PTR(err); + if (!ndev) + return ERR_PTR(-ENODEV); + /* Get the VCAP instance */ + admin = vcap_find_admin(vctrl, vcap_chain_id); + if (!admin) + return ERR_PTR(-ENOENT); + /* Sanity check that this VCAP is supported on this platform */ + if (vctrl->vcaps[admin->vtype].rows == 0) + return ERR_PTR(-EINVAL); + /* Check if a rule with this id already exists */ + if (vcap_lookup_rule(vctrl, id)) + return ERR_PTR(-EEXIST); + /* Check if there is room for the rule in the block(s) of the VCAP */ + maxsize = vctrl->vcaps[admin->vtype].sw_count; /* worst case rule size */ + if (vcap_rule_space(admin, maxsize)) + return ERR_PTR(-ENOSPC); + /* Create a container for the rule and return it */ + ri = kzalloc(sizeof(*ri), GFP_KERNEL); + if (!ri) + return ERR_PTR(-ENOMEM); + ri->data.vcap_chain_id = vcap_chain_id; + ri->data.user = user; + ri->data.priority = priority; + ri->data.id = id; + ri->data.keyset = VCAP_KFS_NO_VALUE; + ri->data.actionset = VCAP_AFS_NO_VALUE; + INIT_LIST_HEAD(&ri->list); + INIT_LIST_HEAD(&ri->data.keyfields); + INIT_LIST_HEAD(&ri->data.actionfields); + ri->ndev = ndev; + ri->admin = admin; /* refer to the vcap instance */ + ri->vctrl = vctrl; /* refer to the client */ + if (vcap_set_rule_id(ri) == 0) + goto out_free; + vcap_erase_cache(ri); + return (struct vcap_rule *)ri; + +out_free: + kfree(ri); + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(vcap_alloc_rule); + +/* Free mem of a rule owned by client after the rule as been added to the VCAP */ +void vcap_free_rule(struct vcap_rule *rule) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_actionfield *caf, *next_caf; + struct vcap_client_keyfield *ckf, *next_ckf; + + /* Deallocate the list of keys and actions */ + list_for_each_entry_safe(ckf, next_ckf, &ri->data.keyfields, ctrl.list) { + list_del(&ckf->ctrl.list); + kfree(ckf); + } + list_for_each_entry_safe(caf, next_caf, &ri->data.actionfields, ctrl.list) { + list_del(&caf->ctrl.list); + kfree(caf); + } + /* Deallocate the rule */ + kfree(rule); +} +EXPORT_SYMBOL_GPL(vcap_free_rule); + +struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id) +{ + struct vcap_rule_internal *elem; + struct vcap_rule_internal *ri; + int err; + + ri = NULL; + + err = vcap_api_check(vctrl); + if (err) + return ERR_PTR(err); + elem = vcap_lookup_rule(vctrl, id); + if (!elem) + return NULL; + mutex_lock(&elem->admin->lock); + ri = vcap_dup_rule(elem); + if (IS_ERR(ri)) + goto unlock; + err = vcap_read_rule(ri); + if (err) { + ri = ERR_PTR(err); + goto unlock; + } + err = vcap_decode_keyset(ri); + if (err) { + ri = ERR_PTR(err); + goto unlock; + } + err = vcap_decode_actionset(ri); + if (err) { + ri = ERR_PTR(err); + goto unlock; + } + +unlock: + mutex_unlock(&elem->admin->lock); + return (struct vcap_rule *)ri; +} +EXPORT_SYMBOL_GPL(vcap_get_rule); + +/* Update existing rule */ +int vcap_mod_rule(struct vcap_rule *rule) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_counter ctr; + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return err; + + if (!vcap_lookup_rule(ri->vctrl, ri->data.id)) + return -ENOENT; + + mutex_lock(&ri->admin->lock); + /* Encode the bitstreams to the VCAP cache */ + vcap_erase_cache(ri); + err = vcap_encode_rule(ri); + if (err) + goto out; + + err = vcap_write_rule(ri); + if (err) + goto out; + + memset(&ctr, 0, sizeof(ctr)); + err = vcap_write_counter(ri, &ctr); + if (err) + goto out; + +out: + mutex_unlock(&ri->admin->lock); + return err; +} +EXPORT_SYMBOL_GPL(vcap_mod_rule); + +/* Return the alignment offset for a new rule address */ +static int vcap_valid_rule_move(struct vcap_rule_internal *el, int offset) +{ + return (el->addr + offset) % el->size; +} + +/* Update the rule address with an offset */ +static void vcap_adjust_rule_addr(struct vcap_rule_internal *el, int offset) +{ + el->addr += offset; +} + +/* Rules needs to be moved to fill the gap of the deleted rule */ +static int vcap_fill_rule_gap(struct vcap_rule_internal *ri) +{ + struct vcap_admin *admin = ri->admin; + struct vcap_rule_internal *elem; + struct vcap_rule_move move; + int gap = 0, offset = 0; + + /* If the first rule is deleted: Move other rules to the top */ + if (list_is_first(&ri->list, &admin->rules)) + offset = admin->last_valid_addr + 1 - ri->addr - ri->size; + + /* Locate gaps between odd size rules and adjust the move */ + elem = ri; + list_for_each_entry_continue(elem, &admin->rules, list) + gap += vcap_valid_rule_move(elem, ri->size); + + /* Update the address in the remaining rules in the list */ + elem = ri; + list_for_each_entry_continue(elem, &admin->rules, list) + vcap_adjust_rule_addr(elem, ri->size + gap + offset); + + /* Update the move info */ + move.addr = admin->last_used_addr; + move.count = ri->addr - admin->last_used_addr - gap; + move.offset = -(ri->size + gap + offset); + + /* Do the actual move operation */ + vcap_move_rules(ri, &move); + + return gap + offset; +} + +/* Delete rule in a VCAP instance */ +int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id) +{ + struct vcap_rule_internal *ri, *elem; + struct vcap_admin *admin; + int gap = 0, err; + + /* This will later also handle rule moving */ + if (!ndev) + return -ENODEV; + err = vcap_api_check(vctrl); + if (err) + return err; + /* Look for the rule id in all vcaps */ + ri = vcap_lookup_rule(vctrl, id); + if (!ri) + return -EINVAL; + admin = ri->admin; + + if (ri->addr > admin->last_used_addr) + gap = vcap_fill_rule_gap(ri); + + /* Delete the rule from the list of rules and the cache */ + mutex_lock(&admin->lock); + list_del(&ri->list); + vctrl->ops->init(ndev, admin, admin->last_used_addr, ri->size + gap); + kfree(ri); + mutex_unlock(&admin->lock); + + /* Update the last used address, set to default when no rules */ + if (list_empty(&admin->rules)) { + admin->last_used_addr = admin->last_valid_addr + 1; + } else { + elem = list_last_entry(&admin->rules, struct vcap_rule_internal, + list); + admin->last_used_addr = elem->addr; + } + return 0; +} +EXPORT_SYMBOL_GPL(vcap_del_rule); + +/* Delete all rules in the VCAP instance */ +int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin) +{ + struct vcap_enabled_port *eport, *next_eport; + struct vcap_rule_internal *ri, *next_ri; + int ret = vcap_api_check(vctrl); + + if (ret) + return ret; + + mutex_lock(&admin->lock); + list_for_each_entry_safe(ri, next_ri, &admin->rules, list) { + vctrl->ops->init(ri->ndev, admin, ri->addr, ri->size); + list_del(&ri->list); + kfree(ri); + } + admin->last_used_addr = admin->last_valid_addr; + + /* Remove list of enabled ports */ + list_for_each_entry_safe(eport, next_eport, &admin->enabled, list) { + list_del(&eport->list); + kfree(eport); + } + mutex_unlock(&admin->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(vcap_del_rules); + +/* Find a client key field in a rule */ +static struct vcap_client_keyfield * +vcap_find_keyfield(struct vcap_rule *rule, enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_keyfield *ckf; + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + if (ckf->ctrl.key == key) + return ckf; + return NULL; +} + +/* Find information on a key field in a rule */ +const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, + enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_keyfield_set keyset = rule->keyset; + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + + if (keyset == VCAP_KFS_NO_VALUE) + return NULL; + fields = vcap_keyfields(ri->vctrl, vt, keyset); + if (!fields) + return NULL; + return &fields[key]; +} +EXPORT_SYMBOL_GPL(vcap_lookup_keyfield); + +/* Copy data from src to dst but reverse the data in chunks of 32bits. + * For example if src is 00:11:22:33:44:55 where 55 is LSB the dst will + * have the value 22:33:44:55:00:11. + */ +static void vcap_copy_to_w32be(u8 *dst, u8 *src, int size) +{ + for (int idx = 0; idx < size; ++idx) { + int first_byte_index = 0; + int nidx; + + first_byte_index = size - (((idx >> 2) + 1) << 2); + if (first_byte_index < 0) + first_byte_index = 0; + nidx = idx + first_byte_index - (idx & ~0x3); + dst[nidx] = src[idx]; + } +} + +static void vcap_copy_from_client_keyfield(struct vcap_rule *rule, + struct vcap_client_keyfield *field, + struct vcap_client_keyfield_data *data) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + int size; + + if (!ri->admin->w32be) { + memcpy(&field->data, data, sizeof(field->data)); + return; + } + + size = keyfield_size_table[field->ctrl.type] / 2; + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + case VCAP_FIELD_U32: + memcpy(&field->data, data, sizeof(field->data)); + break; + case VCAP_FIELD_U48: + vcap_copy_to_w32be(field->data.u48.value, data->u48.value, size); + vcap_copy_to_w32be(field->data.u48.mask, data->u48.mask, size); + break; + case VCAP_FIELD_U56: + vcap_copy_to_w32be(field->data.u56.value, data->u56.value, size); + vcap_copy_to_w32be(field->data.u56.mask, data->u56.mask, size); + break; + case VCAP_FIELD_U64: + vcap_copy_to_w32be(field->data.u64.value, data->u64.value, size); + vcap_copy_to_w32be(field->data.u64.mask, data->u64.mask, size); + break; + case VCAP_FIELD_U72: + vcap_copy_to_w32be(field->data.u72.value, data->u72.value, size); + vcap_copy_to_w32be(field->data.u72.mask, data->u72.mask, size); + break; + case VCAP_FIELD_U112: + vcap_copy_to_w32be(field->data.u112.value, data->u112.value, size); + vcap_copy_to_w32be(field->data.u112.mask, data->u112.mask, size); + break; + case VCAP_FIELD_U128: + vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size); + vcap_copy_to_w32be(field->data.u128.mask, data->u128.mask, size); + break; + } +} + +/* Check if the keyfield is already in the rule */ +static bool vcap_keyfield_unique(struct vcap_rule *rule, + enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_client_keyfield *ckf; + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) + if (ckf->ctrl.key == key) + return false; + return true; +} + +/* Check if the keyfield is in the keyset */ +static bool vcap_keyfield_match_keyset(struct vcap_rule *rule, + enum vcap_key_field key) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_keyfield_set keyset = rule->keyset; + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + + /* the field is accepted if the rule has no keyset yet */ + if (keyset == VCAP_KFS_NO_VALUE) + return true; + fields = vcap_keyfields(ri->vctrl, vt, keyset); + if (!fields) + return false; + /* if there is a width there is a way */ + return fields[key].width > 0; +} + +static int vcap_rule_add_key(struct vcap_rule *rule, + enum vcap_key_field key, + enum vcap_field_type ftype, + struct vcap_client_keyfield_data *data) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_keyfield *field; + + if (!vcap_keyfield_unique(rule, key)) { + pr_warn("%s:%d: keyfield %s is already in the rule\n", + __func__, __LINE__, + vcap_keyfield_name(ri->vctrl, key)); + return -EINVAL; + } + + if (!vcap_keyfield_match_keyset(rule, key)) { + pr_err("%s:%d: keyfield %s does not belong in the rule keyset\n", + __func__, __LINE__, + vcap_keyfield_name(ri->vctrl, key)); + return -EINVAL; + } + + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return -ENOMEM; + field->ctrl.key = key; + field->ctrl.type = ftype; + vcap_copy_from_client_keyfield(rule, field, data); + list_add_tail(&field->ctrl.list, &rule->keyfields); + return 0; +} + +static void vcap_rule_set_key_bitsize(struct vcap_u1_key *u1, enum vcap_bit val) +{ + switch (val) { + case VCAP_BIT_0: + u1->value = 0; + u1->mask = 1; + break; + case VCAP_BIT_1: + u1->value = 1; + u1->mask = 1; + break; + case VCAP_BIT_ANY: + u1->value = 0; + u1->mask = 0; + break; + } +} + +/* Add a bit key with value and mask to the rule */ +int vcap_rule_add_key_bit(struct vcap_rule *rule, enum vcap_key_field key, + enum vcap_bit val) +{ + struct vcap_client_keyfield_data data; + + vcap_rule_set_key_bitsize(&data.u1, val); + return vcap_rule_add_key(rule, key, VCAP_FIELD_BIT, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_bit); + +/* Add a 32 bit key field with value and mask to the rule */ +int vcap_rule_add_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask) +{ + struct vcap_client_keyfield_data data; + + data.u32.value = value; + data.u32.mask = mask; + return vcap_rule_add_key(rule, key, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u32); + +/* Add a 48 bit key with value and mask to the rule */ +int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u48_key *fieldval) +{ + struct vcap_client_keyfield_data data; + + memcpy(&data.u48, fieldval, sizeof(data.u48)); + return vcap_rule_add_key(rule, key, VCAP_FIELD_U48, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u48); + +/* Add a 72 bit key with value and mask to the rule */ +int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u72_key *fieldval) +{ + struct vcap_client_keyfield_data data; + + memcpy(&data.u72, fieldval, sizeof(data.u72)); + return vcap_rule_add_key(rule, key, VCAP_FIELD_U72, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u72); + +/* Add a 128 bit key with value and mask to the rule */ +int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u128_key *fieldval) +{ + struct vcap_client_keyfield_data data; + + memcpy(&data.u128, fieldval, sizeof(data.u128)); + return vcap_rule_add_key(rule, key, VCAP_FIELD_U128, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_key_u128); + +int vcap_rule_get_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 *value, u32 *mask) +{ + struct vcap_client_keyfield *ckf; + + ckf = vcap_find_keyfield(rule, key); + if (!ckf) + return -ENOENT; + + *value = ckf->data.u32.value; + *mask = ckf->data.u32.mask; + + return 0; +} +EXPORT_SYMBOL_GPL(vcap_rule_get_key_u32); + +/* Find a client action field in a rule */ +static struct vcap_client_actionfield * +vcap_find_actionfield(struct vcap_rule *rule, enum vcap_action_field act) +{ + struct vcap_rule_internal *ri = (struct vcap_rule_internal *)rule; + struct vcap_client_actionfield *caf; + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) + if (caf->ctrl.action == act) + return caf; + return NULL; +} + +static void vcap_copy_from_client_actionfield(struct vcap_rule *rule, + struct vcap_client_actionfield *field, + struct vcap_client_actionfield_data *data) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + int size; + + if (!ri->admin->w32be) { + memcpy(&field->data, data, sizeof(field->data)); + return; + } + + size = actionfield_size_table[field->ctrl.type]; + switch (field->ctrl.type) { + case VCAP_FIELD_BIT: + case VCAP_FIELD_U32: + memcpy(&field->data, data, sizeof(field->data)); + break; + case VCAP_FIELD_U48: + vcap_copy_to_w32be(field->data.u48.value, data->u48.value, size); + break; + case VCAP_FIELD_U56: + vcap_copy_to_w32be(field->data.u56.value, data->u56.value, size); + break; + case VCAP_FIELD_U64: + vcap_copy_to_w32be(field->data.u64.value, data->u64.value, size); + break; + case VCAP_FIELD_U72: + vcap_copy_to_w32be(field->data.u72.value, data->u72.value, size); + break; + case VCAP_FIELD_U112: + vcap_copy_to_w32be(field->data.u112.value, data->u112.value, size); + break; + case VCAP_FIELD_U128: + vcap_copy_to_w32be(field->data.u128.value, data->u128.value, size); + break; + } +} + +/* Check if the actionfield is already in the rule */ +static bool vcap_actionfield_unique(struct vcap_rule *rule, + enum vcap_action_field act) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + const struct vcap_client_actionfield *caf; + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) + if (caf->ctrl.action == act) + return false; + return true; +} + +/* Check if the actionfield is in the actionset */ +static bool vcap_actionfield_match_actionset(struct vcap_rule *rule, + enum vcap_action_field action) +{ + enum vcap_actionfield_set actionset = rule->actionset; + struct vcap_rule_internal *ri = to_intrule(rule); + enum vcap_type vt = ri->admin->vtype; + const struct vcap_field *fields; + + /* the field is accepted if the rule has no actionset yet */ + if (actionset == VCAP_AFS_NO_VALUE) + return true; + fields = vcap_actionfields(ri->vctrl, vt, actionset); + if (!fields) + return false; + /* if there is a width there is a way */ + return fields[action].width > 0; +} + +static int vcap_rule_add_action(struct vcap_rule *rule, + enum vcap_action_field action, + enum vcap_field_type ftype, + struct vcap_client_actionfield_data *data) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_actionfield *field; + + if (!vcap_actionfield_unique(rule, action)) { + pr_warn("%s:%d: actionfield %s is already in the rule\n", + __func__, __LINE__, + vcap_actionfield_name(ri->vctrl, action)); + return -EINVAL; + } + + if (!vcap_actionfield_match_actionset(rule, action)) { + pr_err("%s:%d: actionfield %s does not belong in the rule actionset\n", + __func__, __LINE__, + vcap_actionfield_name(ri->vctrl, action)); + return -EINVAL; + } + + field = kzalloc(sizeof(*field), GFP_KERNEL); + if (!field) + return -ENOMEM; + field->ctrl.action = action; + field->ctrl.type = ftype; + vcap_copy_from_client_actionfield(rule, field, data); + list_add_tail(&field->ctrl.list, &rule->actionfields); + return 0; +} + +static void vcap_rule_set_action_bitsize(struct vcap_u1_action *u1, + enum vcap_bit val) +{ + switch (val) { + case VCAP_BIT_0: + u1->value = 0; + break; + case VCAP_BIT_1: + u1->value = 1; + break; + case VCAP_BIT_ANY: + u1->value = 0; + break; + } +} + +/* Add a bit action with value to the rule */ +int vcap_rule_add_action_bit(struct vcap_rule *rule, + enum vcap_action_field action, + enum vcap_bit val) +{ + struct vcap_client_actionfield_data data; + + vcap_rule_set_action_bitsize(&data.u1, val); + return vcap_rule_add_action(rule, action, VCAP_FIELD_BIT, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_action_bit); + +/* Add a 32 bit action field with value to the rule */ +int vcap_rule_add_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, + u32 value) +{ + struct vcap_client_actionfield_data data; + + data.u32.value = value; + return vcap_rule_add_action(rule, action, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_add_action_u32); + +static int vcap_read_counter(struct vcap_rule_internal *ri, + struct vcap_counter *ctr) +{ + struct vcap_admin *admin = ri->admin; + + ri->vctrl->ops->update(ri->ndev, admin, VCAP_CMD_READ, VCAP_SEL_COUNTER, + ri->addr); + ri->vctrl->ops->cache_read(ri->ndev, admin, VCAP_SEL_COUNTER, + ri->counter_id, 0); + ctr->value = admin->cache.counter; + ctr->sticky = admin->cache.sticky; + return 0; +} + +/* Copy to host byte order */ +void vcap_netbytes_copy(u8 *dst, u8 *src, int count) +{ + int idx; + + for (idx = 0; idx < count; ++idx, ++dst) + *dst = src[count - idx - 1]; +} +EXPORT_SYMBOL_GPL(vcap_netbytes_copy); + +/* Convert validation error code into tc extact error message */ +void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule) +{ + switch (vrule->exterr) { + case VCAP_ERR_NONE: + break; + case VCAP_ERR_NO_ADMIN: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Missing VCAP instance"); + break; + case VCAP_ERR_NO_NETDEV: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "Missing network interface"); + break; + case VCAP_ERR_NO_KEYSET_MATCH: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No keyset matched the filter keys"); + break; + case VCAP_ERR_NO_ACTIONSET_MATCH: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No actionset matched the filter actions"); + break; + case VCAP_ERR_NO_PORT_KEYSET_MATCH: + NL_SET_ERR_MSG_MOD(fco->common.extack, + "No port keyset matched the filter keys"); + break; + } +} +EXPORT_SYMBOL_GPL(vcap_set_tc_exterr); + +/* Check if this port is already enabled for this VCAP instance */ +static bool vcap_is_enabled(struct vcap_admin *admin, struct net_device *ndev, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + + list_for_each_entry(eport, &admin->enabled, list) + if (eport->cookie == cookie || eport->ndev == ndev) + return true; + + return false; +} + +/* Enable this port for this VCAP instance */ +static int vcap_enable(struct vcap_admin *admin, struct net_device *ndev, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + + eport = kzalloc(sizeof(*eport), GFP_KERNEL); + if (!eport) + return -ENOMEM; + + eport->ndev = ndev; + eport->cookie = cookie; + list_add_tail(&eport->list, &admin->enabled); + + return 0; +} + +/* Disable this port for this VCAP instance */ +static int vcap_disable(struct vcap_admin *admin, struct net_device *ndev, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + + list_for_each_entry(eport, &admin->enabled, list) { + if (eport->cookie == cookie && eport->ndev == ndev) { + list_del(&eport->list); + kfree(eport); + return 0; + } + } + + return -ENOENT; +} + +/* Find the VCAP instance that enabled the port using a specific filter */ +static struct vcap_admin *vcap_find_admin_by_cookie(struct vcap_control *vctrl, + unsigned long cookie) +{ + struct vcap_enabled_port *eport; + struct vcap_admin *admin; + + list_for_each_entry(admin, &vctrl->list, list) + list_for_each_entry(eport, &admin->enabled, list) + if (eport->cookie == cookie) + return admin; + + return NULL; +} + +/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */ +int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, + int chain_id, unsigned long cookie, bool enable) +{ + struct vcap_admin *admin; + int err; + + err = vcap_api_check(vctrl); + if (err) + return err; + + if (!ndev) + return -ENODEV; + + if (chain_id) + admin = vcap_find_admin(vctrl, chain_id); + else + admin = vcap_find_admin_by_cookie(vctrl, cookie); + if (!admin) + return -ENOENT; + + /* first instance and first chain */ + if (admin->vinst || chain_id > admin->first_cid) + return -EFAULT; + + err = vctrl->ops->enable(ndev, admin, enable); + if (err) + return err; + + if (chain_id) { + if (vcap_is_enabled(admin, ndev, cookie)) + return -EADDRINUSE; + mutex_lock(&admin->lock); + vcap_enable(admin, ndev, cookie); + } else { + mutex_lock(&admin->lock); + vcap_disable(admin, ndev, cookie); + } + mutex_unlock(&admin->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(vcap_enable_lookups); + +/* Set a rule counter id (for certain vcaps only) */ +void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + + ri->counter_id = counter_id; +} +EXPORT_SYMBOL_GPL(vcap_rule_set_counter_id); + +/* Provide all rules via a callback interface */ +int vcap_rule_iter(struct vcap_control *vctrl, + int (*callback)(void *, struct vcap_rule *), void *arg) +{ + struct vcap_rule_internal *ri; + struct vcap_admin *admin; + int ret; + + ret = vcap_api_check(vctrl); + if (ret) + return ret; + + /* Iterate all rules in each VCAP instance */ + list_for_each_entry(admin, &vctrl->list, list) { + list_for_each_entry(ri, &admin->rules, list) { + ret = callback(arg, &ri->data); + if (ret) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(vcap_rule_iter); + +int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return err; + if (!ctr) { + pr_err("%s:%d: counter is missing\n", __func__, __LINE__); + return -EINVAL; + } + return vcap_write_counter(ri, ctr); +} +EXPORT_SYMBOL_GPL(vcap_rule_set_counter); + +int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return err; + if (!ctr) { + pr_err("%s:%d: counter is missing\n", __func__, __LINE__); + return -EINVAL; + } + return vcap_read_counter(ri, ctr); +} +EXPORT_SYMBOL_GPL(vcap_rule_get_counter); + +static int vcap_rule_mod_key(struct vcap_rule *rule, + enum vcap_key_field key, + enum vcap_field_type ftype, + struct vcap_client_keyfield_data *data) +{ + struct vcap_client_keyfield *field; + + field = vcap_find_keyfield(rule, key); + if (!field) + return vcap_rule_add_key(rule, key, ftype, data); + vcap_copy_from_client_keyfield(rule, field, data); + return 0; +} + +/* Modify a 32 bit key field with value and mask in the rule */ +int vcap_rule_mod_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask) +{ + struct vcap_client_keyfield_data data; + + data.u32.value = value; + data.u32.mask = mask; + return vcap_rule_mod_key(rule, key, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_mod_key_u32); + +static int vcap_rule_mod_action(struct vcap_rule *rule, + enum vcap_action_field action, + enum vcap_field_type ftype, + struct vcap_client_actionfield_data *data) +{ + struct vcap_client_actionfield *field; + + field = vcap_find_actionfield(rule, action); + if (!field) + return vcap_rule_add_action(rule, action, ftype, data); + vcap_copy_from_client_actionfield(rule, field, data); + return 0; +} + +/* Modify a 32 bit action field with value in the rule */ +int vcap_rule_mod_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, + u32 value) +{ + struct vcap_client_actionfield_data data; + + data.u32.value = value; + return vcap_rule_mod_action(rule, action, VCAP_FIELD_U32, &data); +} +EXPORT_SYMBOL_GPL(vcap_rule_mod_action_u32); + +/* Drop keys in a keylist and any keys that are not supported by the keyset */ +int vcap_filter_rule_keys(struct vcap_rule *rule, + enum vcap_key_field keylist[], int length, + bool drop_unsupported) +{ + struct vcap_rule_internal *ri = to_intrule(rule); + struct vcap_client_keyfield *ckf, *next_ckf; + const struct vcap_field *fields; + enum vcap_key_field key; + int err = 0; + int idx; + + if (length > 0) { + err = -EEXIST; + list_for_each_entry_safe(ckf, next_ckf, + &ri->data.keyfields, ctrl.list) { + key = ckf->ctrl.key; + for (idx = 0; idx < length; ++idx) + if (key == keylist[idx]) { + list_del(&ckf->ctrl.list); + kfree(ckf); + idx++; + err = 0; + } + } + } + if (drop_unsupported) { + err = -EEXIST; + fields = vcap_keyfields(ri->vctrl, ri->admin->vtype, + rule->keyset); + if (!fields) + return err; + list_for_each_entry_safe(ckf, next_ckf, + &ri->data.keyfields, ctrl.list) { + key = ckf->ctrl.key; + if (fields[key].width == 0) { + list_del(&ckf->ctrl.list); + kfree(ckf); + err = 0; + } + } + } + return err; +} +EXPORT_SYMBOL_GPL(vcap_filter_rule_keys); + +/* Make a full copy of an existing rule with a new rule id */ +struct vcap_rule *vcap_copy_rule(struct vcap_rule *erule) +{ + struct vcap_rule_internal *ri = to_intrule(erule); + struct vcap_client_actionfield *caf; + struct vcap_client_keyfield *ckf; + struct vcap_rule *rule; + int err; + + err = vcap_api_check(ri->vctrl); + if (err) + return ERR_PTR(err); + + rule = vcap_alloc_rule(ri->vctrl, ri->ndev, ri->data.vcap_chain_id, + ri->data.user, ri->data.priority, 0); + if (IS_ERR(rule)) + return rule; + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { + /* Add a key duplicate in the new rule */ + err = vcap_rule_add_key(rule, + ckf->ctrl.key, + ckf->ctrl.type, + &ckf->data); + if (err) + goto err; + } + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { + /* Add a action duplicate in the new rule */ + err = vcap_rule_add_action(rule, + caf->ctrl.action, + caf->ctrl.type, + &caf->data); + if (err) + goto err; + } + return rule; +err: + vcap_free_rule(rule); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(vcap_copy_rule); + +#ifdef CONFIG_VCAP_KUNIT_TEST +#include "vcap_api_kunit.c" +#endif diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h new file mode 100644 index 000000000000..689c7270f2a8 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API__ +#define __VCAP_API__ + +#include <linux/types.h> +#include <linux/list.h> +#include <linux/netdevice.h> + +/* Use the generated API model */ +#include "vcap_ag_api.h" + +#define VCAP_CID_LOOKUP_SIZE 100000 /* Chains in a lookup */ +#define VCAP_CID_INGRESS_L0 1000000 /* Ingress Stage 1 Lookup 0 */ +#define VCAP_CID_INGRESS_L1 1100000 /* Ingress Stage 1 Lookup 1 */ +#define VCAP_CID_INGRESS_L2 1200000 /* Ingress Stage 1 Lookup 2 */ +#define VCAP_CID_INGRESS_L3 1300000 /* Ingress Stage 1 Lookup 3 */ +#define VCAP_CID_INGRESS_L4 1400000 /* Ingress Stage 1 Lookup 4 */ +#define VCAP_CID_INGRESS_L5 1500000 /* Ingress Stage 1 Lookup 5 */ + +#define VCAP_CID_PREROUTING_IPV6 3000000 /* Prerouting Stage */ +#define VCAP_CID_PREROUTING 6000000 /* Prerouting Stage */ + +#define VCAP_CID_INGRESS_STAGE2_L0 8000000 /* Ingress Stage 2 Lookup 0 */ +#define VCAP_CID_INGRESS_STAGE2_L1 8100000 /* Ingress Stage 2 Lookup 1 */ +#define VCAP_CID_INGRESS_STAGE2_L2 8200000 /* Ingress Stage 2 Lookup 2 */ +#define VCAP_CID_INGRESS_STAGE2_L3 8300000 /* Ingress Stage 2 Lookup 3 */ + +#define VCAP_CID_EGRESS_L0 10000000 /* Egress Lookup 0 */ +#define VCAP_CID_EGRESS_L1 10100000 /* Egress Lookup 1 */ + +#define VCAP_CID_EGRESS_STAGE2_L0 20000000 /* Egress Stage 2 Lookup 0 */ +#define VCAP_CID_EGRESS_STAGE2_L1 20100000 /* Egress Stage 2 Lookup 1 */ + +/* Known users of the VCAP API */ +enum vcap_user { + VCAP_USER_PTP, + VCAP_USER_MRP, + VCAP_USER_CFM, + VCAP_USER_VLAN, + VCAP_USER_QOS, + VCAP_USER_VCAP_UTIL, + VCAP_USER_TC, + VCAP_USER_TC_EXTRA, + + /* add new users above here */ + + /* used to define VCAP_USER_MAX below */ + __VCAP_USER_AFTER_LAST, + VCAP_USER_MAX = __VCAP_USER_AFTER_LAST - 1, +}; + +/* VCAP information used for displaying data */ +struct vcap_statistics { + char *name; + int count; + const char * const *keyfield_set_names; + const char * const *actionfield_set_names; + const char * const *keyfield_names; + const char * const *actionfield_names; +}; + +/* VCAP key/action field type, position and width */ +struct vcap_field { + u16 type; + u16 width; + u16 offset; +}; + +/* VCAP keyset or actionset type and width */ +struct vcap_set { + u8 type_id; + u8 sw_per_item; + u8 sw_cnt; +}; + +/* VCAP typegroup position and bitvalue */ +struct vcap_typegroup { + u16 offset; + u16 width; + u16 value; +}; + +/* VCAP model data */ +struct vcap_info { + char *name; /* user-friendly name */ + u16 rows; /* number of row in instance */ + u16 sw_count; /* maximum subwords used per rule */ + u16 sw_width; /* bits per subword in a keyset */ + u16 sticky_width; /* sticky bits per rule */ + u16 act_width; /* bits per subword in an actionset */ + u16 default_cnt; /* number of default rules */ + u16 require_cnt_dis; /* not used */ + u16 version; /* vcap rtl version */ + const struct vcap_set *keyfield_set; /* keysets */ + int keyfield_set_size; /* number of keysets */ + const struct vcap_set *actionfield_set; /* actionsets */ + int actionfield_set_size; /* number of actionsets */ + /* map of keys per keyset */ + const struct vcap_field **keyfield_set_map; + /* number of entries in the above map */ + int *keyfield_set_map_size; + /* map of actions per actionset */ + const struct vcap_field **actionfield_set_map; + /* number of entries in the above map */ + int *actionfield_set_map_size; + /* map of keyset typegroups per subword size */ + const struct vcap_typegroup **keyfield_set_typegroups; + /* map of actionset typegroups per subword size */ + const struct vcap_typegroup **actionfield_set_typegroups; +}; + +enum vcap_field_type { + VCAP_FIELD_BIT, + VCAP_FIELD_U32, + VCAP_FIELD_U48, + VCAP_FIELD_U56, + VCAP_FIELD_U64, + VCAP_FIELD_U72, + VCAP_FIELD_U112, + VCAP_FIELD_U128, +}; + +/* VCAP rule data towards the VCAP cache */ +struct vcap_cache_data { + u32 *keystream; + u32 *maskstream; + u32 *actionstream; + u32 counter; + bool sticky; +}; + +/* Selects which part of the rule must be updated */ +enum vcap_selection { + VCAP_SEL_ENTRY = 0x01, + VCAP_SEL_ACTION = 0x02, + VCAP_SEL_COUNTER = 0x04, + VCAP_SEL_ALL = 0xff, +}; + +/* Commands towards the VCAP cache */ +enum vcap_command { + VCAP_CMD_WRITE = 0, + VCAP_CMD_READ = 1, + VCAP_CMD_MOVE_DOWN = 2, + VCAP_CMD_MOVE_UP = 3, + VCAP_CMD_INITIALIZE = 4, +}; + +enum vcap_rule_error { + VCAP_ERR_NONE = 0, /* No known error */ + VCAP_ERR_NO_ADMIN, /* No admin instance */ + VCAP_ERR_NO_NETDEV, /* No netdev instance */ + VCAP_ERR_NO_KEYSET_MATCH, /* No keyset matched the rule keys */ + VCAP_ERR_NO_ACTIONSET_MATCH, /* No actionset matched the rule actions */ + VCAP_ERR_NO_PORT_KEYSET_MATCH, /* No port keyset matched the rule keys */ +}; + +/* Administration of each VCAP instance */ +struct vcap_admin { + struct list_head list; /* for insertion in vcap_control */ + struct list_head rules; /* list of rules */ + struct list_head enabled; /* list of enabled ports */ + struct mutex lock; /* control access to rules */ + enum vcap_type vtype; /* type of vcap */ + int vinst; /* instance number within the same type */ + int first_cid; /* first chain id in this vcap */ + int last_cid; /* last chain id in this vcap */ + int tgt_inst; /* hardware instance number */ + int lookups; /* number of lookups in this vcap type */ + int lookups_per_instance; /* number of lookups in this instance */ + int last_valid_addr; /* top of address range to be used */ + int first_valid_addr; /* bottom of address range to be used */ + int last_used_addr; /* address of lowest added rule */ + bool w32be; /* vcap uses "32bit-word big-endian" encoding */ + struct vcap_cache_data cache; /* encoded rule data */ +}; + +/* Client supplied VCAP rule data */ +struct vcap_rule { + int vcap_chain_id; /* chain used for this rule */ + enum vcap_user user; /* rule owner */ + u16 priority; + u32 id; /* vcap rule id, must be unique, 0 will auto-generate a value */ + u64 cookie; /* used by the client to identify the rule */ + struct list_head keyfields; /* list of vcap_client_keyfield */ + struct list_head actionfields; /* list of vcap_client_actionfield */ + enum vcap_keyfield_set keyset; /* keyset used: may be derived from fields */ + enum vcap_actionfield_set actionset; /* actionset used: may be derived from fields */ + enum vcap_rule_error exterr; /* extended error - used by TC */ + u64 client; /* space for client defined data */ +}; + +/* List of keysets */ +struct vcap_keyset_list { + int max; /* size of the keyset list */ + int cnt; /* count of keysets actually in the list */ + enum vcap_keyfield_set *keysets; /* the list of keysets */ +}; + +/* Client output printf-like function with destination */ +struct vcap_output_print { + __printf(2, 3) + void (*prf)(void *out, const char *fmt, ...); + void *dst; +}; + +/* Client supplied VCAP callback operations */ +struct vcap_operations { + /* validate port keyset operation */ + enum vcap_keyfield_set (*validate_keyset) + (struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto); + /* add default rule fields for the selected keyset operations */ + void (*add_default_fields) + (struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule); + /* cache operations */ + void (*cache_erase) + (struct vcap_admin *admin); + void (*cache_write) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 idx, u32 count); + void (*cache_read) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_selection sel, + u32 idx, + u32 count); + /* block operations */ + void (*init) + (struct net_device *ndev, + struct vcap_admin *admin, + u32 addr, + u32 count); + void (*update) + (struct net_device *ndev, + struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, + u32 addr); + void (*move) + (struct net_device *ndev, + struct vcap_admin *admin, + u32 addr, + int offset, + int count); + /* informational */ + int (*port_info) + (struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out); + /* enable/disable the lookups in a vcap instance */ + int (*enable) + (struct net_device *ndev, + struct vcap_admin *admin, + bool enable); +}; + +/* VCAP API Client control interface */ +struct vcap_control { + struct vcap_operations *ops; /* client supplied operations */ + const struct vcap_info *vcaps; /* client supplied vcap models */ + const struct vcap_statistics *stats; /* client supplied vcap stats */ + struct list_head list; /* list of vcap instances */ +}; + +/* Set client control interface on the API */ +int vcap_api_set_client(struct vcap_control *vctrl); + +#endif /* __VCAP_API__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_client.h b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h new file mode 100644 index 000000000000..0319866f9c94 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_client.h @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API_CLIENT__ +#define __VCAP_API_CLIENT__ + +#include <linux/types.h> +#include <linux/list.h> +#include <linux/netdevice.h> +#include <net/flow_offload.h> + +#include "vcap_api.h" + +/* Client supplied VCAP rule key control part */ +struct vcap_client_keyfield_ctrl { + struct list_head list; /* For insertion into a rule */ + enum vcap_key_field key; + enum vcap_field_type type; +}; + +struct vcap_u1_key { + u8 value; + u8 mask; +}; + +struct vcap_u32_key { + u32 value; + u32 mask; +}; + +struct vcap_u48_key { + u8 value[6]; + u8 mask[6]; +}; + +struct vcap_u56_key { + u8 value[7]; + u8 mask[7]; +}; + +struct vcap_u64_key { + u8 value[8]; + u8 mask[8]; +}; + +struct vcap_u72_key { + u8 value[9]; + u8 mask[9]; +}; + +struct vcap_u112_key { + u8 value[14]; + u8 mask[14]; +}; + +struct vcap_u128_key { + u8 value[16]; + u8 mask[16]; +}; + +/* Client supplied VCAP rule field data */ +struct vcap_client_keyfield_data { + union { + struct vcap_u1_key u1; + struct vcap_u32_key u32; + struct vcap_u48_key u48; + struct vcap_u56_key u56; + struct vcap_u64_key u64; + struct vcap_u72_key u72; + struct vcap_u112_key u112; + struct vcap_u128_key u128; + }; +}; + +/* Client supplied VCAP rule key (value, mask) */ +struct vcap_client_keyfield { + struct vcap_client_keyfield_ctrl ctrl; + struct vcap_client_keyfield_data data; +}; + +/* Client supplied VCAP rule action control part */ +struct vcap_client_actionfield_ctrl { + struct list_head list; /* For insertion into a rule */ + enum vcap_action_field action; + enum vcap_field_type type; +}; + +struct vcap_u1_action { + u8 value; +}; + +struct vcap_u32_action { + u32 value; +}; + +struct vcap_u48_action { + u8 value[6]; +}; + +struct vcap_u56_action { + u8 value[7]; +}; + +struct vcap_u64_action { + u8 value[8]; +}; + +struct vcap_u72_action { + u8 value[9]; +}; + +struct vcap_u112_action { + u8 value[14]; +}; + +struct vcap_u128_action { + u8 value[16]; +}; + +struct vcap_client_actionfield_data { + union { + struct vcap_u1_action u1; + struct vcap_u32_action u32; + struct vcap_u48_action u48; + struct vcap_u56_action u56; + struct vcap_u64_action u64; + struct vcap_u72_action u72; + struct vcap_u112_action u112; + struct vcap_u128_action u128; + }; +}; + +struct vcap_client_actionfield { + struct vcap_client_actionfield_ctrl ctrl; + struct vcap_client_actionfield_data data; +}; + +enum vcap_bit { + VCAP_BIT_ANY, + VCAP_BIT_0, + VCAP_BIT_1 +}; + +struct vcap_counter { + u32 value; + bool sticky; +}; + +/* Enable/Disable the VCAP instance lookups. Chain id 0 means disable */ +int vcap_enable_lookups(struct vcap_control *vctrl, struct net_device *ndev, + int chain_id, unsigned long cookie, bool enable); + +/* VCAP rule operations */ +/* Allocate a rule and fill in the basic information */ +struct vcap_rule *vcap_alloc_rule(struct vcap_control *vctrl, + struct net_device *ndev, + int vcap_chain_id, + enum vcap_user user, + u16 priority, + u32 id); +/* Free mem of a rule owned by client */ +void vcap_free_rule(struct vcap_rule *rule); +/* Validate a rule before adding it to the VCAP */ +int vcap_val_rule(struct vcap_rule *rule, u16 l3_proto); +/* Add rule to a VCAP instance */ +int vcap_add_rule(struct vcap_rule *rule); +/* Delete rule in a VCAP instance */ +int vcap_del_rule(struct vcap_control *vctrl, struct net_device *ndev, u32 id); +/* Make a full copy of an existing rule with a new rule id */ +struct vcap_rule *vcap_copy_rule(struct vcap_rule *rule); +/* Get rule from a VCAP instance */ +struct vcap_rule *vcap_get_rule(struct vcap_control *vctrl, u32 id); +/* Update existing rule */ +int vcap_mod_rule(struct vcap_rule *rule); + +/* Update the keyset for the rule */ +int vcap_set_rule_set_keyset(struct vcap_rule *rule, + enum vcap_keyfield_set keyset); +/* Update the actionset for the rule */ +int vcap_set_rule_set_actionset(struct vcap_rule *rule, + enum vcap_actionfield_set actionset); +/* Set a rule counter id (for certain VCAPs only) */ +void vcap_rule_set_counter_id(struct vcap_rule *rule, u32 counter_id); + +/* VCAP rule field operations */ +int vcap_rule_add_key_bit(struct vcap_rule *rule, enum vcap_key_field key, + enum vcap_bit val); +int vcap_rule_add_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask); +int vcap_rule_add_key_u48(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u48_key *fieldval); +int vcap_rule_add_key_u72(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u72_key *fieldval); +int vcap_rule_add_key_u128(struct vcap_rule *rule, enum vcap_key_field key, + struct vcap_u128_key *fieldval); +int vcap_rule_add_action_bit(struct vcap_rule *rule, + enum vcap_action_field action, enum vcap_bit val); +int vcap_rule_add_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, u32 value); + +/* VCAP rule counter operations */ +int vcap_rule_set_counter(struct vcap_rule *rule, struct vcap_counter *ctr); +int vcap_rule_get_counter(struct vcap_rule *rule, struct vcap_counter *ctr); + +/* VCAP lookup operations */ +/* Convert a chain id to a VCAP lookup index */ +int vcap_chain_id_to_lookup(struct vcap_admin *admin, int cur_cid); +/* Lookup a vcap instance using chain id */ +struct vcap_admin *vcap_find_admin(struct vcap_control *vctrl, int cid); +/* Find information on a key field in a rule */ +const struct vcap_field *vcap_lookup_keyfield(struct vcap_rule *rule, + enum vcap_key_field key); +/* Find a rule id with a provided cookie */ +int vcap_lookup_rule_by_cookie(struct vcap_control *vctrl, u64 cookie); +/* Is the next chain id in the following lookup, possible in another VCAP */ +bool vcap_is_next_lookup(struct vcap_control *vctrl, int cur_cid, int next_cid); +/* Provide all rules via a callback interface */ +int vcap_rule_iter(struct vcap_control *vctrl, + int (*callback)(void *, struct vcap_rule *), void *arg); +/* Match a list of keys against the keysets available in a vcap type */ +bool vcap_rule_find_keysets(struct vcap_rule *rule, + struct vcap_keyset_list *matches); +/* Return the keyset information for the keyset */ +const struct vcap_set *vcap_keyfieldset(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset); +/* Copy to host byte order */ +void vcap_netbytes_copy(u8 *dst, u8 *src, int count); + +/* Convert validation error code into tc extact error message */ +void vcap_set_tc_exterr(struct flow_cls_offload *fco, struct vcap_rule *vrule); + +/* Cleanup a VCAP instance */ +int vcap_del_rules(struct vcap_control *vctrl, struct vcap_admin *admin); + +/* Add a keyset to a keyset list */ +bool vcap_keyset_list_add(struct vcap_keyset_list *keysetlist, + enum vcap_keyfield_set keyset); +/* Drop keys in a keylist and any keys that are not supported by the keyset */ +int vcap_filter_rule_keys(struct vcap_rule *rule, + enum vcap_key_field keylist[], int length, + bool drop_unsupported); + +/* map keyset id to a string with the keyset name */ +const char *vcap_keyset_name(struct vcap_control *vctrl, + enum vcap_keyfield_set keyset); +/* map key field id to a string with the key name */ +const char *vcap_keyfield_name(struct vcap_control *vctrl, + enum vcap_key_field key); + +/* Modify a 32 bit key field with value and mask in the rule */ +int vcap_rule_mod_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 value, u32 mask); +/* Modify a 32 bit action field with value in the rule */ +int vcap_rule_mod_action_u32(struct vcap_rule *rule, + enum vcap_action_field action, + u32 value); + +/* Get a 32 bit key field value and mask from the rule */ +int vcap_rule_get_key_u32(struct vcap_rule *rule, enum vcap_key_field key, + u32 *value, u32 *mask); + +#endif /* __VCAP_API_CLIENT__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c new file mode 100644 index 000000000000..895bfff550d2 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Microchip VCAP API debug file system support + * + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries. + * + */ + +#include "vcap_api_private.h" +#include "vcap_api_debugfs.h" + +struct vcap_admin_debugfs_info { + struct vcap_control *vctrl; + struct vcap_admin *admin; +}; + +struct vcap_port_debugfs_info { + struct vcap_control *vctrl; + struct net_device *ndev; +}; + +/* Dump the keyfields value and mask values */ +static void vcap_debugfs_show_rule_keyfield(struct vcap_control *vctrl, + struct vcap_output_print *out, + enum vcap_key_field key, + const struct vcap_field *keyfield, + struct vcap_client_keyfield_data *data) +{ + bool hex = false; + u8 *value, *mask; + int idx, bytes; + + out->prf(out->dst, " %s: W%d: ", vcap_keyfield_name(vctrl, key), + keyfield[key].width); + + switch (keyfield[key].type) { + case VCAP_FIELD_BIT: + out->prf(out->dst, "%d/%d", data->u1.value, data->u1.mask); + break; + case VCAP_FIELD_U32: + value = (u8 *)(&data->u32.value); + mask = (u8 *)(&data->u32.mask); + + if (key == VCAP_KF_L3_IP4_SIP || key == VCAP_KF_L3_IP4_DIP) { + out->prf(out->dst, "%pI4h/%pI4h", &data->u32.value, + &data->u32.mask); + } else if (key == VCAP_KF_ETYPE || + key == VCAP_KF_IF_IGR_PORT_MASK) { + hex = true; + } else { + u32 fmsk = (1 << keyfield[key].width) - 1; + + out->prf(out->dst, "%u/%u", data->u32.value & fmsk, + data->u32.mask & fmsk); + } + break; + case VCAP_FIELD_U48: + value = data->u48.value; + mask = data->u48.mask; + if (key == VCAP_KF_L2_SMAC || key == VCAP_KF_L2_DMAC) + out->prf(out->dst, "%pMR/%pMR", data->u48.value, + data->u48.mask); + else + hex = true; + break; + case VCAP_FIELD_U56: + value = data->u56.value; + mask = data->u56.mask; + hex = true; + break; + case VCAP_FIELD_U64: + value = data->u64.value; + mask = data->u64.mask; + hex = true; + break; + case VCAP_FIELD_U72: + value = data->u72.value; + mask = data->u72.mask; + hex = true; + break; + case VCAP_FIELD_U112: + value = data->u112.value; + mask = data->u112.mask; + hex = true; + break; + case VCAP_FIELD_U128: + if (key == VCAP_KF_L3_IP6_SIP || key == VCAP_KF_L3_IP6_DIP) { + u8 nvalue[16], nmask[16]; + + vcap_netbytes_copy(nvalue, data->u128.value, + sizeof(nvalue)); + vcap_netbytes_copy(nmask, data->u128.mask, + sizeof(nmask)); + out->prf(out->dst, "%pI6/%pI6", nvalue, nmask); + } else { + hex = true; + } + break; + } + if (hex) { + bytes = DIV_ROUND_UP(keyfield[key].width, BITS_PER_BYTE); + out->prf(out->dst, "0x"); + for (idx = 0; idx < bytes; ++idx) + out->prf(out->dst, "%02x", value[bytes - idx - 1]); + out->prf(out->dst, "/0x"); + for (idx = 0; idx < bytes; ++idx) + out->prf(out->dst, "%02x", mask[bytes - idx - 1]); + } + out->prf(out->dst, "\n"); +} + +static void +vcap_debugfs_show_rule_actionfield(struct vcap_control *vctrl, + struct vcap_output_print *out, + enum vcap_action_field action, + const struct vcap_field *actionfield, + u8 *value) +{ + bool hex = false; + int idx, bytes; + u32 fmsk, val; + + out->prf(out->dst, " %s: W%d: ", + vcap_actionfield_name(vctrl, action), + actionfield[action].width); + + switch (actionfield[action].type) { + case VCAP_FIELD_BIT: + out->prf(out->dst, "%d", value[0]); + break; + case VCAP_FIELD_U32: + fmsk = (1 << actionfield[action].width) - 1; + val = *(u32 *)value; + out->prf(out->dst, "%u", val & fmsk); + break; + case VCAP_FIELD_U48: + case VCAP_FIELD_U56: + case VCAP_FIELD_U64: + case VCAP_FIELD_U72: + case VCAP_FIELD_U112: + case VCAP_FIELD_U128: + hex = true; + break; + } + if (hex) { + bytes = DIV_ROUND_UP(actionfield[action].width, BITS_PER_BYTE); + out->prf(out->dst, "0x"); + for (idx = 0; idx < bytes; ++idx) + out->prf(out->dst, "%02x", value[bytes - idx - 1]); + } + out->prf(out->dst, "\n"); +} + +static int vcap_debugfs_show_rule_keyset(struct vcap_rule_internal *ri, + struct vcap_output_print *out) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_admin *admin = ri->admin; + enum vcap_keyfield_set keysets[10]; + const struct vcap_field *keyfield; + enum vcap_type vt = admin->vtype; + struct vcap_client_keyfield *ckf; + struct vcap_keyset_list matches; + u32 *maskstream; + u32 *keystream; + int res; + + keystream = admin->cache.keystream; + maskstream = admin->cache.maskstream; + matches.keysets = keysets; + matches.cnt = 0; + matches.max = ARRAY_SIZE(keysets); + res = vcap_find_keystream_keysets(vctrl, vt, keystream, maskstream, + false, 0, &matches); + if (res < 0) { + pr_err("%s:%d: could not find valid keysets: %d\n", + __func__, __LINE__, res); + return -EINVAL; + } + out->prf(out->dst, " keysets:"); + for (int idx = 0; idx < matches.cnt; ++idx) + out->prf(out->dst, " %s", + vcap_keyset_name(vctrl, matches.keysets[idx])); + out->prf(out->dst, "\n"); + out->prf(out->dst, " keyset_sw: %d\n", ri->keyset_sw); + out->prf(out->dst, " keyset_sw_regs: %d\n", ri->keyset_sw_regs); + + list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { + keyfield = vcap_keyfields(vctrl, admin->vtype, ri->data.keyset); + vcap_debugfs_show_rule_keyfield(vctrl, out, ckf->ctrl.key, + keyfield, &ckf->data); + } + + return 0; +} + +static int vcap_debugfs_show_rule_actionset(struct vcap_rule_internal *ri, + struct vcap_output_print *out) +{ + struct vcap_control *vctrl = ri->vctrl; + struct vcap_admin *admin = ri->admin; + const struct vcap_field *actionfield; + struct vcap_client_actionfield *caf; + + out->prf(out->dst, " actionset: %s\n", + vcap_actionset_name(vctrl, ri->data.actionset)); + out->prf(out->dst, " actionset_sw: %d\n", ri->actionset_sw); + out->prf(out->dst, " actionset_sw_regs: %d\n", ri->actionset_sw_regs); + + list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { + actionfield = vcap_actionfields(vctrl, admin->vtype, + ri->data.actionset); + vcap_debugfs_show_rule_actionfield(vctrl, out, caf->ctrl.action, + actionfield, + &caf->data.u1.value); + } + + return 0; +} + +static void vcap_show_admin_rule(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out, + struct vcap_rule_internal *ri) +{ + ri->counter.value = admin->cache.counter; + ri->counter.sticky = admin->cache.sticky; + out->prf(out->dst, + "rule: %u, addr: [%d,%d], X%d, ctr[%d]: %d, hit: %d\n", + ri->data.id, ri->addr, ri->addr + ri->size - 1, ri->size, + ri->counter_id, ri->counter.value, ri->counter.sticky); + out->prf(out->dst, " chain_id: %d\n", ri->data.vcap_chain_id); + out->prf(out->dst, " user: %d\n", ri->data.user); + out->prf(out->dst, " priority: %d\n", ri->data.priority); + vcap_debugfs_show_rule_keyset(ri, out); + vcap_debugfs_show_rule_actionset(ri, out); +} + +static void vcap_show_admin_info(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + const struct vcap_info *vcap = &vctrl->vcaps[admin->vtype]; + + out->prf(out->dst, "name: %s\n", vcap->name); + out->prf(out->dst, "rows: %d\n", vcap->rows); + out->prf(out->dst, "sw_count: %d\n", vcap->sw_count); + out->prf(out->dst, "sw_width: %d\n", vcap->sw_width); + out->prf(out->dst, "sticky_width: %d\n", vcap->sticky_width); + out->prf(out->dst, "act_width: %d\n", vcap->act_width); + out->prf(out->dst, "default_cnt: %d\n", vcap->default_cnt); + out->prf(out->dst, "require_cnt_dis: %d\n", vcap->require_cnt_dis); + out->prf(out->dst, "version: %d\n", vcap->version); + out->prf(out->dst, "vtype: %d\n", admin->vtype); + out->prf(out->dst, "vinst: %d\n", admin->vinst); + out->prf(out->dst, "first_cid: %d\n", admin->first_cid); + out->prf(out->dst, "last_cid: %d\n", admin->last_cid); + out->prf(out->dst, "lookups: %d\n", admin->lookups); + out->prf(out->dst, "first_valid_addr: %d\n", admin->first_valid_addr); + out->prf(out->dst, "last_valid_addr: %d\n", admin->last_valid_addr); + out->prf(out->dst, "last_used_addr: %d\n", admin->last_used_addr); +} + +static int vcap_show_admin(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + struct vcap_rule_internal *elem; + struct vcap_rule *vrule; + int ret = 0; + + vcap_show_admin_info(vctrl, admin, out); + list_for_each_entry(elem, &admin->rules, list) { + vrule = vcap_get_rule(vctrl, elem->data.id); + if (IS_ERR_OR_NULL(vrule)) { + ret = PTR_ERR(vrule); + break; + } + + out->prf(out->dst, "\n"); + vcap_show_admin_rule(vctrl, admin, out, to_intrule(vrule)); + vcap_free_rule(vrule); + } + return ret; +} + +static int vcap_show_admin_raw(struct vcap_control *vctrl, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + enum vcap_keyfield_set keysets[10]; + enum vcap_type vt = admin->vtype; + struct vcap_keyset_list kslist; + struct vcap_rule_internal *ri; + const struct vcap_set *info; + int addr, idx; + int ret; + + if (list_empty(&admin->rules)) + return 0; + + ret = vcap_api_check(vctrl); + if (ret) + return ret; + + ri = list_first_entry(&admin->rules, struct vcap_rule_internal, list); + + /* Go from higher to lower addresses searching for a keyset */ + kslist.keysets = keysets; + kslist.max = ARRAY_SIZE(keysets); + for (addr = admin->last_valid_addr; addr >= admin->first_valid_addr; + --addr) { + kslist.cnt = 0; + ret = vcap_addr_keysets(vctrl, ri->ndev, admin, addr, &kslist); + if (ret < 0) + continue; + info = vcap_keyfieldset(vctrl, vt, kslist.keysets[0]); + if (!info) + continue; + if (addr % info->sw_per_item) { + pr_info("addr: %d X%d error rule, keyset: %s\n", + addr, + info->sw_per_item, + vcap_keyset_name(vctrl, kslist.keysets[0])); + } else { + out->prf(out->dst, " addr: %d, X%d rule, keysets:", + addr, + info->sw_per_item); + for (idx = 0; idx < kslist.cnt; ++idx) + out->prf(out->dst, " %s", + vcap_keyset_name(vctrl, + kslist.keysets[idx])); + out->prf(out->dst, "\n"); + } + } + return 0; +} + +/* Show the port configuration and status */ +static int vcap_port_debugfs_show(struct seq_file *m, void *unused) +{ + struct vcap_port_debugfs_info *info = m->private; + struct vcap_admin *admin; + struct vcap_output_print out = { + .prf = (void *)seq_printf, + .dst = m, + }; + + list_for_each_entry(admin, &info->vctrl->list, list) { + if (admin->vinst) + continue; + info->vctrl->ops->port_info(info->ndev, admin, &out); + } + return 0; +} +DEFINE_SHOW_ATTRIBUTE(vcap_port_debugfs); + +void vcap_port_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl, + struct net_device *ndev) +{ + struct vcap_port_debugfs_info *info; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return; + + info->vctrl = vctrl; + info->ndev = ndev; + debugfs_create_file(netdev_name(ndev), 0444, parent, info, + &vcap_port_debugfs_fops); +} +EXPORT_SYMBOL_GPL(vcap_port_debugfs); + +/* Show the full VCAP instance data (rules with all fields) */ +static int vcap_debugfs_show(struct seq_file *m, void *unused) +{ + struct vcap_admin_debugfs_info *info = m->private; + struct vcap_output_print out = { + .prf = (void *)seq_printf, + .dst = m, + }; + + return vcap_show_admin(info->vctrl, info->admin, &out); +} +DEFINE_SHOW_ATTRIBUTE(vcap_debugfs); + +/* Show the raw VCAP instance data (rules with address info) */ +static int vcap_raw_debugfs_show(struct seq_file *m, void *unused) +{ + struct vcap_admin_debugfs_info *info = m->private; + struct vcap_output_print out = { + .prf = (void *)seq_printf, + .dst = m, + }; + + return vcap_show_admin_raw(info->vctrl, info->admin, &out); +} +DEFINE_SHOW_ATTRIBUTE(vcap_raw_debugfs); + +struct dentry *vcap_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl) +{ + struct vcap_admin_debugfs_info *info; + struct vcap_admin *admin; + struct dentry *dir; + char name[50]; + + dir = debugfs_create_dir("vcaps", parent); + if (PTR_ERR_OR_ZERO(dir)) + return NULL; + list_for_each_entry(admin, &vctrl->list, list) { + sprintf(name, "raw_%s_%d", vctrl->vcaps[admin->vtype].name, + admin->vinst); + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return NULL; + info->vctrl = vctrl; + info->admin = admin; + debugfs_create_file(name, 0444, dir, info, + &vcap_raw_debugfs_fops); + sprintf(name, "%s_%d", vctrl->vcaps[admin->vtype].name, + admin->vinst); + debugfs_create_file(name, 0444, dir, info, &vcap_debugfs_fops); + } + return dir; +} +EXPORT_SYMBOL_GPL(vcap_debugfs); + +#ifdef CONFIG_VCAP_KUNIT_TEST +#include "vcap_api_debugfs_kunit.c" +#endif diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h new file mode 100644 index 000000000000..9f2c59b5f6f5 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API_DEBUGFS__ +#define __VCAP_API_DEBUGFS__ + +#include <linux/types.h> +#include <linux/debugfs.h> +#include <linux/netdevice.h> + +#include "vcap_api.h" + +#if defined(CONFIG_DEBUG_FS) + +void vcap_port_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl, + struct net_device *ndev); + +/* Create a debugFS entry for a vcap instance */ +struct dentry *vcap_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl); + +#else + +static inline void vcap_port_debugfs(struct device *dev, struct dentry *parent, + struct vcap_control *vctrl, + struct net_device *ndev) +{ +} + +static inline struct dentry *vcap_debugfs(struct device *dev, + struct dentry *parent, + struct vcap_control *vctrl) +{ + return NULL; +} + +#endif +#endif /* __VCAP_API_DEBUGFS__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c new file mode 100644 index 000000000000..cf594668d5d9 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs_kunit.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API kunit test suite + */ + +#include <kunit/test.h> +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "vcap_api_debugfs.h" +#include "vcap_model_kunit.h" + +/* First we have the test infrastructure that emulates the platform + * implementation + */ +#define TEST_BUF_CNT 100 +#define TEST_BUF_SZ 350 +#define STREAMWSIZE 64 + +static u32 test_updateaddr[STREAMWSIZE] = {}; +static int test_updateaddridx; +static int test_cache_erase_count; +static u32 test_init_start; +static u32 test_init_count; +static u32 test_hw_counter_id; +static struct vcap_cache_data test_hw_cache; +static struct net_device test_netdev = {}; +static int test_move_addr; +static int test_move_offset; +static int test_move_count; +static char test_pr_buffer[TEST_BUF_CNT][TEST_BUF_SZ]; +static int test_pr_bufferidx; +static int test_pr_idx; + +/* Callback used by the VCAP API */ +static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + int idx; + + if (kslist->cnt > 0) { + switch (admin->vtype) { + case VCAP_TYPE_IS0: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_ETAG) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == + VCAP_KFS_PURE_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == + VCAP_KFS_NORMAL_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == + VCAP_KFS_NORMAL_7TUPLE) + return kslist->keysets[idx]; + } + break; + case VCAP_TYPE_IS2: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_MAC_ETYPE) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_ARP) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_IP_7TUPLE) + return kslist->keysets[idx]; + } + break; + default: + pr_info("%s:%d: no validation for VCAP %d\n", + __func__, __LINE__, admin->vtype); + break; + } + } + return -EINVAL; +} + +/* Callback used by the VCAP API */ +static void test_add_def_fields(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + if (admin->vinst == 0 || admin->vinst == 2) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_0); +} + +/* Callback used by the VCAP API */ +static void test_cache_erase(struct vcap_admin *admin) +{ + if (test_cache_erase_count) { + memset(admin->cache.keystream, 0, test_cache_erase_count); + memset(admin->cache.maskstream, 0, test_cache_erase_count); + memset(admin->cache.actionstream, 0, test_cache_erase_count); + test_cache_erase_count = 0; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_init(struct net_device *ndev, struct vcap_admin *admin, + u32 start, u32 count) +{ + test_init_start = start; + test_init_count = count; +} + +/* Callback used by the VCAP API */ +static void test_cache_read(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + pr_debug("%s:%d: %d %d\n", __func__, __LINE__, start, count); + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before decoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + admin->cache.counter = test_hw_cache.counter; + admin->cache.sticky = test_hw_cache.sticky; + break; + case VCAP_SEL_ALL: + pr_debug("%s:%d\n", __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_write(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before encoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + test_hw_cache.counter = admin->cache.counter; + test_hw_cache.sticky = admin->cache.sticky; + break; + case VCAP_SEL_ALL: + pr_err("%s:%d: cannot write all streams at once\n", + __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_update(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, u32 addr) +{ + if (test_updateaddridx < ARRAY_SIZE(test_updateaddr)) + test_updateaddr[test_updateaddridx] = addr; + else + pr_err("%s:%d: overflow: %d\n", __func__, __LINE__, + test_updateaddridx); + test_updateaddridx++; +} + +static void test_cache_move(struct net_device *ndev, struct vcap_admin *admin, + u32 addr, int offset, int count) +{ + test_move_addr = addr; + test_move_offset = offset; + test_move_count = count; +} + +/* Provide port information via a callback interface */ +static int vcap_test_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} + +static int vcap_test_enable(struct net_device *ndev, + struct vcap_admin *admin, + bool enable) +{ + return 0; +} + +static struct vcap_operations test_callbacks = { + .validate_keyset = test_val_keyset, + .add_default_fields = test_add_def_fields, + .cache_erase = test_cache_erase, + .cache_write = test_cache_write, + .cache_read = test_cache_read, + .init = test_cache_init, + .update = test_cache_update, + .move = test_cache_move, + .port_info = vcap_test_port_info, + .enable = vcap_test_enable, +}; + +static struct vcap_control test_vctrl = { + .vcaps = kunit_test_vcaps, + .stats = &kunit_test_vcap_stats, + .ops = &test_callbacks, +}; + +static void vcap_test_api_init(struct vcap_admin *admin) +{ + /* Initialize the shared objects */ + INIT_LIST_HEAD(&test_vctrl.list); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + list_add_tail(&admin->list, &test_vctrl.list); + memset(test_updateaddr, 0, sizeof(test_updateaddr)); + test_updateaddridx = 0; + test_pr_bufferidx = 0; + test_pr_idx = 0; +} + +/* callback used by the show_admin function */ +static __printf(2, 3) +int test_prf(void *out, const char *fmt, ...) +{ + static char test_buffer[TEST_BUF_SZ]; + va_list args; + int idx, cnt; + + if (test_pr_bufferidx >= TEST_BUF_CNT) { + pr_err("%s:%d: overflow: %d\n", __func__, __LINE__, + test_pr_bufferidx); + return 0; + } + + va_start(args, fmt); + cnt = vscnprintf(test_buffer, TEST_BUF_SZ, fmt, args); + va_end(args); + + for (idx = 0; idx < cnt; ++idx) { + test_pr_buffer[test_pr_bufferidx][test_pr_idx] = + test_buffer[idx]; + if (test_buffer[idx] == '\n') { + test_pr_buffer[test_pr_bufferidx][++test_pr_idx] = 0; + test_pr_idx = 0; + test_pr_bufferidx++; + } else { + ++test_pr_idx; + } + } + + return cnt; +} + +/* Define the test cases. */ + +static void vcap_api_addr_keyset_test(struct kunit *test) +{ + u32 keydata[12] = { + 0x40450042, 0x000feaf3, 0x00000003, 0x00050600, + 0x10203040, 0x00075880, 0x633c6864, 0x00040003, + 0x00000020, 0x00000008, 0x00000240, 0x00000000, + }; + u32 mskdata[12] = { + 0x0030ff80, 0xfff00000, 0xfffffffc, 0xfff000ff, + 0x00000000, 0xfff00000, 0x00000000, 0xfff3fffc, + 0xffffffc0, 0xffffffff, 0xfffffc03, 0xffffffff, + }; + u32 actdata[12] = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + enum vcap_keyfield_set keysets[10]; + struct vcap_keyset_list matches; + int ret, idx, addr; + + vcap_test_api_init(&admin); + + /* Go from higher to lower addresses searching for a keyset */ + matches.keysets = keysets; + matches.cnt = 0; + matches.max = ARRAY_SIZE(keysets); + for (idx = ARRAY_SIZE(keydata) - 1, addr = 799; idx > 0; + --idx, --addr) { + admin.cache.keystream = &keydata[idx]; + admin.cache.maskstream = &mskdata[idx]; + ret = vcap_addr_keysets(&test_vctrl, &test_netdev, &admin, + addr, &matches); + KUNIT_EXPECT_EQ(test, -EINVAL, ret); + } + + /* Finally we hit the start of the rule */ + admin.cache.keystream = &keydata[idx]; + admin.cache.maskstream = &mskdata[idx]; + matches.cnt = 0; + ret = vcap_addr_keysets(&test_vctrl, &test_netdev, &admin, + addr, &matches); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, matches.cnt, 1); + KUNIT_EXPECT_EQ(test, matches.keysets[0], VCAP_KFS_MAC_ETYPE); +} + +static void vcap_api_show_admin_raw_test(struct kunit *test) +{ + u32 keydata[4] = { + 0x40450042, 0x000feaf3, 0x00000003, 0x00050600, + }; + u32 mskdata[4] = { + 0x0030ff80, 0xfff00000, 0xfffffffc, 0xfff000ff, + }; + u32 actdata[12] = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + .first_valid_addr = 786, + .last_valid_addr = 788, + }; + struct vcap_rule_internal ri = { + .ndev = &test_netdev, + }; + struct vcap_output_print out = { + .prf = (void *)test_prf, + }; + const char *test_expected = + " addr: 786, X6 rule, keysets: VCAP_KFS_MAC_ETYPE\n"; + int ret; + + vcap_test_api_init(&admin); + list_add_tail(&ri.list, &admin.rules); + + ret = vcap_show_admin_raw(&test_vctrl, &admin, &out); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_STREQ(test, test_expected, test_pr_buffer[0]); +} + +static const char * const test_admin_info_expect[] = { + "name: is2\n", + "rows: 256\n", + "sw_count: 12\n", + "sw_width: 52\n", + "sticky_width: 1\n", + "act_width: 110\n", + "default_cnt: 73\n", + "require_cnt_dis: 0\n", + "version: 1\n", + "vtype: 2\n", + "vinst: 0\n", + "first_cid: 10000\n", + "last_cid: 19999\n", + "lookups: 4\n", + "first_valid_addr: 0\n", + "last_valid_addr: 3071\n", + "last_used_addr: 794\n", +}; + +static void vcap_api_show_admin_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 794, + }; + struct vcap_output_print out = { + .prf = (void *)test_prf, + }; + int idx; + + vcap_test_api_init(&admin); + + vcap_show_admin_info(&test_vctrl, &admin, &out); + for (idx = 0; idx < test_pr_bufferidx; ++idx) { + /* pr_info("log[%02d]: %s", idx, test_pr_buffer[idx]); */ + KUNIT_EXPECT_STREQ(test, test_admin_info_expect[idx], + test_pr_buffer[idx]); + } +} + +static const char * const test_admin_expect[] = { + "name: is2\n", + "rows: 256\n", + "sw_count: 12\n", + "sw_width: 52\n", + "sticky_width: 1\n", + "act_width: 110\n", + "default_cnt: 73\n", + "require_cnt_dis: 0\n", + "version: 1\n", + "vtype: 2\n", + "vinst: 0\n", + "first_cid: 8000000\n", + "last_cid: 8199999\n", + "lookups: 4\n", + "first_valid_addr: 0\n", + "last_valid_addr: 3071\n", + "last_used_addr: 794\n", + "\n", + "rule: 100, addr: [794,799], X6, ctr[0]: 0, hit: 0\n", + " chain_id: 0\n", + " user: 0\n", + " priority: 0\n", + " keysets: VCAP_KFS_MAC_ETYPE\n", + " keyset_sw: 6\n", + " keyset_sw_regs: 2\n", + " ETYPE_LEN_IS: W1: 1/1\n", + " IF_IGR_PORT_MASK: W32: 0xffabcd01/0xffffffff\n", + " IF_IGR_PORT_MASK_RNG: W4: 5/15\n", + " L2_DMAC: W48: 01:02:03:04:05:06/ff:ff:ff:ff:ff:ff\n", + " L2_PAYLOAD_ETYPE: W64: 0x9000002000000081/0xff000000000000ff\n", + " L2_SMAC: W48: b1:9e:34:32:75:88/ff:ff:ff:ff:ff:ff\n", + " LOOKUP_FIRST_IS: W1: 1/1\n", + " TYPE: W4: 0/15\n", + " actionset: VCAP_AFS_BASE_TYPE\n", + " actionset_sw: 3\n", + " actionset_sw_regs: 4\n", + " CNT_ID: W12: 100\n", + " MATCH_ID: W16: 1\n", + " MATCH_ID_MASK: W16: 1\n", + " POLICE_ENA: W1: 1\n", + " PORT_MASK: W68: 0x0514670115f3324589\n", +}; + +static void vcap_api_show_admin_rule_test(struct kunit *test) +{ + u32 keydata[] = { + 0x40450042, 0x000feaf3, 0x00000003, 0x00050600, + 0x10203040, 0x00075880, 0x633c6864, 0x00040003, + 0x00000020, 0x00000008, 0x00000240, 0x00000000, + }; + u32 mskdata[] = { + 0x0030ff80, 0xfff00000, 0xfffffffc, 0xfff000ff, + 0x00000000, 0xfff00000, 0x00000000, 0xfff3fffc, + 0xffffffc0, 0xffffffff, 0xfffffc03, 0xffffffff, + }; + u32 actdata[] = { + 0x00040002, 0xf3324589, 0x14670115, 0x00000005, + 0x00000000, 0x00100000, 0x06400010, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .first_cid = 8000000, + .last_cid = 8199999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 794, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .data = { + .id = 100, + .keyset = VCAP_KFS_MAC_ETYPE, + .actionset = VCAP_AFS_BASE_TYPE, + }, + .size = 6, + .keyset_sw = 6, + .keyset_sw_regs = 2, + .actionset_sw = 3, + .actionset_sw_regs = 4, + .addr = 794, + .vctrl = &test_vctrl, + }; + struct vcap_output_print out = { + .prf = (void *)test_prf, + }; + int ret, idx; + + vcap_test_api_init(&admin); + list_add_tail(&ri.list, &admin.rules); + + ret = vcap_show_admin(&test_vctrl, &admin, &out); + KUNIT_EXPECT_EQ(test, 0, ret); + for (idx = 0; idx < test_pr_bufferidx; ++idx) { + /* pr_info("log[%02d]: %s", idx, test_pr_buffer[idx]); */ + KUNIT_EXPECT_STREQ(test, test_admin_expect[idx], + test_pr_buffer[idx]); + } +} + +static struct kunit_case vcap_api_debugfs_test_cases[] = { + KUNIT_CASE(vcap_api_addr_keyset_test), + KUNIT_CASE(vcap_api_show_admin_raw_test), + KUNIT_CASE(vcap_api_show_admin_test), + KUNIT_CASE(vcap_api_show_admin_rule_test), + {} +}; + +static struct kunit_suite vcap_api_debugfs_test_suite = { + .name = "VCAP_API_DebugFS_Testsuite", + .test_cases = vcap_api_debugfs_test_cases, +}; + +kunit_test_suite(vcap_api_debugfs_test_suite); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c new file mode 100644 index 000000000000..76a31215ebfb --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -0,0 +1,2245 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API kunit test suite + */ + +#include <kunit/test.h> +#include "vcap_api.h" +#include "vcap_api_client.h" +#include "vcap_model_kunit.h" + +/* First we have the test infrastructure that emulates the platform + * implementation + */ +#define TEST_BUF_CNT 100 +#define TEST_BUF_SZ 350 +#define STREAMWSIZE 64 + +static u32 test_updateaddr[STREAMWSIZE] = {}; +static int test_updateaddridx; +static int test_cache_erase_count; +static u32 test_init_start; +static u32 test_init_count; +static u32 test_hw_counter_id; +static struct vcap_cache_data test_hw_cache; +static struct net_device test_netdev = {}; +static int test_move_addr; +static int test_move_offset; +static int test_move_count; + +/* Callback used by the VCAP API */ +static enum vcap_keyfield_set test_val_keyset(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule, + struct vcap_keyset_list *kslist, + u16 l3_proto) +{ + int idx; + + if (kslist->cnt > 0) { + switch (admin->vtype) { + case VCAP_TYPE_IS0: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_ETAG) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_PURE_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_NORMAL_5TUPLE_IP4) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_NORMAL_7TUPLE) + return kslist->keysets[idx]; + } + break; + case VCAP_TYPE_IS2: + for (idx = 0; idx < kslist->cnt; idx++) { + if (kslist->keysets[idx] == VCAP_KFS_MAC_ETYPE) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_ARP) + return kslist->keysets[idx]; + if (kslist->keysets[idx] == VCAP_KFS_IP_7TUPLE) + return kslist->keysets[idx]; + } + break; + default: + pr_info("%s:%d: no validation for VCAP %d\n", + __func__, __LINE__, admin->vtype); + break; + } + } + return -EINVAL; +} + +/* Callback used by the VCAP API */ +static void test_add_def_fields(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_rule *rule) +{ + if (admin->vinst == 0 || admin->vinst == 2) + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); + else + vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0); +} + +/* Callback used by the VCAP API */ +static void test_cache_erase(struct vcap_admin *admin) +{ + if (test_cache_erase_count) { + memset(admin->cache.keystream, 0, test_cache_erase_count); + memset(admin->cache.maskstream, 0, test_cache_erase_count); + memset(admin->cache.actionstream, 0, test_cache_erase_count); + test_cache_erase_count = 0; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_init(struct net_device *ndev, struct vcap_admin *admin, + u32 start, u32 count) +{ + test_init_start = start; + test_init_count = count; +} + +/* Callback used by the VCAP API */ +static void test_cache_read(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + pr_debug("%s:%d: %d %d\n", __func__, __LINE__, start, count); + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before decoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + admin->cache.counter = test_hw_cache.counter; + admin->cache.sticky = test_hw_cache.sticky; + break; + case VCAP_SEL_ALL: + pr_debug("%s:%d\n", __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_write(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_selection sel, u32 start, u32 count) +{ + u32 *keystr, *mskstr, *actstr; + int idx; + + switch (sel) { + case VCAP_SEL_ENTRY: + keystr = &admin->cache.keystream[start]; + mskstr = &admin->cache.maskstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: keydata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, keystr[idx]); + } + for (idx = 0; idx < count; ++idx) { + /* Invert the mask before encoding starts */ + mskstr[idx] = ~mskstr[idx]; + pr_debug("%s:%d: mskdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, mskstr[idx]); + } + break; + case VCAP_SEL_ACTION: + actstr = &admin->cache.actionstream[start]; + for (idx = 0; idx < count; ++idx) { + pr_debug("%s:%d: actdata[%02d]: 0x%08x\n", __func__, + __LINE__, start + idx, actstr[idx]); + } + break; + case VCAP_SEL_COUNTER: + pr_debug("%s:%d\n", __func__, __LINE__); + test_hw_counter_id = start; + test_hw_cache.counter = admin->cache.counter; + test_hw_cache.sticky = admin->cache.sticky; + break; + case VCAP_SEL_ALL: + pr_err("%s:%d: cannot write all streams at once\n", + __func__, __LINE__); + break; + } +} + +/* Callback used by the VCAP API */ +static void test_cache_update(struct net_device *ndev, struct vcap_admin *admin, + enum vcap_command cmd, + enum vcap_selection sel, u32 addr) +{ + if (test_updateaddridx < ARRAY_SIZE(test_updateaddr)) + test_updateaddr[test_updateaddridx] = addr; + else + pr_err("%s:%d: overflow: %d\n", __func__, __LINE__, test_updateaddridx); + test_updateaddridx++; +} + +static void test_cache_move(struct net_device *ndev, struct vcap_admin *admin, + u32 addr, int offset, int count) +{ + test_move_addr = addr; + test_move_offset = offset; + test_move_count = count; +} + +/* Provide port information via a callback interface */ +static int vcap_test_port_info(struct net_device *ndev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} + +static int vcap_test_enable(struct net_device *ndev, + struct vcap_admin *admin, + bool enable) +{ + return 0; +} + +static struct vcap_operations test_callbacks = { + .validate_keyset = test_val_keyset, + .add_default_fields = test_add_def_fields, + .cache_erase = test_cache_erase, + .cache_write = test_cache_write, + .cache_read = test_cache_read, + .init = test_cache_init, + .update = test_cache_update, + .move = test_cache_move, + .port_info = vcap_test_port_info, + .enable = vcap_test_enable, +}; + +static struct vcap_control test_vctrl = { + .vcaps = kunit_test_vcaps, + .stats = &kunit_test_vcap_stats, + .ops = &test_callbacks, +}; + +static void vcap_test_api_init(struct vcap_admin *admin) +{ + /* Initialize the shared objects */ + INIT_LIST_HEAD(&test_vctrl.list); + INIT_LIST_HEAD(&admin->list); + INIT_LIST_HEAD(&admin->rules); + list_add_tail(&admin->list, &test_vctrl.list); + memset(test_updateaddr, 0, sizeof(test_updateaddr)); + test_updateaddridx = 0; +} + +/* Helper function to create a rule of a specific size */ +static struct vcap_rule * +test_vcap_xn_rule_creator(struct kunit *test, int cid, enum vcap_user user, + u16 priority, + int id, int size, int expected_addr) +{ + struct vcap_rule *rule; + struct vcap_rule_internal *ri; + enum vcap_keyfield_set keyset = VCAP_KFS_NO_VALUE; + enum vcap_actionfield_set actionset = VCAP_AFS_NO_VALUE; + int ret; + + /* init before testing */ + memset(test_updateaddr, 0, sizeof(test_updateaddr)); + test_updateaddridx = 0; + test_move_addr = 0; + test_move_offset = 0; + test_move_count = 0; + + switch (size) { + case 2: + keyset = VCAP_KFS_ETAG; + actionset = VCAP_AFS_CLASS_REDUCED; + break; + case 3: + keyset = VCAP_KFS_PURE_5TUPLE_IP4; + actionset = VCAP_AFS_CLASSIFICATION; + break; + case 6: + keyset = VCAP_KFS_NORMAL_5TUPLE_IP4; + actionset = VCAP_AFS_CLASSIFICATION; + break; + case 12: + keyset = VCAP_KFS_NORMAL_7TUPLE; + actionset = VCAP_AFS_FULL; + break; + default: + break; + } + + /* Check that a valid size was used */ + KUNIT_ASSERT_NE(test, VCAP_KFS_NO_VALUE, keyset); + + /* Allocate the rule */ + rule = vcap_alloc_rule(&test_vctrl, &test_netdev, cid, user, priority, + id); + KUNIT_EXPECT_PTR_NE(test, NULL, rule); + + ri = (struct vcap_rule_internal *)rule; + + /* Override rule keyset */ + ret = vcap_set_rule_set_keyset(rule, keyset); + + /* Add rule actions : there must be at least one action */ + ret = vcap_rule_add_action_u32(rule, VCAP_AF_COSID_VAL, 0); + + /* Override rule actionset */ + ret = vcap_set_rule_set_actionset(rule, actionset); + + ret = vcap_val_rule(rule, ETH_P_ALL); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, keyset, rule->keyset); + KUNIT_EXPECT_EQ(test, actionset, rule->actionset); + KUNIT_EXPECT_EQ(test, size, ri->size); + + /* Add rule with write callback */ + ret = vcap_add_rule(rule); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, expected_addr, ri->addr); + return rule; +} + +/* Prepare testing rule deletion */ +static void test_init_rule_deletion(void) +{ + test_move_addr = 0; + test_move_offset = 0; + test_move_count = 0; + test_init_start = 0; + test_init_count = 0; +} + +/* Define the test cases. */ + +static void vcap_api_set_bit_1_test(struct kunit *test) +{ + struct vcap_stream_iter iter = { + .offset = 35, + .sw_width = 52, + .reg_idx = 1, + .reg_bitpos = 20, + .tg = NULL, + }; + u32 stream[2] = {0}; + + vcap_set_bit(stream, &iter, 1); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)BIT(20), stream[1]); +} + +static void vcap_api_set_bit_0_test(struct kunit *test) +{ + struct vcap_stream_iter iter = { + .offset = 35, + .sw_width = 52, + .reg_idx = 2, + .reg_bitpos = 11, + .tg = NULL, + }; + u32 stream[3] = {~0, ~0, ~0}; + + vcap_set_bit(stream, &iter, 0); + + KUNIT_EXPECT_EQ(test, (u32)~0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)~0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)~BIT(11), stream[2]); +} + +static void vcap_api_iterator_init_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 156, .width = 1, .value = 0, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + struct vcap_typegroup typegroups2[] = { + { .offset = 0, .width = 3, .value = 4, }, + { .offset = 49, .width = 2, .value = 0, }, + { .offset = 98, .width = 2, .value = 0, }, + }; + + vcap_iter_init(&iter, 52, typegroups, 86); + + KUNIT_EXPECT_EQ(test, 52, iter.sw_width); + KUNIT_EXPECT_EQ(test, 86 + 2, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 4, iter.reg_bitpos); + + vcap_iter_init(&iter, 49, typegroups2, 134); + + KUNIT_EXPECT_EQ(test, 49, iter.sw_width); + KUNIT_EXPECT_EQ(test, 134 + 7, iter.offset); + KUNIT_EXPECT_EQ(test, 5, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 11, iter.reg_bitpos); +} + +static void vcap_api_iterator_next_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 0, }, + { .offset = 98, .width = 2, .value = 0, }, + { .offset = 147, .width = 3, .value = 0, }, + { .offset = 196, .width = 2, .value = 0, }, + { .offset = 245, .width = 1, .value = 0, }, + }; + int idx; + + vcap_iter_init(&iter, 49, typegroups, 86); + + KUNIT_EXPECT_EQ(test, 49, iter.sw_width); + KUNIT_EXPECT_EQ(test, 86 + 5, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 10, iter.reg_bitpos); + + vcap_iter_next(&iter); + + KUNIT_EXPECT_EQ(test, 91 + 1, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 11, iter.reg_bitpos); + + for (idx = 0; idx < 6; idx++) + vcap_iter_next(&iter); + + KUNIT_EXPECT_EQ(test, 92 + 6 + 2, iter.offset); + KUNIT_EXPECT_EQ(test, 4, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 2, iter.reg_bitpos); +} + +static void vcap_api_encode_typegroups_test(struct kunit *test) +{ + u32 stream[12] = {0}; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 1, }, + { .offset = 98, .width = 2, .value = 3, }, + { .offset = 147, .width = 3, .value = 5, }, + { .offset = 196, .width = 2, .value = 2, }, + { .offset = 245, .width = 5, .value = 27, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + + vcap_encode_typegroups(stream, 49, typegroups, false); + + KUNIT_EXPECT_EQ(test, (u32)0x8, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x1, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x3, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[5]); + KUNIT_EXPECT_EQ(test, (u32)0x5, stream[6]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[7]); + KUNIT_EXPECT_EQ(test, (u32)0x2, stream[8]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[9]); + KUNIT_EXPECT_EQ(test, (u32)27, stream[10]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[11]); +} + +static void vcap_api_encode_bit_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + u32 stream[4] = {0}; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 1, }, + { .offset = 98, .width = 2, .value = 3, }, + { .offset = 147, .width = 3, .value = 5, }, + { .offset = 196, .width = 2, .value = 2, }, + { .offset = 245, .width = 1, .value = 0, }, + }; + + vcap_iter_init(&iter, 49, typegroups, 44); + + KUNIT_EXPECT_EQ(test, 48, iter.offset); + KUNIT_EXPECT_EQ(test, 1, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 16, iter.reg_bitpos); + + vcap_encode_bit(stream, &iter, 1); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)BIT(16), stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[2]); +} + +static void vcap_api_encode_field_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + u32 stream[16] = {0}; + struct vcap_typegroup typegroups[] = { + { .offset = 0, .width = 4, .value = 8, }, + { .offset = 49, .width = 1, .value = 1, }, + { .offset = 98, .width = 2, .value = 3, }, + { .offset = 147, .width = 3, .value = 5, }, + { .offset = 196, .width = 2, .value = 2, }, + { .offset = 245, .width = 5, .value = 27, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 4, + }; + u8 value[] = {0x5}; + + vcap_iter_init(&iter, 49, typegroups, rf.offset); + + KUNIT_EXPECT_EQ(test, 91, iter.offset); + KUNIT_EXPECT_EQ(test, 3, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 10, iter.reg_bitpos); + + vcap_encode_field(stream, &iter, rf.width, value); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)(0x5 << 10), stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[4]); + + vcap_encode_typegroups(stream, 49, typegroups, false); + + KUNIT_EXPECT_EQ(test, (u32)0x8, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x1, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)(0x5 << 10), stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x3, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[5]); + KUNIT_EXPECT_EQ(test, (u32)0x5, stream[6]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[7]); + KUNIT_EXPECT_EQ(test, (u32)0x2, stream[8]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[9]); + KUNIT_EXPECT_EQ(test, (u32)27, stream[10]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[11]); +} + +/* In this testcase the subword is smaller than a register */ +static void vcap_api_encode_short_field_test(struct kunit *test) +{ + struct vcap_stream_iter iter; + int sw_width = 21; + u32 stream[6] = {0}; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 3, .value = 7, }, + { .offset = 21, .width = 2, .value = 3, }, + { .offset = 42, .width = 1, .value = 1, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 4, + }; + u8 value[] = {0x5}; + + vcap_iter_init(&iter, sw_width, tgt, rf.offset); + + KUNIT_EXPECT_EQ(test, 1, iter.regs_per_sw); + KUNIT_EXPECT_EQ(test, 21, iter.sw_width); + KUNIT_EXPECT_EQ(test, 25 + 3 + 2, iter.offset); + KUNIT_EXPECT_EQ(test, 1, iter.reg_idx); + KUNIT_EXPECT_EQ(test, 25 + 3 + 2 - sw_width, iter.reg_bitpos); + + vcap_encode_field(stream, &iter, rf.width, value); + + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)(0x5 << (25 + 3 + 2 - sw_width)), stream[1]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, stream[5]); + + vcap_encode_typegroups(stream, sw_width, tgt, false); + + KUNIT_EXPECT_EQ(test, (u32)7, stream[0]); + KUNIT_EXPECT_EQ(test, (u32)((0x5 << (25 + 3 + 2 - sw_width)) + 3), stream[1]); + KUNIT_EXPECT_EQ(test, (u32)1, stream[2]); + KUNIT_EXPECT_EQ(test, (u32)0, stream[3]); + KUNIT_EXPECT_EQ(test, (u32)0, stream[4]); + KUNIT_EXPECT_EQ(test, (u32)0, stream[5]); +} + +static void vcap_api_encode_keyfield_test(struct kunit *test) +{ + u32 keywords[16] = {0}; + u32 maskwords[16] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keywords, + .maskstream = maskwords, + .actionstream = keywords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_MAC_ETYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf = { + .ctrl.list = {}, + .ctrl.key = VCAP_KF_ISDX_CLS, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0xeef014a1, + .data.u32.mask = 0xfff, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 156, .width = 1, .value = 1, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + + vcap_test_api_init(&admin); + vcap_encode_keyfield(&rule, &ckf, &rf, tgt); + + /* Key */ + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[1]); + KUNIT_EXPECT_EQ(test, (u32)(0x04a1 << 6), keywords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x0, keywords[6]); + + /* Mask */ + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[1]); + KUNIT_EXPECT_EQ(test, (u32)(0x0fff << 6), maskwords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x0, maskwords[6]); +} + +static void vcap_api_encode_max_keyfield_test(struct kunit *test) +{ + int idx; + u32 keywords[6] = {0}; + u32 maskwords[6] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + /* IS2 sw_width = 52 bit */ + .cache = { + .keystream = keywords, + .maskstream = maskwords, + .actionstream = keywords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_IP_7TUPLE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf = { + .ctrl.list = {}, + .ctrl.key = VCAP_KF_L3_IP6_DIP, + .ctrl.type = VCAP_FIELD_U128, + .data.u128.value = { 0xa1, 0xa2, 0xa3, 0xa4, 0, 0, 0x43, 0, + 0, 0, 0, 0, 0, 0, 0x78, 0x8e, }, + .data.u128.mask = { 0xff, 0xff, 0xff, 0xff, 0, 0, 0xff, 0, + 0, 0, 0, 0, 0, 0, 0xff, 0xff }, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U128, + .offset = 0, + .width = 128, + }; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 156, .width = 1, .value = 1, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + u32 keyres[] = { + 0x928e8a84, + 0x000c0002, + 0x00000010, + 0x00000000, + 0x0239e000, + 0x00000000, + }; + u32 mskres[] = { + 0xfffffffc, + 0x000c0003, + 0x0000003f, + 0x00000000, + 0x03fffc00, + 0x00000000, + }; + + vcap_encode_keyfield(&rule, &ckf, &rf, tgt); + + /* Key */ + for (idx = 0; idx < ARRAY_SIZE(keyres); ++idx) + KUNIT_EXPECT_EQ(test, keyres[idx], keywords[idx]); + /* Mask */ + for (idx = 0; idx < ARRAY_SIZE(mskres); ++idx) + KUNIT_EXPECT_EQ(test, mskres[idx], maskwords[idx]); +} + +static void vcap_api_encode_actionfield_test(struct kunit *test) +{ + u32 actwords[16] = {0}; + int sw_width = 21; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_ES2, /* act_width = 21 */ + .cache = { + .actionstream = actwords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .actionset = VCAP_AFS_BASE_TYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_actionfield caf = { + .ctrl.list = {}, + .ctrl.action = VCAP_AF_POLICE_IDX, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x67908032, + }; + struct vcap_field rf = { + .type = VCAP_FIELD_U32, + .offset = 35, + .width = 6, + }; + struct vcap_typegroup tgt[] = { + { .offset = 0, .width = 2, .value = 2, }, + { .offset = 21, .width = 1, .value = 1, }, + { .offset = 42, .width = 1, .value = 0, }, + { .offset = 0, .width = 0, .value = 0, }, + }; + + vcap_encode_actionfield(&rule, &caf, &rf, tgt); + + /* Action */ + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[0]); + KUNIT_EXPECT_EQ(test, (u32)((0x32 << (35 + 2 + 1 - sw_width)) & 0x1fffff), actwords[1]); + KUNIT_EXPECT_EQ(test, (u32)((0x32 >> ((2 * sw_width) - 38 - 1))), actwords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x0, actwords[6]); +} + +static void vcap_api_keyfield_typegroup_test(struct kunit *test) +{ + const struct vcap_typegroup *tg; + + tg = vcap_keyfield_typegroup(&test_vctrl, VCAP_TYPE_IS2, VCAP_KFS_MAC_ETYPE); + KUNIT_EXPECT_PTR_NE(test, NULL, tg); + KUNIT_EXPECT_EQ(test, 0, tg[0].offset); + KUNIT_EXPECT_EQ(test, 2, tg[0].width); + KUNIT_EXPECT_EQ(test, 2, tg[0].value); + KUNIT_EXPECT_EQ(test, 156, tg[1].offset); + KUNIT_EXPECT_EQ(test, 1, tg[1].width); + KUNIT_EXPECT_EQ(test, 0, tg[1].value); + KUNIT_EXPECT_EQ(test, 0, tg[2].offset); + KUNIT_EXPECT_EQ(test, 0, tg[2].width); + KUNIT_EXPECT_EQ(test, 0, tg[2].value); + + tg = vcap_keyfield_typegroup(&test_vctrl, VCAP_TYPE_ES2, VCAP_KFS_LL_FULL); + KUNIT_EXPECT_PTR_EQ(test, NULL, tg); +} + +static void vcap_api_actionfield_typegroup_test(struct kunit *test) +{ + const struct vcap_typegroup *tg; + + tg = vcap_actionfield_typegroup(&test_vctrl, VCAP_TYPE_IS0, VCAP_AFS_FULL); + KUNIT_EXPECT_PTR_NE(test, NULL, tg); + KUNIT_EXPECT_EQ(test, 0, tg[0].offset); + KUNIT_EXPECT_EQ(test, 3, tg[0].width); + KUNIT_EXPECT_EQ(test, 4, tg[0].value); + KUNIT_EXPECT_EQ(test, 110, tg[1].offset); + KUNIT_EXPECT_EQ(test, 2, tg[1].width); + KUNIT_EXPECT_EQ(test, 0, tg[1].value); + KUNIT_EXPECT_EQ(test, 220, tg[2].offset); + KUNIT_EXPECT_EQ(test, 2, tg[2].width); + KUNIT_EXPECT_EQ(test, 0, tg[2].value); + KUNIT_EXPECT_EQ(test, 0, tg[3].offset); + KUNIT_EXPECT_EQ(test, 0, tg[3].width); + KUNIT_EXPECT_EQ(test, 0, tg[3].value); + + tg = vcap_actionfield_typegroup(&test_vctrl, VCAP_TYPE_IS2, VCAP_AFS_CLASSIFICATION); + KUNIT_EXPECT_PTR_EQ(test, NULL, tg); +} + +static void vcap_api_vcap_keyfields_test(struct kunit *test) +{ + const struct vcap_field *ft; + + ft = vcap_keyfields(&test_vctrl, VCAP_TYPE_IS2, VCAP_KFS_MAC_ETYPE); + KUNIT_EXPECT_PTR_NE(test, NULL, ft); + + /* Keyset that is not available and within the maximum keyset enum value */ + ft = vcap_keyfields(&test_vctrl, VCAP_TYPE_ES2, VCAP_KFS_PURE_5TUPLE_IP4); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); + + /* Keyset that is not available and beyond the maximum keyset enum value */ + ft = vcap_keyfields(&test_vctrl, VCAP_TYPE_ES2, VCAP_KFS_LL_FULL); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); +} + +static void vcap_api_vcap_actionfields_test(struct kunit *test) +{ + const struct vcap_field *ft; + + ft = vcap_actionfields(&test_vctrl, VCAP_TYPE_IS0, VCAP_AFS_FULL); + KUNIT_EXPECT_PTR_NE(test, NULL, ft); + + ft = vcap_actionfields(&test_vctrl, VCAP_TYPE_IS2, VCAP_AFS_FULL); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); + + ft = vcap_actionfields(&test_vctrl, VCAP_TYPE_IS2, VCAP_AFS_CLASSIFICATION); + KUNIT_EXPECT_PTR_EQ(test, NULL, ft); +} + +static void vcap_api_encode_rule_keyset_test(struct kunit *test) +{ + u32 keywords[16] = {0}; + u32 maskwords[16] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .keystream = keywords, + .maskstream = maskwords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_MAC_ETYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x00, + .data.u32.mask = 0x0f, + }, + { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + .ctrl.type = VCAP_FIELD_BIT, + .data.u1.value = 0x01, + .data.u1.mask = 0x01, + }, + { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_L3, + .ctrl.type = VCAP_FIELD_BIT, + .data.u1.value = 0x00, + .data.u1.mask = 0x01, + }, + { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_RNG, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x00, + .data.u32.mask = 0x0f, + }, + { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK, + .ctrl.type = VCAP_FIELD_U72, + .data.u72.value = {0x0, 0x00, 0x00, 0x00}, + .data.u72.mask = {0xfd, 0xff, 0xff, 0xff}, + }, + { + .ctrl.key = VCAP_KF_L2_DMAC, + .ctrl.type = VCAP_FIELD_U48, + /* Opposite endianness */ + .data.u48.value = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, + .data.u48.mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + }, + { + .ctrl.key = VCAP_KF_ETYPE_LEN_IS, + .ctrl.type = VCAP_FIELD_BIT, + .data.u1.value = 0x01, + .data.u1.mask = 0x01, + }, + { + .ctrl.key = VCAP_KF_ETYPE, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0xaabb, + .data.u32.mask = 0xffff, + }, + }; + int idx; + int ret; + + /* Empty entry list */ + INIT_LIST_HEAD(&rule.data.keyfields); + ret = vcap_encode_rule_keyset(&rule); + KUNIT_EXPECT_EQ(test, -EINVAL, ret); + + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &rule.data.keyfields); + ret = vcap_encode_rule_keyset(&rule); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* The key and mask values below are from an actual Sparx5 rule config */ + /* Key */ + KUNIT_EXPECT_EQ(test, (u32)0x00000042, keywords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[1]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x00020100, keywords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x60504030, keywords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[6]); + KUNIT_EXPECT_EQ(test, (u32)0x0002aaee, keywords[7]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[8]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[9]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[10]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, keywords[11]); + + /* Mask: they will be inverted when applied to the register */ + KUNIT_EXPECT_EQ(test, (u32)~0x00b07f80, maskwords[0]); + KUNIT_EXPECT_EQ(test, (u32)~0xfff00000, maskwords[1]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffffffc, maskwords[2]); + KUNIT_EXPECT_EQ(test, (u32)~0xfff000ff, maskwords[3]); + KUNIT_EXPECT_EQ(test, (u32)~0x00000000, maskwords[4]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffffff0, maskwords[5]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffffffe, maskwords[6]); + KUNIT_EXPECT_EQ(test, (u32)~0xfffc0001, maskwords[7]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[8]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[9]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[10]); + KUNIT_EXPECT_EQ(test, (u32)~0xffffffff, maskwords[11]); +} + +static void vcap_api_encode_rule_actionset_test(struct kunit *test) +{ + u32 actwords[16] = {0}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + .cache = { + .actionstream = actwords, + }, + }; + struct vcap_rule_internal rule = { + .admin = &admin, + .data = { + .actionset = VCAP_AFS_BASE_TYPE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_client_actionfield caf[] = { + { + .ctrl.action = VCAP_AF_MATCH_ID, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x01, + }, + { + .ctrl.action = VCAP_AF_MATCH_ID_MASK, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x01, + }, + { + .ctrl.action = VCAP_AF_CNT_ID, + .ctrl.type = VCAP_FIELD_U32, + .data.u32.value = 0x64, + }, + }; + int idx; + int ret; + + /* Empty entry list */ + INIT_LIST_HEAD(&rule.data.actionfields); + ret = vcap_encode_rule_actionset(&rule); + /* We allow rules with no actions */ + KUNIT_EXPECT_EQ(test, 0, ret); + + for (idx = 0; idx < ARRAY_SIZE(caf); idx++) + list_add_tail(&caf[idx].ctrl.list, &rule.data.actionfields); + ret = vcap_encode_rule_actionset(&rule); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* The action values below are from an actual Sparx5 rule config */ + KUNIT_EXPECT_EQ(test, (u32)0x00000002, actwords[0]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[1]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[2]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[3]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[4]); + KUNIT_EXPECT_EQ(test, (u32)0x00100000, actwords[5]); + KUNIT_EXPECT_EQ(test, (u32)0x06400010, actwords[6]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[7]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[8]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[9]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[10]); + KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[11]); +} + +static void vcap_api_rule_add_keyvalue_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .data = { + .keyset = VCAP_KFS_NO_VALUE, + }, + .vctrl = &test_vctrl, + }; + struct vcap_rule *rule = (struct vcap_rule *)&ri; + struct vcap_client_keyfield *kf; + int ret; + struct vcap_u128_key dip = { + .value = {0x17, 0x26, 0x35, 0x44, 0x63, 0x62, 0x71}, + .mask = {0xf1, 0xf2, 0xf3, 0xf4, 0x4f, 0x3f, 0x2f, 0x1f}, + }; + int idx; + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_0); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value); + KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.value); + KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, + VCAP_BIT_ANY); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_LOOKUP_FIRST_IS, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value); + KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_u32(rule, VCAP_KF_TYPE, 0x98765432, 0xff00ffab); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_TYPE, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, kf->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x98765432, kf->data.u32.value); + KUNIT_EXPECT_EQ(test, 0xff00ffab, kf->data.u32.mask); + + INIT_LIST_HEAD(&rule->keyfields); + ret = vcap_rule_add_key_u128(rule, VCAP_KF_L3_IP6_SIP, &dip); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, false, ret); + kf = list_first_entry(&rule->keyfields, struct vcap_client_keyfield, + ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_KF_L3_IP6_SIP, kf->ctrl.key); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U128, kf->ctrl.type); + for (idx = 0; idx < ARRAY_SIZE(dip.value); ++idx) + KUNIT_EXPECT_EQ(test, dip.value[idx], kf->data.u128.value[idx]); + for (idx = 0; idx < ARRAY_SIZE(dip.mask); ++idx) + KUNIT_EXPECT_EQ(test, dip.mask[idx], kf->data.u128.mask[idx]); +} + +static void vcap_api_rule_add_actionvalue_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .data = { + .actionset = VCAP_AFS_NO_VALUE, + }, + }; + struct vcap_rule *rule = (struct vcap_rule *)&ri; + struct vcap_client_actionfield *af; + int ret; + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_0); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x1, af->data.u1.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_ANY); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_TYPE, 0x98765432); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_TYPE, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0x98765432, af->data.u32.value); + + INIT_LIST_HEAD(&rule->actionfields); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_MASK_MODE, 0xaabbccdd); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, false, ret); + af = list_first_entry(&rule->actionfields, + struct vcap_client_actionfield, ctrl.list); + KUNIT_EXPECT_EQ(test, VCAP_AF_MASK_MODE, af->ctrl.action); + KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type); + KUNIT_EXPECT_EQ(test, 0xaabbccdd, af->data.u32.value); +} + +static void vcap_api_rule_find_keyset_basic_test(struct kunit *test) +{ + struct vcap_keyset_list matches = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + }, { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + }, { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_L3, + }, { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK_RNG, + }, { + .ctrl.key = VCAP_KF_IF_IGR_PORT_MASK, + }, { + .ctrl.key = VCAP_KF_L2_DMAC, + }, { + .ctrl.key = VCAP_KF_ETYPE_LEN_IS, + }, { + .ctrl.key = VCAP_KF_ETYPE, + }, + }; + int idx; + bool ret; + enum vcap_keyfield_set keysets[10] = {}; + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); + + ret = vcap_rule_find_keysets(&ri.data, &matches); + + KUNIT_EXPECT_EQ(test, true, ret); + KUNIT_EXPECT_EQ(test, 1, matches.cnt); + KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, matches.keysets[0]); +} + +static void vcap_api_rule_find_keyset_failed_test(struct kunit *test) +{ + struct vcap_keyset_list matches = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + }, { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + }, { + .ctrl.key = VCAP_KF_ARP_OPCODE, + }, { + .ctrl.key = VCAP_KF_L3_IP4_SIP, + }, { + .ctrl.key = VCAP_KF_L3_IP4_DIP, + }, { + .ctrl.key = VCAP_KF_8021Q_PCP_CLS, + }, { + .ctrl.key = VCAP_KF_ETYPE_LEN_IS, /* Not with ARP */ + }, { + .ctrl.key = VCAP_KF_ETYPE, /* Not with ARP */ + }, + }; + int idx; + bool ret; + enum vcap_keyfield_set keysets[10] = {}; + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); + + ret = vcap_rule_find_keysets(&ri.data, &matches); + + KUNIT_EXPECT_EQ(test, false, ret); + KUNIT_EXPECT_EQ(test, 0, matches.cnt); + KUNIT_EXPECT_EQ(test, VCAP_KFS_NO_VALUE, matches.keysets[0]); +} + +static void vcap_api_rule_find_keyset_many_test(struct kunit *test) +{ + struct vcap_keyset_list matches = {}; + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + }; + struct vcap_client_keyfield ckf[] = { + { + .ctrl.key = VCAP_KF_TYPE, + }, { + .ctrl.key = VCAP_KF_LOOKUP_FIRST_IS, + }, { + .ctrl.key = VCAP_KF_8021Q_DEI_CLS, + }, { + .ctrl.key = VCAP_KF_8021Q_PCP_CLS, + }, { + .ctrl.key = VCAP_KF_8021Q_VID_CLS, + }, { + .ctrl.key = VCAP_KF_ISDX_CLS, + }, { + .ctrl.key = VCAP_KF_L2_MC_IS, + }, { + .ctrl.key = VCAP_KF_L2_BC_IS, + }, + }; + int idx; + bool ret; + enum vcap_keyfield_set keysets[10] = {}; + + matches.keysets = keysets; + matches.max = ARRAY_SIZE(keysets); + + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(ckf); idx++) + list_add_tail(&ckf[idx].ctrl.list, &ri.data.keyfields); + + ret = vcap_rule_find_keysets(&ri.data, &matches); + + KUNIT_EXPECT_EQ(test, true, ret); + KUNIT_EXPECT_EQ(test, 6, matches.cnt); + KUNIT_EXPECT_EQ(test, VCAP_KFS_ARP, matches.keysets[0]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP4_OTHER, matches.keysets[1]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP4_TCP_UDP, matches.keysets[2]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP6_STD, matches.keysets[3]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_IP_7TUPLE, matches.keysets[4]); + KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, matches.keysets[5]); +} + +static void vcap_api_encode_rule_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin is2_admin = { + .vtype = VCAP_TYPE_IS2, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + struct vcap_rule *rule; + struct vcap_rule_internal *ri; + int vcap_chain_id = 10005; + enum vcap_user user = VCAP_USER_VCAP_UTIL; + u16 priority = 10; + int id = 100; + int ret; + struct vcap_u48_key smac = { + .value = { 0x88, 0x75, 0x32, 0x34, 0x9e, 0xb1 }, + .mask = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } + }; + struct vcap_u48_key dmac = { + .value = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 }, + .mask = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } + }; + u32 port_mask_rng_value = 0x05; + u32 port_mask_rng_mask = 0x0f; + u32 igr_port_mask_value = 0xffabcd01; + u32 igr_port_mask_mask = ~0; + /* counter is not written yet, so it is not in expwriteaddr */ + u32 expwriteaddr[] = {792, 793, 794, 795, 796, 797, 0}; + int idx; + + vcap_test_api_init(&is2_admin); + + /* Allocate the rule */ + rule = vcap_alloc_rule(&test_vctrl, &test_netdev, vcap_chain_id, user, + priority, id); + KUNIT_EXPECT_PTR_NE(test, NULL, rule); + ri = (struct vcap_rule_internal *)rule; + + /* Add rule keys */ + ret = vcap_rule_add_key_u48(rule, VCAP_KF_L2_DMAC, &dmac); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_u48(rule, VCAP_KF_L2_SMAC, &smac); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_ETYPE_LEN_IS, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + /* Cannot add the same field twice */ + ret = vcap_rule_add_key_bit(rule, VCAP_KF_ETYPE_LEN_IS, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, -EINVAL, ret); + ret = vcap_rule_add_key_bit(rule, VCAP_KF_IF_IGR_PORT_MASK_L3, + VCAP_BIT_ANY); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK_RNG, + port_mask_rng_value, port_mask_rng_mask); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_key_u32(rule, VCAP_KF_IF_IGR_PORT_MASK, + igr_port_mask_value, igr_port_mask_mask); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Add rule actions */ + ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_CNT_ID, id); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_MATCH_ID, 1); + KUNIT_EXPECT_EQ(test, 0, ret); + ret = vcap_rule_add_action_u32(rule, VCAP_AF_MATCH_ID_MASK, 1); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* For now the actionset is hardcoded */ + ret = vcap_set_rule_set_actionset(rule, VCAP_AFS_BASE_TYPE); + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Validation with validate keyset callback */ + ret = vcap_val_rule(rule, ETH_P_ALL); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, VCAP_KFS_MAC_ETYPE, rule->keyset); + KUNIT_EXPECT_EQ(test, VCAP_AFS_BASE_TYPE, rule->actionset); + KUNIT_EXPECT_EQ(test, 6, ri->size); + KUNIT_EXPECT_EQ(test, 2, ri->keyset_sw_regs); + KUNIT_EXPECT_EQ(test, 4, ri->actionset_sw_regs); + + /* Add rule with write callback */ + ret = vcap_add_rule(rule); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 792, is2_admin.last_used_addr); + for (idx = 0; idx < ARRAY_SIZE(expwriteaddr); ++idx) + KUNIT_EXPECT_EQ(test, expwriteaddr[idx], test_updateaddr[idx]); + + /* Check that the rule has been added */ + ret = list_empty(&is2_admin.rules); + KUNIT_EXPECT_EQ(test, false, ret); + KUNIT_EXPECT_EQ(test, 0, ret); + vcap_free_rule(rule); + + /* Check that the rule has been freed: tricky to access since this + * memory should not be accessible anymore + */ + KUNIT_EXPECT_PTR_NE(test, NULL, rule); + ret = list_empty(&rule->keyfields); + KUNIT_EXPECT_EQ(test, true, ret); + ret = list_empty(&rule->actionfields); + KUNIT_EXPECT_EQ(test, true, ret); +} + +static void vcap_api_set_rule_counter_test(struct kunit *test) +{ + struct vcap_admin is2_admin = { + .cache = { + .counter = 100, + .sticky = true, + }, + }; + struct vcap_rule_internal ri = { + .data = { + .id = 1001, + }, + .addr = 600, + .admin = &is2_admin, + .counter_id = 1002, + .vctrl = &test_vctrl, + }; + struct vcap_rule_internal ri2 = { + .data = { + .id = 2001, + }, + .addr = 700, + .admin = &is2_admin, + .counter_id = 2002, + .vctrl = &test_vctrl, + }; + struct vcap_counter ctr = { .value = 0, .sticky = false}; + struct vcap_counter ctr2 = { .value = 101, .sticky = true}; + int ret; + + vcap_test_api_init(&is2_admin); + list_add_tail(&ri.list, &is2_admin.rules); + list_add_tail(&ri2.list, &is2_admin.rules); + + pr_info("%s:%d\n", __func__, __LINE__); + ret = vcap_rule_set_counter(&ri.data, &ctr); + pr_info("%s:%d\n", __func__, __LINE__); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 1002, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 0, test_hw_cache.counter); + KUNIT_EXPECT_EQ(test, false, test_hw_cache.sticky); + KUNIT_EXPECT_EQ(test, 600, test_updateaddr[0]); + + ret = vcap_rule_set_counter(&ri2.data, &ctr2); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 2002, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 101, test_hw_cache.counter); + KUNIT_EXPECT_EQ(test, true, test_hw_cache.sticky); + KUNIT_EXPECT_EQ(test, 700, test_updateaddr[1]); +} + +static void vcap_api_get_rule_counter_test(struct kunit *test) +{ + struct vcap_admin is2_admin = { + .cache = { + .counter = 100, + .sticky = true, + }, + }; + struct vcap_rule_internal ri = { + .data = { + .id = 1010, + }, + .addr = 400, + .admin = &is2_admin, + .counter_id = 1011, + .vctrl = &test_vctrl, + }; + struct vcap_rule_internal ri2 = { + .data = { + .id = 2011, + }, + .addr = 300, + .admin = &is2_admin, + .counter_id = 2012, + .vctrl = &test_vctrl, + }; + struct vcap_counter ctr = {}; + struct vcap_counter ctr2 = {}; + int ret; + + vcap_test_api_init(&is2_admin); + test_hw_cache.counter = 55; + test_hw_cache.sticky = true; + + list_add_tail(&ri.list, &is2_admin.rules); + list_add_tail(&ri2.list, &is2_admin.rules); + + ret = vcap_rule_get_counter(&ri.data, &ctr); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 1011, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 55, ctr.value); + KUNIT_EXPECT_EQ(test, true, ctr.sticky); + KUNIT_EXPECT_EQ(test, 400, test_updateaddr[0]); + + test_hw_cache.counter = 22; + test_hw_cache.sticky = false; + + ret = vcap_rule_get_counter(&ri2.data, &ctr2); + KUNIT_EXPECT_EQ(test, 0, ret); + + KUNIT_EXPECT_EQ(test, 2012, test_hw_counter_id); + KUNIT_EXPECT_EQ(test, 22, ctr2.value); + KUNIT_EXPECT_EQ(test, false, ctr2.sticky); + KUNIT_EXPECT_EQ(test, 300, test_updateaddr[1]); +} + +static void vcap_api_rule_insert_in_order_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + + vcap_test_api_init(&admin); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768); +} + +static void vcap_api_rule_insert_reverse_order_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + struct vcap_rule_internal *elem; + u32 exp_addr[] = {780, 774, 771, 768, 767}; + int idx; + + vcap_test_api_init(&admin); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 200, 2, 798); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 795); + KUNIT_EXPECT_EQ(test, 6, test_move_offset); + KUNIT_EXPECT_EQ(test, 3, test_move_count); + KUNIT_EXPECT_EQ(test, 798, test_move_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 400, 6, 792); + KUNIT_EXPECT_EQ(test, 6, test_move_offset); + KUNIT_EXPECT_EQ(test, 6, test_move_count); + KUNIT_EXPECT_EQ(test, 792, test_move_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 50, 500, 12, 780); + KUNIT_EXPECT_EQ(test, 18, test_move_offset); + KUNIT_EXPECT_EQ(test, 12, test_move_count); + KUNIT_EXPECT_EQ(test, 786, test_move_addr); + + idx = 0; + list_for_each_entry(elem, &admin.rules, list) { + KUNIT_EXPECT_EQ(test, exp_addr[idx], elem->addr); + ++idx; + } + KUNIT_EXPECT_EQ(test, 768, admin.last_used_addr); +} + +static void vcap_api_rule_remove_at_end_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .last_valid_addr = 3071, + .first_valid_addr = 0, + .last_used_addr = 800, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + int ret; + + vcap_test_api_init(&admin); + test_init_rule_deletion(); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768); + + /* Remove rules again from the end */ + ret = vcap_del_rule(&test_vctrl, &test_netdev, 200); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 768, test_init_start); + KUNIT_EXPECT_EQ(test, 2, test_init_count); + KUNIT_EXPECT_EQ(test, 771, admin.last_used_addr); + + ret = vcap_del_rule(&test_vctrl, &test_netdev, 300); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 771, test_init_start); + KUNIT_EXPECT_EQ(test, 3, test_init_count); + KUNIT_EXPECT_EQ(test, 774, admin.last_used_addr); + + ret = vcap_del_rule(&test_vctrl, &test_netdev, 400); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 774, test_init_start); + KUNIT_EXPECT_EQ(test, 6, test_init_count); + KUNIT_EXPECT_EQ(test, 780, admin.last_used_addr); + + ret = vcap_del_rule(&test_vctrl, &test_netdev, 500); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 780, test_init_start); + KUNIT_EXPECT_EQ(test, 12, test_init_count); + KUNIT_EXPECT_EQ(test, 3072, admin.last_used_addr); +} + +static void vcap_api_rule_remove_in_middle_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .first_valid_addr = 0, + .last_used_addr = 800, + .last_valid_addr = 800 - 1, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + int ret; + + vcap_test_api_init(&admin); + + /* Create rules with different sizes and check that they are placed + * at the correct address in the VCAP according to size + */ + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768); + + /* Remove rules in the middle */ + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 400); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 768, test_move_addr); + KUNIT_EXPECT_EQ(test, -6, test_move_offset); + KUNIT_EXPECT_EQ(test, 6, test_move_count); + KUNIT_EXPECT_EQ(test, 768, test_init_start); + KUNIT_EXPECT_EQ(test, 6, test_init_count); + KUNIT_EXPECT_EQ(test, 774, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 300); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 774, test_move_addr); + KUNIT_EXPECT_EQ(test, -4, test_move_offset); + KUNIT_EXPECT_EQ(test, 2, test_move_count); + KUNIT_EXPECT_EQ(test, 774, test_init_start); + KUNIT_EXPECT_EQ(test, 4, test_init_count); + KUNIT_EXPECT_EQ(test, 778, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 500); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 778, test_move_addr); + KUNIT_EXPECT_EQ(test, -20, test_move_offset); + KUNIT_EXPECT_EQ(test, 2, test_move_count); + KUNIT_EXPECT_EQ(test, 778, test_init_start); + KUNIT_EXPECT_EQ(test, 20, test_init_count); + KUNIT_EXPECT_EQ(test, 798, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 200); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 798, test_init_start); + KUNIT_EXPECT_EQ(test, 2, test_init_count); + KUNIT_EXPECT_EQ(test, 800, admin.last_used_addr); +} + +static void vcap_api_rule_remove_in_front_test(struct kunit *test) +{ + /* Data used by VCAP Library callback */ + static u32 keydata[32] = {}; + static u32 mskdata[32] = {}; + static u32 actdata[32] = {}; + + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + .first_cid = 10000, + .last_cid = 19999, + .lookups = 4, + .first_valid_addr = 0, + .last_used_addr = 800, + .last_valid_addr = 800 - 1, + .cache = { + .keystream = keydata, + .maskstream = mskdata, + .actionstream = actdata, + }, + }; + int ret; + + vcap_test_api_init(&admin); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 10, 500, 12, 780); + KUNIT_EXPECT_EQ(test, 780, admin.last_used_addr); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 500); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 0, test_move_addr); + KUNIT_EXPECT_EQ(test, 0, test_move_offset); + KUNIT_EXPECT_EQ(test, 0, test_move_count); + KUNIT_EXPECT_EQ(test, 780, test_init_start); + KUNIT_EXPECT_EQ(test, 12, test_init_count); + KUNIT_EXPECT_EQ(test, 800, admin.last_used_addr); + + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 792); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 789); + test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 786); + + test_init_rule_deletion(); + ret = vcap_del_rule(&test_vctrl, &test_netdev, 400); + KUNIT_EXPECT_EQ(test, 0, ret); + KUNIT_EXPECT_EQ(test, 786, test_move_addr); + KUNIT_EXPECT_EQ(test, -8, test_move_offset); + KUNIT_EXPECT_EQ(test, 6, test_move_count); + KUNIT_EXPECT_EQ(test, 786, test_init_start); + KUNIT_EXPECT_EQ(test, 8, test_init_count); + KUNIT_EXPECT_EQ(test, 794, admin.last_used_addr); +} + +static struct kunit_case vcap_api_rule_remove_test_cases[] = { + KUNIT_CASE(vcap_api_rule_remove_at_end_test), + KUNIT_CASE(vcap_api_rule_remove_in_middle_test), + KUNIT_CASE(vcap_api_rule_remove_in_front_test), + {} +}; + +static void vcap_api_next_lookup_basic_test(struct kunit *test) +{ + struct vcap_admin admin1 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 0, + .first_cid = 8000000, + .last_cid = 8199999, + .lookups = 4, + .lookups_per_instance = 2, + }; + struct vcap_admin admin2 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 1, + .first_cid = 8200000, + .last_cid = 8399999, + .lookups = 4, + .lookups_per_instance = 2, + }; + bool ret; + + vcap_test_api_init(&admin1); + list_add_tail(&admin2.list, &test_vctrl.list); + + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 1001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8101000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8100000, 8101000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8100000, 8201000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8200000, 8201000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8200000, 8301000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8301000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8401000); + KUNIT_EXPECT_EQ(test, true, ret); +} + +static void vcap_api_next_lookup_advanced_test(struct kunit *test) +{ + struct vcap_admin admin1 = { + .vtype = VCAP_TYPE_IS0, + .vinst = 0, + .first_cid = 1000000, + .last_cid = 1199999, + .lookups = 6, + .lookups_per_instance = 2, + }; + struct vcap_admin admin2 = { + .vtype = VCAP_TYPE_IS0, + .vinst = 1, + .first_cid = 1200000, + .last_cid = 1399999, + .lookups = 6, + .lookups_per_instance = 2, + }; + struct vcap_admin admin3 = { + .vtype = VCAP_TYPE_IS0, + .vinst = 2, + .first_cid = 1400000, + .last_cid = 1599999, + .lookups = 6, + .lookups_per_instance = 2, + }; + struct vcap_admin admin4 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 0, + .first_cid = 8000000, + .last_cid = 8199999, + .lookups = 4, + .lookups_per_instance = 2, + }; + struct vcap_admin admin5 = { + .vtype = VCAP_TYPE_IS2, + .vinst = 1, + .first_cid = 8200000, + .last_cid = 8399999, + .lookups = 4, + .lookups_per_instance = 2, + }; + bool ret; + + vcap_test_api_init(&admin1); + list_add_tail(&admin2.list, &test_vctrl.list); + list_add_tail(&admin3.list, &test_vctrl.list); + list_add_tail(&admin4.list, &test_vctrl.list); + list_add_tail(&admin5.list, &test_vctrl.list); + + ret = vcap_is_next_lookup(&test_vctrl, 1000000, 1001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1000000, 1101000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 1100000, 1201000); + KUNIT_EXPECT_EQ(test, true, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1100000, 1301000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1100000, 8101000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1300000, 1401000); + KUNIT_EXPECT_EQ(test, true, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1400000, 1501000); + KUNIT_EXPECT_EQ(test, true, ret); + ret = vcap_is_next_lookup(&test_vctrl, 1500000, 8001000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8001000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8000000, 8101000); + KUNIT_EXPECT_EQ(test, true, ret); + + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8301000); + KUNIT_EXPECT_EQ(test, false, ret); + ret = vcap_is_next_lookup(&test_vctrl, 8300000, 8401000); + KUNIT_EXPECT_EQ(test, true, ret); +} + +static void vcap_api_filter_unsupported_keys_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS2, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + .data.keyset = VCAP_KFS_MAC_ETYPE, + }; + enum vcap_key_field keylist[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_ARP_ADDR_SPACE_OK_IS, /* arp keys are not in keyset */ + VCAP_KF_ARP_PROTO_SPACE_OK_IS, + VCAP_KF_ARP_LEN_OK_IS, + VCAP_KF_ARP_TGT_MATCH_IS, + VCAP_KF_ARP_SENDER_MATCH_IS, + VCAP_KF_ARP_OPCODE_UNKNOWN_IS, + VCAP_KF_ARP_OPCODE, + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + }; + enum vcap_key_field expected[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_8021Q_DEI_CLS, + VCAP_KF_8021Q_PCP_CLS, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + }; + struct vcap_client_keyfield *ckf, *next; + bool ret; + int idx; + + /* Add all keys to the rule */ + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(keylist); idx++) { + ckf = kzalloc(sizeof(*ckf), GFP_KERNEL); + if (ckf) { + ckf->ctrl.key = keylist[idx]; + list_add_tail(&ckf->ctrl.list, &ri.data.keyfields); + } + } + + KUNIT_EXPECT_EQ(test, 14, ARRAY_SIZE(keylist)); + + /* Drop unsupported keys from the rule */ + ret = vcap_filter_rule_keys(&ri.data, NULL, 0, true); + + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Check remaining keys in the rule */ + idx = 0; + list_for_each_entry_safe(ckf, next, &ri.data.keyfields, ctrl.list) { + KUNIT_EXPECT_EQ(test, expected[idx], ckf->ctrl.key); + list_del(&ckf->ctrl.list); + kfree(ckf); + ++idx; + } + KUNIT_EXPECT_EQ(test, 7, idx); +} + +static void vcap_api_filter_keylist_test(struct kunit *test) +{ + struct vcap_admin admin = { + .vtype = VCAP_TYPE_IS0, + }; + struct vcap_rule_internal ri = { + .admin = &admin, + .vctrl = &test_vctrl, + .data.keyset = VCAP_KFS_NORMAL_7TUPLE, + }; + enum vcap_key_field keylist[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_GEN_IDX_SEL, + VCAP_KF_LOOKUP_GEN_IDX, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_8021Q_VLAN_TAGS, + VCAP_KF_8021Q_TPID0, + VCAP_KF_8021Q_PCP0, + VCAP_KF_8021Q_DEI0, + VCAP_KF_8021Q_VID0, + VCAP_KF_8021Q_TPID1, + VCAP_KF_8021Q_PCP1, + VCAP_KF_8021Q_DEI1, + VCAP_KF_8021Q_VID1, + VCAP_KF_8021Q_TPID2, + VCAP_KF_8021Q_PCP2, + VCAP_KF_8021Q_DEI2, + VCAP_KF_8021Q_VID2, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_SMAC, + VCAP_KF_IP_MC_IS, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_ETYPE, + VCAP_KF_IP_SNAP_IS, + VCAP_KF_IP4_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_DSCP, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TCP_IS, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_RNG, + }; + enum vcap_key_field droplist[] = { + VCAP_KF_8021Q_TPID1, + VCAP_KF_8021Q_PCP1, + VCAP_KF_8021Q_DEI1, + VCAP_KF_8021Q_VID1, + VCAP_KF_8021Q_TPID2, + VCAP_KF_8021Q_PCP2, + VCAP_KF_8021Q_DEI2, + VCAP_KF_8021Q_VID2, + VCAP_KF_L3_IP6_DIP, + VCAP_KF_L3_IP6_SIP, + VCAP_KF_L4_SPORT, + VCAP_KF_L4_RNG, + }; + enum vcap_key_field expected[] = { + VCAP_KF_TYPE, + VCAP_KF_LOOKUP_FIRST_IS, + VCAP_KF_LOOKUP_GEN_IDX_SEL, + VCAP_KF_LOOKUP_GEN_IDX, + VCAP_KF_IF_IGR_PORT_MASK_SEL, + VCAP_KF_IF_IGR_PORT_MASK, + VCAP_KF_L2_MC_IS, + VCAP_KF_L2_BC_IS, + VCAP_KF_8021Q_VLAN_TAGS, + VCAP_KF_8021Q_TPID0, + VCAP_KF_8021Q_PCP0, + VCAP_KF_8021Q_DEI0, + VCAP_KF_8021Q_VID0, + VCAP_KF_L2_DMAC, + VCAP_KF_L2_SMAC, + VCAP_KF_IP_MC_IS, + VCAP_KF_ETYPE_LEN_IS, + VCAP_KF_ETYPE, + VCAP_KF_IP_SNAP_IS, + VCAP_KF_IP4_IS, + VCAP_KF_L3_FRAGMENT_TYPE, + VCAP_KF_L3_FRAG_INVLD_L4_LEN, + VCAP_KF_L3_OPTIONS_IS, + VCAP_KF_L3_DSCP, + VCAP_KF_TCP_UDP_IS, + VCAP_KF_TCP_IS, + }; + struct vcap_client_keyfield *ckf, *next; + bool ret; + int idx; + + /* Add all keys to the rule */ + INIT_LIST_HEAD(&ri.data.keyfields); + for (idx = 0; idx < ARRAY_SIZE(keylist); idx++) { + ckf = kzalloc(sizeof(*ckf), GFP_KERNEL); + if (ckf) { + ckf->ctrl.key = keylist[idx]; + list_add_tail(&ckf->ctrl.list, &ri.data.keyfields); + } + } + + KUNIT_EXPECT_EQ(test, 38, ARRAY_SIZE(keylist)); + + /* Drop listed keys from the rule */ + ret = vcap_filter_rule_keys(&ri.data, droplist, ARRAY_SIZE(droplist), + false); + + KUNIT_EXPECT_EQ(test, 0, ret); + + /* Check remaining keys in the rule */ + idx = 0; + list_for_each_entry_safe(ckf, next, &ri.data.keyfields, ctrl.list) { + KUNIT_EXPECT_EQ(test, expected[idx], ckf->ctrl.key); + list_del(&ckf->ctrl.list); + kfree(ckf); + ++idx; + } + KUNIT_EXPECT_EQ(test, 26, idx); +} + +static struct kunit_suite vcap_api_rule_remove_test_suite = { + .name = "VCAP_API_Rule_Remove_Testsuite", + .test_cases = vcap_api_rule_remove_test_cases, +}; + +static struct kunit_case vcap_api_rule_insert_test_cases[] = { + KUNIT_CASE(vcap_api_rule_insert_in_order_test), + KUNIT_CASE(vcap_api_rule_insert_reverse_order_test), + {} +}; + +static struct kunit_suite vcap_api_rule_insert_test_suite = { + .name = "VCAP_API_Rule_Insert_Testsuite", + .test_cases = vcap_api_rule_insert_test_cases, +}; + +static struct kunit_case vcap_api_rule_counter_test_cases[] = { + KUNIT_CASE(vcap_api_set_rule_counter_test), + KUNIT_CASE(vcap_api_get_rule_counter_test), + {} +}; + +static struct kunit_suite vcap_api_rule_counter_test_suite = { + .name = "VCAP_API_Rule_Counter_Testsuite", + .test_cases = vcap_api_rule_counter_test_cases, +}; + +static struct kunit_case vcap_api_support_test_cases[] = { + KUNIT_CASE(vcap_api_next_lookup_basic_test), + KUNIT_CASE(vcap_api_next_lookup_advanced_test), + KUNIT_CASE(vcap_api_filter_unsupported_keys_test), + KUNIT_CASE(vcap_api_filter_keylist_test), + {} +}; + +static struct kunit_suite vcap_api_support_test_suite = { + .name = "VCAP_API_Support_Testsuite", + .test_cases = vcap_api_support_test_cases, +}; + +static struct kunit_case vcap_api_full_rule_test_cases[] = { + KUNIT_CASE(vcap_api_rule_find_keyset_basic_test), + KUNIT_CASE(vcap_api_rule_find_keyset_failed_test), + KUNIT_CASE(vcap_api_rule_find_keyset_many_test), + KUNIT_CASE(vcap_api_encode_rule_test), + {} +}; + +static struct kunit_suite vcap_api_full_rule_test_suite = { + .name = "VCAP_API_Full_Rule_Testsuite", + .test_cases = vcap_api_full_rule_test_cases, +}; + +static struct kunit_case vcap_api_rule_value_test_cases[] = { + KUNIT_CASE(vcap_api_rule_add_keyvalue_test), + KUNIT_CASE(vcap_api_rule_add_actionvalue_test), + {} +}; + +static struct kunit_suite vcap_api_rule_value_test_suite = { + .name = "VCAP_API_Rule_Value_Testsuite", + .test_cases = vcap_api_rule_value_test_cases, +}; + +static struct kunit_case vcap_api_encoding_test_cases[] = { + KUNIT_CASE(vcap_api_set_bit_1_test), + KUNIT_CASE(vcap_api_set_bit_0_test), + KUNIT_CASE(vcap_api_iterator_init_test), + KUNIT_CASE(vcap_api_iterator_next_test), + KUNIT_CASE(vcap_api_encode_typegroups_test), + KUNIT_CASE(vcap_api_encode_bit_test), + KUNIT_CASE(vcap_api_encode_field_test), + KUNIT_CASE(vcap_api_encode_short_field_test), + KUNIT_CASE(vcap_api_encode_keyfield_test), + KUNIT_CASE(vcap_api_encode_max_keyfield_test), + KUNIT_CASE(vcap_api_encode_actionfield_test), + KUNIT_CASE(vcap_api_keyfield_typegroup_test), + KUNIT_CASE(vcap_api_actionfield_typegroup_test), + KUNIT_CASE(vcap_api_vcap_keyfields_test), + KUNIT_CASE(vcap_api_vcap_actionfields_test), + KUNIT_CASE(vcap_api_encode_rule_keyset_test), + KUNIT_CASE(vcap_api_encode_rule_actionset_test), + {} +}; + +static struct kunit_suite vcap_api_encoding_test_suite = { + .name = "VCAP_API_Encoding_Testsuite", + .test_cases = vcap_api_encoding_test_cases, +}; + +kunit_test_suite(vcap_api_rule_remove_test_suite); +kunit_test_suite(vcap_api_rule_insert_test_suite); +kunit_test_suite(vcap_api_rule_counter_test_suite); +kunit_test_suite(vcap_api_support_test_suite); +kunit_test_suite(vcap_api_full_rule_test_suite); +kunit_test_suite(vcap_api_rule_value_test_suite); +kunit_test_suite(vcap_api_encoding_test_suite); diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_private.h b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h new file mode 100644 index 000000000000..4fd21da97679 --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_private.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API + */ + +#ifndef __VCAP_API_PRIVATE__ +#define __VCAP_API_PRIVATE__ + +#include <linux/types.h> + +#include "vcap_api.h" +#include "vcap_api_client.h" + +#define to_intrule(rule) container_of((rule), struct vcap_rule_internal, data) + +/* Private VCAP API rule data */ +struct vcap_rule_internal { + struct vcap_rule data; /* provided by the client */ + struct list_head list; /* the vcap admin list of rules */ + struct vcap_admin *admin; /* vcap hw instance */ + struct net_device *ndev; /* the interface that the rule applies to */ + struct vcap_control *vctrl; /* the client control */ + u32 sort_key; /* defines the position in the VCAP */ + int keyset_sw; /* subwords in a keyset */ + int actionset_sw; /* subwords in an actionset */ + int keyset_sw_regs; /* registers in a subword in an keyset */ + int actionset_sw_regs; /* registers in a subword in an actionset */ + int size; /* the size of the rule: max(entry, action) */ + u32 addr; /* address in the VCAP at insertion */ + u32 counter_id; /* counter id (if a dedicated counter is available) */ + struct vcap_counter counter; /* last read counter value */ +}; + +/* Bit iterator for the VCAP cache streams */ +struct vcap_stream_iter { + u32 offset; /* bit offset from the stream start */ + u32 sw_width; /* subword width in bits */ + u32 regs_per_sw; /* registers per subword */ + u32 reg_idx; /* current register index */ + u32 reg_bitpos; /* bit offset in current register */ + const struct vcap_typegroup *tg; /* current typegroup */ +}; + +/* Check that the control has a valid set of callbacks */ +int vcap_api_check(struct vcap_control *ctrl); +/* Make a shallow copy of the rule without the fields */ +struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri); +/* Erase the VCAP cache area used or encoding and decoding */ +void vcap_erase_cache(struct vcap_rule_internal *ri); + +/* Iterator functionality */ + +void vcap_iter_init(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset); +void vcap_iter_next(struct vcap_stream_iter *itr); +void vcap_iter_set(struct vcap_stream_iter *itr, int sw_width, + const struct vcap_typegroup *tg, u32 offset); +void vcap_iter_update(struct vcap_stream_iter *itr); + +/* Keyset and keyfield functionality */ + +/* Return the number of keyfields in the keyset */ +int vcap_keyfield_count(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset); +/* Return the typegroup table for the matching keyset (using subword size) */ +const struct vcap_typegroup * +vcap_keyfield_typegroup(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_keyfield_set keyset); +/* Return the list of keyfields for the keyset */ +const struct vcap_field *vcap_keyfields(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_keyfield_set keyset); + +/* Actionset and actionfield functionality */ + +/* Return the actionset information for the actionset */ +const struct vcap_set * +vcap_actionfieldset(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset); +/* Return the number of actionfields in the actionset */ +int vcap_actionfield_count(struct vcap_control *vctrl, + enum vcap_type vt, + enum vcap_actionfield_set actionset); +/* Return the typegroup table for the matching actionset (using subword size) */ +const struct vcap_typegroup * +vcap_actionfield_typegroup(struct vcap_control *vctrl, enum vcap_type vt, + enum vcap_actionfield_set actionset); +/* Return the list of actionfields for the actionset */ +const struct vcap_field * +vcap_actionfields(struct vcap_control *vctrl, + enum vcap_type vt, enum vcap_actionfield_set actionset); +/* Map actionset id to a string with the actionset name */ +const char *vcap_actionset_name(struct vcap_control *vctrl, + enum vcap_actionfield_set actionset); +/* Map key field id to a string with the key name */ +const char *vcap_actionfield_name(struct vcap_control *vctrl, + enum vcap_action_field action); + +/* Read key data from a VCAP address and discover if there are any rule keysets + * here + */ +int vcap_addr_keysets(struct vcap_control *vctrl, struct net_device *ndev, + struct vcap_admin *admin, int addr, + struct vcap_keyset_list *kslist); + +/* Verify that the typegroup information, subword count, keyset and type id + * are in sync and correct, return the list of matchin keysets + */ +int vcap_find_keystream_keysets(struct vcap_control *vctrl, enum vcap_type vt, + u32 *keystream, u32 *mskstream, bool mask, + int sw_max, struct vcap_keyset_list *kslist); + +#endif /* __VCAP_API_PRIVATE__ */ diff --git a/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c new file mode 100644 index 000000000000..5d681d2697cd --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.c @@ -0,0 +1,5570 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP API Test VCAP Model Data + */ + +#include <linux/types.h> +#include <linux/kernel.h> + +#include "vcap_api.h" +#include "vcap_model_kunit.h" + +/* keyfields */ +static const struct vcap_field is0_mll_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 3, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 28, + .width = 3, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 31, + .width = 12, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 43, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 91, + .width = 48, + }, + [VCAP_KF_ETYPE_MPLS] = { + .type = VCAP_FIELD_U32, + .offset = 139, + .width = 2, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 141, + .width = 8, + }, +}; + +static const struct vcap_field is0_tri_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 24, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 30, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 33, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 34, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 46, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 49, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 53, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 65, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 71, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 72, + .width = 12, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 8, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 92, + .width = 1, + }, + [VCAP_KF_OAM_MEL_FLAGS] = { + .type = VCAP_FIELD_U32, + .offset = 93, + .width = 7, + }, +}; + +static const struct vcap_field is0_ll_full_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 32, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 35, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 38, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 51, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 54, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 57, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 58, + .width = 12, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 70, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 118, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 166, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 167, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 183, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 184, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 185, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 187, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 188, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 189, + .width = 6, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 195, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 227, + .width = 32, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 259, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 260, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 261, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 277, + .width = 8, + }, +}; + +static const struct vcap_field is0_normal_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 12, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 19, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 89, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 92, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 108, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 114, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 115, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 130, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 133, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 134, + .width = 12, + }, + [VCAP_KF_DST_ENTRY] = { + .type = VCAP_FIELD_BIT, + .offset = 146, + .width = 1, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 147, + .width = 48, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 195, + .width = 1, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 196, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 197, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 213, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 214, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 215, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 217, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 219, + .width = 6, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 225, + .width = 32, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 257, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 258, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 259, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 275, + .width = 8, + }, +}; + +static const struct vcap_field is0_normal_7tuple_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 12, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 18, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 85, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 95, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 107, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 110, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 113, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 114, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 126, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 129, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 132, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 133, + .width = 12, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 145, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 193, + .width = 48, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 241, + .width = 1, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 242, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 243, + .width = 16, + }, + [VCAP_KF_IP_SNAP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 259, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 260, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 261, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 263, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 264, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 265, + .width = 6, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 271, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 399, + .width = 128, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 527, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 528, + .width = 1, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 529, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 545, + .width = 8, + }, +}; + +static const struct vcap_field is0_normal_5tuple_ip4_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 12, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 19, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGS] = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 3, + }, + [VCAP_KF_8021Q_TPID0] = { + .type = VCAP_FIELD_U32, + .offset = 89, + .width = 3, + }, + [VCAP_KF_8021Q_PCP0] = { + .type = VCAP_FIELD_U32, + .offset = 92, + .width = 3, + }, + [VCAP_KF_8021Q_DEI0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_8021Q_VID0] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 12, + }, + [VCAP_KF_8021Q_TPID1] = { + .type = VCAP_FIELD_U32, + .offset = 108, + .width = 3, + }, + [VCAP_KF_8021Q_PCP1] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 3, + }, + [VCAP_KF_8021Q_DEI1] = { + .type = VCAP_FIELD_BIT, + .offset = 114, + .width = 1, + }, + [VCAP_KF_8021Q_VID1] = { + .type = VCAP_FIELD_U32, + .offset = 115, + .width = 12, + }, + [VCAP_KF_8021Q_TPID2] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 3, + }, + [VCAP_KF_8021Q_PCP2] = { + .type = VCAP_FIELD_U32, + .offset = 130, + .width = 3, + }, + [VCAP_KF_8021Q_DEI2] = { + .type = VCAP_FIELD_BIT, + .offset = 133, + .width = 1, + }, + [VCAP_KF_8021Q_VID2] = { + .type = VCAP_FIELD_U32, + .offset = 134, + .width = 12, + }, + [VCAP_KF_IP_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 146, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 147, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 148, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 151, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 152, + .width = 6, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 158, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 190, + .width = 32, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 222, + .width = 8, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 230, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 231, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 232, + .width = 8, + }, + [VCAP_KF_IP_PAYLOAD_5TUPLE] = { + .type = VCAP_FIELD_U32, + .offset = 240, + .width = 32, + }, +}; + +static const struct vcap_field is0_pure_5tuple_ip4_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 2, + }, + [VCAP_KF_LOOKUP_GEN_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 12, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_KF_L3_DSCP] = { + .type = VCAP_FIELD_U32, + .offset = 21, + .width = 6, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 59, + .width = 32, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 8, + }, + [VCAP_KF_IP_PAYLOAD_5TUPLE] = { + .type = VCAP_FIELD_U32, + .offset = 107, + .width = 32, + }, +}; + +static const struct vcap_field is0_etag_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 7, + }, + [VCAP_KF_8021BR_E_TAGGED] = { + .type = VCAP_FIELD_BIT, + .offset = 10, + .width = 1, + }, + [VCAP_KF_8021BR_GRP] = { + .type = VCAP_FIELD_U32, + .offset = 11, + .width = 2, + }, + [VCAP_KF_8021BR_ECID_EXT] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 8, + }, + [VCAP_KF_8021BR_ECID_BASE] = { + .type = VCAP_FIELD_U32, + .offset = 21, + .width = 12, + }, + [VCAP_KF_8021BR_IGR_ECID_EXT] = { + .type = VCAP_FIELD_U32, + .offset = 33, + .width = 8, + }, + [VCAP_KF_8021BR_IGR_ECID_BASE] = { + .type = VCAP_FIELD_U32, + .offset = 41, + .width = 12, + }, +}; + +static const struct vcap_field is2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 90, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 138, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 186, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 187, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD_ETYPE] = { + .type = VCAP_FIELD_U64, + .offset = 203, + .width = 64, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 267, + .width = 16, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 283, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 284, + .width = 1, + }, +}; + +static const struct vcap_field is2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 86, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 134, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 135, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 136, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 137, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 138, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 139, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 140, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 142, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 174, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 206, + .width = 1, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 207, + .width = 16, + }, +}; + +static const struct vcap_field is2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 169, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 186, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 202, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 220, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 221, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 222, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 223, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 224, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 225, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 226, + .width = 64, + }, +}; + +static const struct vcap_field is2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 2, + }, + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 136, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 168, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 169, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 177, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U112, + .offset = 193, + .width = 96, + }, +}; + +static const struct vcap_field is2_ip6_std_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 18, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 32, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 52, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 53, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 54, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 56, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 81, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 82, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 88, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 89, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 91, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 219, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 220, + .width = 8, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 228, + .width = 16, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U48, + .offset = 244, + .width = 40, + }, +}; + +static const struct vcap_field is2_ip_7tuple_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 2, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 2, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 3, + .width = 8, + }, + [VCAP_KF_IF_IGR_PORT_MASK_L3] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 4, + }, + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_KF_IF_IGR_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 18, + .width = 65, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 86, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 13, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 112, + .width = 1, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 113, + .width = 3, + }, + [VCAP_KF_L2_FWD_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 116, + .width = 1, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 117, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 118, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 119, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 120, + .width = 1, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 121, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 169, + .width = 48, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 217, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 219, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 227, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 355, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 483, + .width = 1, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 484, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 485, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 486, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 502, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 518, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 534, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 535, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 536, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 537, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 538, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 539, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 540, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 541, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 542, + .width = 64, + }, +}; + +static const struct vcap_field is2_ip6_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 4, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 4, + .width = 1, + }, + [VCAP_KF_LOOKUP_PAG] = { + .type = VCAP_FIELD_U32, + .offset = 5, + .width = 8, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 12, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 13, + }, + [VCAP_KF_L3_SMAC_SIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_KF_L3_DMAC_DIP_MATCH] = { + .type = VCAP_FIELD_BIT, + .offset = 40, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_KF_L3_DST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 43, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 171, + .width = 128, + }, +}; + +static const struct vcap_field es2_mac_etype_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 99, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 147, + .width = 48, + }, + [VCAP_KF_ETYPE_LEN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 195, + .width = 1, + }, + [VCAP_KF_ETYPE] = { + .type = VCAP_FIELD_U32, + .offset = 196, + .width = 16, + }, + [VCAP_KF_L2_PAYLOAD_ETYPE] = { + .type = VCAP_FIELD_U64, + .offset = 212, + .width = 64, + }, + [VCAP_KF_OAM_CCM_CNTS_EQ0] = { + .type = VCAP_FIELD_BIT, + .offset = 276, + .width = 1, + }, + [VCAP_KF_OAM_Y1731_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 277, + .width = 1, + }, +}; + +static const struct vcap_field es2_arp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 96, + .width = 2, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 98, + .width = 48, + }, + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 146, + .width = 1, + }, + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 147, + .width = 1, + }, + [VCAP_KF_ARP_LEN_OK_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 148, + .width = 1, + }, + [VCAP_KF_ARP_TGT_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 149, + .width = 1, + }, + [VCAP_KF_ARP_SENDER_MATCH_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 151, + .width = 1, + }, + [VCAP_KF_ARP_OPCODE] = { + .type = VCAP_FIELD_U32, + .offset = 152, + .width = 2, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 154, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 186, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 218, + .width = 1, + }, +}; + +static const struct vcap_field es2_ip4_tcp_udp_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 99, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 100, + .width = 2, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 102, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 103, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 112, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 144, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 176, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 177, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 178, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 194, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 210, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 226, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 227, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 228, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 229, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 230, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 231, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 232, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 233, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 234, + .width = 64, + }, +}; + +static const struct vcap_field es2_ip4_other_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 77, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 78, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 87, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 90, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 91, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 97, + .width = 2, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 99, + .width = 1, + }, + [VCAP_KF_L3_FRAGMENT_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 100, + .width = 2, + }, + [VCAP_KF_L3_OPTIONS_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 102, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 103, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 8, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 112, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 144, + .width = 32, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 176, + .width = 1, + }, + [VCAP_KF_L3_IP_PROTO] = { + .type = VCAP_FIELD_U32, + .offset = 177, + .width = 8, + }, + [VCAP_KF_L3_PAYLOAD] = { + .type = VCAP_FIELD_U112, + .offset = 185, + .width = 96, + }, +}; + +static const struct vcap_field es2_ip_7tuple_keyfield[] = { + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 10, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 13, + }, + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 3, + }, + [VCAP_KF_IF_EGR_PORT_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 32, + }, + [VCAP_KF_IF_IGR_PORT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 74, + .width = 1, + }, + [VCAP_KF_IF_IGR_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 75, + .width = 9, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 87, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 88, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 91, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 92, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 93, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 94, + .width = 2, + }, + [VCAP_KF_L2_DMAC] = { + .type = VCAP_FIELD_U48, + .offset = 96, + .width = 48, + }, + [VCAP_KF_L2_SMAC] = { + .type = VCAP_FIELD_U48, + .offset = 144, + .width = 48, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 192, + .width = 1, + }, + [VCAP_KF_L3_TTL_GT0] = { + .type = VCAP_FIELD_BIT, + .offset = 193, + .width = 1, + }, + [VCAP_KF_L3_TOS] = { + .type = VCAP_FIELD_U32, + .offset = 194, + .width = 8, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 202, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 330, + .width = 128, + }, + [VCAP_KF_L3_DIP_EQ_SIP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 458, + .width = 1, + }, + [VCAP_KF_TCP_UDP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 459, + .width = 1, + }, + [VCAP_KF_TCP_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 460, + .width = 1, + }, + [VCAP_KF_L4_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 461, + .width = 16, + }, + [VCAP_KF_L4_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 477, + .width = 16, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 493, + .width = 16, + }, + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 509, + .width = 1, + }, + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 510, + .width = 1, + }, + [VCAP_KF_L4_FIN] = { + .type = VCAP_FIELD_BIT, + .offset = 511, + .width = 1, + }, + [VCAP_KF_L4_SYN] = { + .type = VCAP_FIELD_BIT, + .offset = 512, + .width = 1, + }, + [VCAP_KF_L4_RST] = { + .type = VCAP_FIELD_BIT, + .offset = 513, + .width = 1, + }, + [VCAP_KF_L4_PSH] = { + .type = VCAP_FIELD_BIT, + .offset = 514, + .width = 1, + }, + [VCAP_KF_L4_ACK] = { + .type = VCAP_FIELD_BIT, + .offset = 515, + .width = 1, + }, + [VCAP_KF_L4_URG] = { + .type = VCAP_FIELD_BIT, + .offset = 516, + .width = 1, + }, + [VCAP_KF_L4_PAYLOAD] = { + .type = VCAP_FIELD_U64, + .offset = 517, + .width = 64, + }, +}; + +static const struct vcap_field es2_ip4_vid_keyfield[] = { + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 10, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 25, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 13, + }, + [VCAP_KF_8021Q_PCP_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 3, + }, + [VCAP_KF_8021Q_DEI_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_KF_COSID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 43, + .width = 3, + }, + [VCAP_KF_L3_DPL_CLS] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 47, + .width = 1, + }, + [VCAP_KF_ES0_ISDX_KEY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 48, + .width = 1, + }, + [VCAP_KF_MIRROR_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 49, + .width = 2, + }, + [VCAP_KF_IP4_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 51, + .width = 1, + }, + [VCAP_KF_L3_IP4_DIP] = { + .type = VCAP_FIELD_U32, + .offset = 52, + .width = 32, + }, + [VCAP_KF_L3_IP4_SIP] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 32, + }, + [VCAP_KF_L4_RNG] = { + .type = VCAP_FIELD_U32, + .offset = 116, + .width = 16, + }, +}; + +static const struct vcap_field es2_ip6_vid_keyfield[] = { + [VCAP_KF_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 0, + .width = 3, + }, + [VCAP_KF_LOOKUP_FIRST_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 3, + .width = 1, + }, + [VCAP_KF_ACL_GRP_ID] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 8, + }, + [VCAP_KF_PROT_ACTIVE] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_KF_L2_MC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_KF_L2_BC_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_KF_ISDX_GT0_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_KF_ISDX_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 12, + }, + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_KF_8021Q_VID_CLS] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 13, + }, + [VCAP_KF_L3_RT_IS] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_KF_L3_IP6_DIP] = { + .type = VCAP_FIELD_U128, + .offset = 43, + .width = 128, + }, + [VCAP_KF_L3_IP6_SIP] = { + .type = VCAP_FIELD_U128, + .offset = 171, + .width = 128, + }, +}; + +/* keyfield_set */ +static const struct vcap_set is0_keyfield_set[] = { + [VCAP_KFS_MLL] = { + .type_id = 0, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_KFS_TRI_VID] = { + .type_id = 0, + .sw_per_item = 2, + .sw_cnt = 6, + }, + [VCAP_KFS_LL_FULL] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_NORMAL] = { + .type_id = 1, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_NORMAL_7TUPLE] = { + .type_id = 0, + .sw_per_item = 12, + .sw_cnt = 1, + }, + [VCAP_KFS_NORMAL_5TUPLE_IP4] = { + .type_id = 2, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_PURE_5TUPLE_IP4] = { + .type_id = 2, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_KFS_ETAG] = { + .type_id = 3, + .sw_per_item = 2, + .sw_cnt = 6, + }, +}; + +static const struct vcap_set is2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 3, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 4, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 5, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP6_STD] = { + .type_id = 6, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP_7TUPLE] = { + .type_id = 1, + .sw_per_item = 12, + .sw_cnt = 1, + }, + [VCAP_KFS_IP6_VID] = { + .type_id = 9, + .sw_per_item = 6, + .sw_cnt = 2, + }, +}; + +static const struct vcap_set es2_keyfield_set[] = { + [VCAP_KFS_MAC_ETYPE] = { + .type_id = 0, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_ARP] = { + .type_id = 1, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_TCP_UDP] = { + .type_id = 2, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP4_OTHER] = { + .type_id = 3, + .sw_per_item = 6, + .sw_cnt = 2, + }, + [VCAP_KFS_IP_7TUPLE] = { + .type_id = -1, + .sw_per_item = 12, + .sw_cnt = 1, + }, + [VCAP_KFS_IP4_VID] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_KFS_IP6_VID] = { + .type_id = 5, + .sw_per_item = 6, + .sw_cnt = 2, + }, +}; + +/* keyfield_set map */ +static const struct vcap_field *is0_keyfield_set_map[] = { + [VCAP_KFS_MLL] = is0_mll_keyfield, + [VCAP_KFS_TRI_VID] = is0_tri_vid_keyfield, + [VCAP_KFS_LL_FULL] = is0_ll_full_keyfield, + [VCAP_KFS_NORMAL] = is0_normal_keyfield, + [VCAP_KFS_NORMAL_7TUPLE] = is0_normal_7tuple_keyfield, + [VCAP_KFS_NORMAL_5TUPLE_IP4] = is0_normal_5tuple_ip4_keyfield, + [VCAP_KFS_PURE_5TUPLE_IP4] = is0_pure_5tuple_ip4_keyfield, + [VCAP_KFS_ETAG] = is0_etag_keyfield, +}; + +static const struct vcap_field *is2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = is2_mac_etype_keyfield, + [VCAP_KFS_ARP] = is2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = is2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = is2_ip4_other_keyfield, + [VCAP_KFS_IP6_STD] = is2_ip6_std_keyfield, + [VCAP_KFS_IP_7TUPLE] = is2_ip_7tuple_keyfield, + [VCAP_KFS_IP6_VID] = is2_ip6_vid_keyfield, +}; + +static const struct vcap_field *es2_keyfield_set_map[] = { + [VCAP_KFS_MAC_ETYPE] = es2_mac_etype_keyfield, + [VCAP_KFS_ARP] = es2_arp_keyfield, + [VCAP_KFS_IP4_TCP_UDP] = es2_ip4_tcp_udp_keyfield, + [VCAP_KFS_IP4_OTHER] = es2_ip4_other_keyfield, + [VCAP_KFS_IP_7TUPLE] = es2_ip_7tuple_keyfield, + [VCAP_KFS_IP4_VID] = es2_ip4_vid_keyfield, + [VCAP_KFS_IP6_VID] = es2_ip6_vid_keyfield, +}; + +/* keyfield_set map sizes */ +static int is0_keyfield_set_map_size[] = { + [VCAP_KFS_MLL] = ARRAY_SIZE(is0_mll_keyfield), + [VCAP_KFS_TRI_VID] = ARRAY_SIZE(is0_tri_vid_keyfield), + [VCAP_KFS_LL_FULL] = ARRAY_SIZE(is0_ll_full_keyfield), + [VCAP_KFS_NORMAL] = ARRAY_SIZE(is0_normal_keyfield), + [VCAP_KFS_NORMAL_7TUPLE] = ARRAY_SIZE(is0_normal_7tuple_keyfield), + [VCAP_KFS_NORMAL_5TUPLE_IP4] = ARRAY_SIZE(is0_normal_5tuple_ip4_keyfield), + [VCAP_KFS_PURE_5TUPLE_IP4] = ARRAY_SIZE(is0_pure_5tuple_ip4_keyfield), + [VCAP_KFS_ETAG] = ARRAY_SIZE(is0_etag_keyfield), +}; + +static int is2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(is2_mac_etype_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(is2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(is2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(is2_ip4_other_keyfield), + [VCAP_KFS_IP6_STD] = ARRAY_SIZE(is2_ip6_std_keyfield), + [VCAP_KFS_IP_7TUPLE] = ARRAY_SIZE(is2_ip_7tuple_keyfield), + [VCAP_KFS_IP6_VID] = ARRAY_SIZE(is2_ip6_vid_keyfield), +}; + +static int es2_keyfield_set_map_size[] = { + [VCAP_KFS_MAC_ETYPE] = ARRAY_SIZE(es2_mac_etype_keyfield), + [VCAP_KFS_ARP] = ARRAY_SIZE(es2_arp_keyfield), + [VCAP_KFS_IP4_TCP_UDP] = ARRAY_SIZE(es2_ip4_tcp_udp_keyfield), + [VCAP_KFS_IP4_OTHER] = ARRAY_SIZE(es2_ip4_other_keyfield), + [VCAP_KFS_IP_7TUPLE] = ARRAY_SIZE(es2_ip_7tuple_keyfield), + [VCAP_KFS_IP4_VID] = ARRAY_SIZE(es2_ip4_vid_keyfield), + [VCAP_KFS_IP6_VID] = ARRAY_SIZE(es2_ip6_vid_keyfield), +}; + +/* actionfields */ +static const struct vcap_field is0_mlbs_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 6, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 3, + }, + [VCAP_AF_MAP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 9, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 32, + .width = 13, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 45, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 46, + .width = 12, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 58, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 59, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 60, + .width = 3, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 63, + .width = 3, + }, + [VCAP_AF_OAM_TWAMP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 66, + .width = 1, + }, + [VCAP_AF_OAM_IP_BFD_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 67, + .width = 1, + }, + [VCAP_AF_TC_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 3, + }, + [VCAP_AF_TTL_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 71, + .width = 3, + }, + [VCAP_AF_NUM_VLD_LABELS] = { + .type = VCAP_FIELD_U32, + .offset = 74, + .width = 2, + }, + [VCAP_AF_FWD_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 76, + .width = 3, + }, + [VCAP_AF_MPLS_OAM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 79, + .width = 3, + }, + [VCAP_AF_MPLS_MEP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 82, + .width = 1, + }, + [VCAP_AF_MPLS_MIP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_AF_MPLS_OAM_FLAVOR] = { + .type = VCAP_FIELD_BIT, + .offset = 84, + .width = 1, + }, + [VCAP_AF_MPLS_IP_CTRL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 85, + .width = 1, + }, + [VCAP_AF_PAG_OVERRIDE_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 86, + .width = 8, + }, + [VCAP_AF_PAG_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 94, + .width = 8, + }, + [VCAP_AF_S2_KEY_SEL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 102, + .width = 1, + }, + [VCAP_AF_S2_KEY_SEL_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 103, + .width = 6, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 109, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 111, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 112, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 117, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W16_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 122, + .width = 5, + }, + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 129, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 131, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 132, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 135, + .width = 12, + }, +}; + +static const struct vcap_field is0_mlbs_reduced_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 6, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 15, + .width = 12, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 27, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 28, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 3, + }, + [VCAP_AF_TC_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 32, + .width = 1, + }, + [VCAP_AF_TTL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 33, + .width = 1, + }, + [VCAP_AF_FWD_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 34, + .width = 3, + }, + [VCAP_AF_MPLS_OAM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 3, + }, + [VCAP_AF_MPLS_MEP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 40, + .width = 1, + }, + [VCAP_AF_MPLS_MIP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 41, + .width = 1, + }, + [VCAP_AF_MPLS_OAM_FLAVOR] = { + .type = VCAP_FIELD_BIT, + .offset = 42, + .width = 1, + }, + [VCAP_AF_MPLS_IP_CTRL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 43, + .width = 1, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT_REDUCED] = { + .type = VCAP_FIELD_U32, + .offset = 47, + .width = 3, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 50, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W32_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 55, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 57, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 59, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 60, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 63, + .width = 12, + }, +}; + +static const struct vcap_field is0_classification_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_DSCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_DSCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 6, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 9, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 12, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 13, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 2, + }, + [VCAP_AF_DEI_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_AF_DEI_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_AF_PCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 21, + .width = 1, + }, + [VCAP_AF_PCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 22, + .width = 3, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 25, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_AF_MAP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 30, + .width = 9, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 39, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 45, + .width = 13, + }, + [VCAP_AF_VLAN_POP_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 58, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 59, + .width = 2, + }, + [VCAP_AF_VLAN_PUSH_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 61, + .width = 1, + }, + [VCAP_AF_VLAN_PUSH_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 62, + .width = 2, + }, + [VCAP_AF_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 64, + .width = 2, + }, + [VCAP_AF_VLAN_WAS_TAGGED] = { + .type = VCAP_FIELD_U32, + .offset = 66, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 68, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 69, + .width = 12, + }, + [VCAP_AF_RT_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 81, + .width = 2, + }, + [VCAP_AF_LPM_AFFIX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 83, + .width = 1, + }, + [VCAP_AF_LPM_AFFIX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 84, + .width = 10, + }, + [VCAP_AF_RLEG_DMAC_CHK_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 94, + .width = 1, + }, + [VCAP_AF_TTL_DECR_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 95, + .width = 1, + }, + [VCAP_AF_L3_MAC_UPDATE_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 96, + .width = 1, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 97, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 98, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 3, + }, + [VCAP_AF_MIP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 102, + .width = 2, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 104, + .width = 3, + }, + [VCAP_AF_OAM_TWAMP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 107, + .width = 1, + }, + [VCAP_AF_OAM_IP_BFD_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 108, + .width = 1, + }, + [VCAP_AF_PAG_OVERRIDE_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 109, + .width = 8, + }, + [VCAP_AF_PAG_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 117, + .width = 8, + }, + [VCAP_AF_S2_KEY_SEL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 125, + .width = 1, + }, + [VCAP_AF_S2_KEY_SEL_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 126, + .width = 6, + }, + [VCAP_AF_INJ_MASQ_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 132, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 133, + .width = 7, + }, + [VCAP_AF_LPORT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 140, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_LPORT] = { + .type = VCAP_FIELD_U32, + .offset = 141, + .width = 7, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 148, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 151, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 156, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W16_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 161, + .width = 5, + }, + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 166, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 168, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 170, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 171, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 174, + .width = 12, + }, +}; + +static const struct vcap_field is0_full_actionfield[] = { + [VCAP_AF_DSCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_DSCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 1, + .width = 6, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 8, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 11, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 16, + .width = 2, + }, + [VCAP_AF_DEI_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 18, + .width = 1, + }, + [VCAP_AF_DEI_VAL] = { + .type = VCAP_FIELD_BIT, + .offset = 19, + .width = 1, + }, + [VCAP_AF_PCP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 20, + .width = 1, + }, + [VCAP_AF_PCP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 21, + .width = 3, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 24, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 26, + .width = 3, + }, + [VCAP_AF_MAP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 29, + .width = 9, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 38, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 41, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 13, + }, + [VCAP_AF_VLAN_POP_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 57, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 58, + .width = 2, + }, + [VCAP_AF_VLAN_PUSH_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 60, + .width = 1, + }, + [VCAP_AF_VLAN_PUSH_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 61, + .width = 2, + }, + [VCAP_AF_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 63, + .width = 2, + }, + [VCAP_AF_VLAN_WAS_TAGGED] = { + .type = VCAP_FIELD_U32, + .offset = 65, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 67, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 68, + .width = 12, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 80, + .width = 3, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 83, + .width = 65, + }, + [VCAP_AF_RT_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 148, + .width = 2, + }, + [VCAP_AF_LPM_AFFIX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 150, + .width = 1, + }, + [VCAP_AF_LPM_AFFIX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 151, + .width = 10, + }, + [VCAP_AF_RLEG_DMAC_CHK_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 161, + .width = 1, + }, + [VCAP_AF_TTL_DECR_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 162, + .width = 1, + }, + [VCAP_AF_L3_MAC_UPDATE_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 163, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 164, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 165, + .width = 3, + }, + [VCAP_AF_MIP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 168, + .width = 2, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 170, + .width = 3, + }, + [VCAP_AF_OAM_TWAMP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 173, + .width = 1, + }, + [VCAP_AF_OAM_IP_BFD_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 174, + .width = 1, + }, + [VCAP_AF_RSVD_LBL_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 4, + }, + [VCAP_AF_TC_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 179, + .width = 3, + }, + [VCAP_AF_TTL_LABEL] = { + .type = VCAP_FIELD_U32, + .offset = 182, + .width = 3, + }, + [VCAP_AF_NUM_VLD_LABELS] = { + .type = VCAP_FIELD_U32, + .offset = 185, + .width = 2, + }, + [VCAP_AF_FWD_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 187, + .width = 3, + }, + [VCAP_AF_MPLS_OAM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 190, + .width = 3, + }, + [VCAP_AF_MPLS_MEP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 193, + .width = 1, + }, + [VCAP_AF_MPLS_MIP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 194, + .width = 1, + }, + [VCAP_AF_MPLS_OAM_FLAVOR] = { + .type = VCAP_FIELD_BIT, + .offset = 195, + .width = 1, + }, + [VCAP_AF_MPLS_IP_CTRL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 196, + .width = 1, + }, + [VCAP_AF_CUSTOM_ACE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 197, + .width = 5, + }, + [VCAP_AF_CUSTOM_ACE_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 202, + .width = 2, + }, + [VCAP_AF_PAG_OVERRIDE_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 204, + .width = 8, + }, + [VCAP_AF_PAG_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 212, + .width = 8, + }, + [VCAP_AF_S2_KEY_SEL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 220, + .width = 1, + }, + [VCAP_AF_S2_KEY_SEL_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 221, + .width = 6, + }, + [VCAP_AF_INJ_MASQ_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 227, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_PORT] = { + .type = VCAP_FIELD_U32, + .offset = 228, + .width = 7, + }, + [VCAP_AF_LPORT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 235, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_LPORT] = { + .type = VCAP_FIELD_U32, + .offset = 236, + .width = 7, + }, + [VCAP_AF_MATCH_ID] = { + .type = VCAP_FIELD_U32, + .offset = 243, + .width = 16, + }, + [VCAP_AF_MATCH_ID_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 259, + .width = 16, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 275, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 277, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 278, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 283, + .width = 5, + }, + [VCAP_AF_NXT_NORM_W16_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 288, + .width = 5, + }, + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 293, + .width = 2, + }, + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 295, + .width = 2, + }, + [VCAP_AF_NXT_NORMALIZE] = { + .type = VCAP_FIELD_BIT, + .offset = 297, + .width = 1, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 298, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 301, + .width = 12, + }, +}; + +static const struct vcap_field is0_class_reduced_actionfield[] = { + [VCAP_AF_TYPE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_COSID_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_COSID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 3, + }, + [VCAP_AF_QOS_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 5, + .width = 1, + }, + [VCAP_AF_QOS_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 6, + .width = 3, + }, + [VCAP_AF_DP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_DP_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 2, + }, + [VCAP_AF_MAP_LOOKUP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 12, + .width = 2, + }, + [VCAP_AF_MAP_KEY] = { + .type = VCAP_FIELD_U32, + .offset = 14, + .width = 3, + }, + [VCAP_AF_CLS_VID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 3, + }, + [VCAP_AF_GVID_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 3, + }, + [VCAP_AF_VID_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 23, + .width = 13, + }, + [VCAP_AF_VLAN_POP_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 36, + .width = 1, + }, + [VCAP_AF_VLAN_POP_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 37, + .width = 2, + }, + [VCAP_AF_VLAN_PUSH_CNT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 39, + .width = 1, + }, + [VCAP_AF_VLAN_PUSH_CNT] = { + .type = VCAP_FIELD_U32, + .offset = 40, + .width = 2, + }, + [VCAP_AF_TPID_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 42, + .width = 2, + }, + [VCAP_AF_VLAN_WAS_TAGGED] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 2, + }, + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 46, + .width = 1, + }, + [VCAP_AF_ISDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 47, + .width = 12, + }, + [VCAP_AF_FWD_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 59, + .width = 1, + }, + [VCAP_AF_CPU_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 60, + .width = 1, + }, + [VCAP_AF_CPU_Q] = { + .type = VCAP_FIELD_U32, + .offset = 61, + .width = 3, + }, + [VCAP_AF_MIP_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 64, + .width = 2, + }, + [VCAP_AF_OAM_Y1731_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 66, + .width = 3, + }, + [VCAP_AF_LPORT_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 69, + .width = 1, + }, + [VCAP_AF_INJ_MASQ_LPORT] = { + .type = VCAP_FIELD_U32, + .offset = 70, + .width = 7, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_U32, + .offset = 77, + .width = 2, + }, + [VCAP_AF_PIPELINE_ACT_SEL] = { + .type = VCAP_FIELD_BIT, + .offset = 79, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 80, + .width = 5, + }, + [VCAP_AF_NXT_KEY_TYPE] = { + .type = VCAP_FIELD_U32, + .offset = 85, + .width = 5, + }, + [VCAP_AF_NXT_IDX_CTRL] = { + .type = VCAP_FIELD_U32, + .offset = 90, + .width = 3, + }, + [VCAP_AF_NXT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 93, + .width = 12, + }, +}; + +static const struct vcap_field is2_base_type_actionfield[] = { + [VCAP_AF_IS_INNER_ACL] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_PIPELINE_FORCE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_PIPELINE_PT] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 5, + }, + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 7, + .width = 1, + }, + [VCAP_AF_INTR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 8, + .width = 1, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 9, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 10, + .width = 3, + }, + [VCAP_AF_CPU_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 13, + .width = 1, + }, + [VCAP_AF_LRN_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 14, + .width = 1, + }, + [VCAP_AF_RT_DIS] = { + .type = VCAP_FIELD_BIT, + .offset = 15, + .width = 1, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 16, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 17, + .width = 6, + }, + [VCAP_AF_IGNORE_PIPELINE_CTRL] = { + .type = VCAP_FIELD_BIT, + .offset = 23, + .width = 1, + }, + [VCAP_AF_DLB_OFFSET] = { + .type = VCAP_FIELD_U32, + .offset = 24, + .width = 3, + }, + [VCAP_AF_MASK_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 3, + }, + [VCAP_AF_PORT_MASK] = { + .type = VCAP_FIELD_U72, + .offset = 30, + .width = 68, + }, + [VCAP_AF_RSDX_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 98, + .width = 1, + }, + [VCAP_AF_RSDX_VAL] = { + .type = VCAP_FIELD_U32, + .offset = 99, + .width = 12, + }, + [VCAP_AF_MIRROR_PROBE] = { + .type = VCAP_FIELD_U32, + .offset = 111, + .width = 2, + }, + [VCAP_AF_REW_CMD] = { + .type = VCAP_FIELD_U32, + .offset = 113, + .width = 11, + }, + [VCAP_AF_TTL_UPDATE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 124, + .width = 1, + }, + [VCAP_AF_SAM_SEQ_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 125, + .width = 1, + }, + [VCAP_AF_TCP_UDP_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 126, + .width = 1, + }, + [VCAP_AF_TCP_UDP_DPORT] = { + .type = VCAP_FIELD_U32, + .offset = 127, + .width = 16, + }, + [VCAP_AF_TCP_UDP_SPORT] = { + .type = VCAP_FIELD_U32, + .offset = 143, + .width = 16, + }, + [VCAP_AF_MATCH_ID] = { + .type = VCAP_FIELD_U32, + .offset = 159, + .width = 16, + }, + [VCAP_AF_MATCH_ID_MASK] = { + .type = VCAP_FIELD_U32, + .offset = 175, + .width = 16, + }, + [VCAP_AF_CNT_ID] = { + .type = VCAP_FIELD_U32, + .offset = 191, + .width = 12, + }, + [VCAP_AF_SWAP_MAC_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 203, + .width = 1, + }, + [VCAP_AF_ACL_RT_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 204, + .width = 4, + }, + [VCAP_AF_ACL_MAC] = { + .type = VCAP_FIELD_U48, + .offset = 208, + .width = 48, + }, + [VCAP_AF_DMAC_OFFSET_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 256, + .width = 1, + }, + [VCAP_AF_PTP_MASTER_SEL] = { + .type = VCAP_FIELD_U32, + .offset = 257, + .width = 2, + }, + [VCAP_AF_LOG_MSG_INTERVAL] = { + .type = VCAP_FIELD_U32, + .offset = 259, + .width = 4, + }, + [VCAP_AF_SIP_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 263, + .width = 5, + }, + [VCAP_AF_RLEG_STAT_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 268, + .width = 3, + }, + [VCAP_AF_IGR_ACL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 271, + .width = 1, + }, + [VCAP_AF_EGR_ACL_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 272, + .width = 1, + }, +}; + +static const struct vcap_field es2_base_type_actionfield[] = { + [VCAP_AF_HIT_ME_ONCE] = { + .type = VCAP_FIELD_BIT, + .offset = 0, + .width = 1, + }, + [VCAP_AF_INTR_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 1, + .width = 1, + }, + [VCAP_AF_FWD_MODE] = { + .type = VCAP_FIELD_U32, + .offset = 2, + .width = 2, + }, + [VCAP_AF_COPY_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 4, + .width = 16, + }, + [VCAP_AF_COPY_PORT_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 20, + .width = 7, + }, + [VCAP_AF_MIRROR_PROBE_ID] = { + .type = VCAP_FIELD_U32, + .offset = 27, + .width = 2, + }, + [VCAP_AF_CPU_COPY_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 29, + .width = 1, + }, + [VCAP_AF_CPU_QUEUE_NUM] = { + .type = VCAP_FIELD_U32, + .offset = 30, + .width = 3, + }, + [VCAP_AF_POLICE_ENA] = { + .type = VCAP_FIELD_BIT, + .offset = 33, + .width = 1, + }, + [VCAP_AF_POLICE_REMARK] = { + .type = VCAP_FIELD_BIT, + .offset = 34, + .width = 1, + }, + [VCAP_AF_POLICE_IDX] = { + .type = VCAP_FIELD_U32, + .offset = 35, + .width = 6, + }, + [VCAP_AF_ES2_REW_CMD] = { + .type = VCAP_FIELD_U32, + .offset = 41, + .width = 3, + }, + [VCAP_AF_CNT_ID] = { + .type = VCAP_FIELD_U32, + .offset = 44, + .width = 11, + }, + [VCAP_AF_IGNORE_PIPELINE_CTRL] = { + .type = VCAP_FIELD_BIT, + .offset = 55, + .width = 1, + }, +}; + +/* actionfield_set */ +static const struct vcap_set is0_actionfield_set[] = { + [VCAP_AFS_MLBS] = { + .type_id = 0, + .sw_per_item = 2, + .sw_cnt = 6, + }, + [VCAP_AFS_MLBS_REDUCED] = { + .type_id = 0, + .sw_per_item = 1, + .sw_cnt = 12, + }, + [VCAP_AFS_CLASSIFICATION] = { + .type_id = 1, + .sw_per_item = 2, + .sw_cnt = 6, + }, + [VCAP_AFS_FULL] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, + [VCAP_AFS_CLASS_REDUCED] = { + .type_id = 1, + .sw_per_item = 1, + .sw_cnt = 12, + }, +}; + +static const struct vcap_set is2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, +}; + +static const struct vcap_set es2_actionfield_set[] = { + [VCAP_AFS_BASE_TYPE] = { + .type_id = -1, + .sw_per_item = 3, + .sw_cnt = 4, + }, +}; + +/* actionfield_set map */ +static const struct vcap_field *is0_actionfield_set_map[] = { + [VCAP_AFS_MLBS] = is0_mlbs_actionfield, + [VCAP_AFS_MLBS_REDUCED] = is0_mlbs_reduced_actionfield, + [VCAP_AFS_CLASSIFICATION] = is0_classification_actionfield, + [VCAP_AFS_FULL] = is0_full_actionfield, + [VCAP_AFS_CLASS_REDUCED] = is0_class_reduced_actionfield, +}; + +static const struct vcap_field *is2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = is2_base_type_actionfield, +}; + +static const struct vcap_field *es2_actionfield_set_map[] = { + [VCAP_AFS_BASE_TYPE] = es2_base_type_actionfield, +}; + +/* actionfield_set map size */ +static int is0_actionfield_set_map_size[] = { + [VCAP_AFS_MLBS] = ARRAY_SIZE(is0_mlbs_actionfield), + [VCAP_AFS_MLBS_REDUCED] = ARRAY_SIZE(is0_mlbs_reduced_actionfield), + [VCAP_AFS_CLASSIFICATION] = ARRAY_SIZE(is0_classification_actionfield), + [VCAP_AFS_FULL] = ARRAY_SIZE(is0_full_actionfield), + [VCAP_AFS_CLASS_REDUCED] = ARRAY_SIZE(is0_class_reduced_actionfield), +}; + +static int is2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(is2_base_type_actionfield), +}; + +static int es2_actionfield_set_map_size[] = { + [VCAP_AFS_BASE_TYPE] = ARRAY_SIZE(es2_base_type_actionfield), +}; + +/* Type Groups */ +static const struct vcap_typegroup is0_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 5, + .value = 16, + }, + { + .offset = 52, + .width = 1, + .value = 0, + }, + { + .offset = 104, + .width = 2, + .value = 0, + }, + { + .offset = 156, + .width = 3, + .value = 0, + }, + { + .offset = 208, + .width = 2, + .value = 0, + }, + { + .offset = 260, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 4, + .value = 0, + }, + { + .offset = 364, + .width = 1, + .value = 0, + }, + { + .offset = 416, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 3, + .value = 0, + }, + { + .offset = 520, + .width = 2, + .value = 0, + }, + { + .offset = 572, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 4, + .value = 8, + }, + { + .offset = 52, + .width = 1, + .value = 0, + }, + { + .offset = 104, + .width = 2, + .value = 0, + }, + { + .offset = 156, + .width = 3, + .value = 0, + }, + { + .offset = 208, + .width = 2, + .value = 0, + }, + { + .offset = 260, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x3_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 52, + .width = 2, + .value = 0, + }, + { + .offset = 104, + .width = 2, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x2_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 52, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup is2_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x3_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup is2_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup es2_x12_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + { + .offset = 312, + .width = 2, + .value = 0, + }, + { + .offset = 468, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup es2_x6_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 156, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup es2_x3_keyfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup es2_x1_keyfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is0_keyfield_set_typegroups[] = { + [12] = is0_x12_keyfield_set_typegroups, + [6] = is0_x6_keyfield_set_typegroups, + [3] = is0_x3_keyfield_set_typegroups, + [2] = is0_x2_keyfield_set_typegroups, + [1] = is0_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *is2_keyfield_set_typegroups[] = { + [12] = is2_x12_keyfield_set_typegroups, + [6] = is2_x6_keyfield_set_typegroups, + [3] = is2_x3_keyfield_set_typegroups, + [1] = is2_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *es2_keyfield_set_typegroups[] = { + [12] = es2_x12_keyfield_set_typegroups, + [6] = es2_x6_keyfield_set_typegroups, + [3] = es2_x3_keyfield_set_typegroups, + [1] = es2_x1_keyfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup is0_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 3, + .value = 4, + }, + { + .offset = 110, + .width = 2, + .value = 0, + }, + { + .offset = 220, + .width = 2, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x2_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 110, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is0_x1_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 1, + .value = 1, + }, + {} +}; + +static const struct vcap_typegroup is2_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 110, + .width = 1, + .value = 0, + }, + { + .offset = 220, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup is2_x1_actionfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup es2_x3_actionfield_set_typegroups[] = { + { + .offset = 0, + .width = 2, + .value = 2, + }, + { + .offset = 21, + .width = 1, + .value = 0, + }, + { + .offset = 42, + .width = 1, + .value = 0, + }, + {} +}; + +static const struct vcap_typegroup es2_x1_actionfield_set_typegroups[] = { + {} +}; + +static const struct vcap_typegroup *is0_actionfield_set_typegroups[] = { + [3] = is0_x3_actionfield_set_typegroups, + [2] = is0_x2_actionfield_set_typegroups, + [1] = is0_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *is2_actionfield_set_typegroups[] = { + [3] = is2_x3_actionfield_set_typegroups, + [1] = is2_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +static const struct vcap_typegroup *es2_actionfield_set_typegroups[] = { + [3] = es2_x3_actionfield_set_typegroups, + [1] = es2_x1_actionfield_set_typegroups, + [13] = NULL, +}; + +/* Keyfieldset names */ +static const char * const vcap_keyfield_set_names[] = { + [VCAP_KFS_NO_VALUE] = "(None)", + [VCAP_KFS_ARP] = "VCAP_KFS_ARP", + [VCAP_KFS_ETAG] = "VCAP_KFS_ETAG", + [VCAP_KFS_IP4_OTHER] = "VCAP_KFS_IP4_OTHER", + [VCAP_KFS_IP4_TCP_UDP] = "VCAP_KFS_IP4_TCP_UDP", + [VCAP_KFS_IP4_VID] = "VCAP_KFS_IP4_VID", + [VCAP_KFS_IP6_STD] = "VCAP_KFS_IP6_STD", + [VCAP_KFS_IP6_VID] = "VCAP_KFS_IP6_VID", + [VCAP_KFS_IP_7TUPLE] = "VCAP_KFS_IP_7TUPLE", + [VCAP_KFS_LL_FULL] = "VCAP_KFS_LL_FULL", + [VCAP_KFS_MAC_ETYPE] = "VCAP_KFS_MAC_ETYPE", + [VCAP_KFS_MLL] = "VCAP_KFS_MLL", + [VCAP_KFS_NORMAL] = "VCAP_KFS_NORMAL", + [VCAP_KFS_NORMAL_5TUPLE_IP4] = "VCAP_KFS_NORMAL_5TUPLE_IP4", + [VCAP_KFS_NORMAL_7TUPLE] = "VCAP_KFS_NORMAL_7TUPLE", + [VCAP_KFS_PURE_5TUPLE_IP4] = "VCAP_KFS_PURE_5TUPLE_IP4", + [VCAP_KFS_TRI_VID] = "VCAP_KFS_TRI_VID", +}; + +/* Actionfieldset names */ +static const char * const vcap_actionfield_set_names[] = { + [VCAP_AFS_NO_VALUE] = "(None)", + [VCAP_AFS_BASE_TYPE] = "VCAP_AFS_BASE_TYPE", + [VCAP_AFS_CLASSIFICATION] = "VCAP_AFS_CLASSIFICATION", + [VCAP_AFS_CLASS_REDUCED] = "VCAP_AFS_CLASS_REDUCED", + [VCAP_AFS_FULL] = "VCAP_AFS_FULL", + [VCAP_AFS_MLBS] = "VCAP_AFS_MLBS", + [VCAP_AFS_MLBS_REDUCED] = "VCAP_AFS_MLBS_REDUCED", +}; + +/* Keyfield names */ +static const char * const vcap_keyfield_names[] = { + [VCAP_KF_NO_VALUE] = "(None)", + [VCAP_KF_8021BR_ECID_BASE] = "8021BR_ECID_BASE", + [VCAP_KF_8021BR_ECID_EXT] = "8021BR_ECID_EXT", + [VCAP_KF_8021BR_E_TAGGED] = "8021BR_E_TAGGED", + [VCAP_KF_8021BR_GRP] = "8021BR_GRP", + [VCAP_KF_8021BR_IGR_ECID_BASE] = "8021BR_IGR_ECID_BASE", + [VCAP_KF_8021BR_IGR_ECID_EXT] = "8021BR_IGR_ECID_EXT", + [VCAP_KF_8021Q_DEI0] = "8021Q_DEI0", + [VCAP_KF_8021Q_DEI1] = "8021Q_DEI1", + [VCAP_KF_8021Q_DEI2] = "8021Q_DEI2", + [VCAP_KF_8021Q_DEI_CLS] = "8021Q_DEI_CLS", + [VCAP_KF_8021Q_PCP0] = "8021Q_PCP0", + [VCAP_KF_8021Q_PCP1] = "8021Q_PCP1", + [VCAP_KF_8021Q_PCP2] = "8021Q_PCP2", + [VCAP_KF_8021Q_PCP_CLS] = "8021Q_PCP_CLS", + [VCAP_KF_8021Q_TPID0] = "8021Q_TPID0", + [VCAP_KF_8021Q_TPID1] = "8021Q_TPID1", + [VCAP_KF_8021Q_TPID2] = "8021Q_TPID2", + [VCAP_KF_8021Q_VID0] = "8021Q_VID0", + [VCAP_KF_8021Q_VID1] = "8021Q_VID1", + [VCAP_KF_8021Q_VID2] = "8021Q_VID2", + [VCAP_KF_8021Q_VID_CLS] = "8021Q_VID_CLS", + [VCAP_KF_8021Q_VLAN_TAGGED_IS] = "8021Q_VLAN_TAGGED_IS", + [VCAP_KF_8021Q_VLAN_TAGS] = "8021Q_VLAN_TAGS", + [VCAP_KF_ACL_GRP_ID] = "ACL_GRP_ID", + [VCAP_KF_ARP_ADDR_SPACE_OK_IS] = "ARP_ADDR_SPACE_OK_IS", + [VCAP_KF_ARP_LEN_OK_IS] = "ARP_LEN_OK_IS", + [VCAP_KF_ARP_OPCODE] = "ARP_OPCODE", + [VCAP_KF_ARP_OPCODE_UNKNOWN_IS] = "ARP_OPCODE_UNKNOWN_IS", + [VCAP_KF_ARP_PROTO_SPACE_OK_IS] = "ARP_PROTO_SPACE_OK_IS", + [VCAP_KF_ARP_SENDER_MATCH_IS] = "ARP_SENDER_MATCH_IS", + [VCAP_KF_ARP_TGT_MATCH_IS] = "ARP_TGT_MATCH_IS", + [VCAP_KF_COSID_CLS] = "COSID_CLS", + [VCAP_KF_DST_ENTRY] = "DST_ENTRY", + [VCAP_KF_ES0_ISDX_KEY_ENA] = "ES0_ISDX_KEY_ENA", + [VCAP_KF_ETYPE] = "ETYPE", + [VCAP_KF_ETYPE_LEN_IS] = "ETYPE_LEN_IS", + [VCAP_KF_ETYPE_MPLS] = "ETYPE_MPLS", + [VCAP_KF_IF_EGR_PORT_MASK] = "IF_EGR_PORT_MASK", + [VCAP_KF_IF_EGR_PORT_MASK_RNG] = "IF_EGR_PORT_MASK_RNG", + [VCAP_KF_IF_IGR_PORT] = "IF_IGR_PORT", + [VCAP_KF_IF_IGR_PORT_MASK] = "IF_IGR_PORT_MASK", + [VCAP_KF_IF_IGR_PORT_MASK_L3] = "IF_IGR_PORT_MASK_L3", + [VCAP_KF_IF_IGR_PORT_MASK_RNG] = "IF_IGR_PORT_MASK_RNG", + [VCAP_KF_IF_IGR_PORT_MASK_SEL] = "IF_IGR_PORT_MASK_SEL", + [VCAP_KF_IF_IGR_PORT_SEL] = "IF_IGR_PORT_SEL", + [VCAP_KF_IP4_IS] = "IP4_IS", + [VCAP_KF_IP_MC_IS] = "IP_MC_IS", + [VCAP_KF_IP_PAYLOAD_5TUPLE] = "IP_PAYLOAD_5TUPLE", + [VCAP_KF_IP_SNAP_IS] = "IP_SNAP_IS", + [VCAP_KF_ISDX_CLS] = "ISDX_CLS", + [VCAP_KF_ISDX_GT0_IS] = "ISDX_GT0_IS", + [VCAP_KF_L2_BC_IS] = "L2_BC_IS", + [VCAP_KF_L2_DMAC] = "L2_DMAC", + [VCAP_KF_L2_FWD_IS] = "L2_FWD_IS", + [VCAP_KF_L2_MC_IS] = "L2_MC_IS", + [VCAP_KF_L2_PAYLOAD_ETYPE] = "L2_PAYLOAD_ETYPE", + [VCAP_KF_L2_SMAC] = "L2_SMAC", + [VCAP_KF_L3_DIP_EQ_SIP_IS] = "L3_DIP_EQ_SIP_IS", + [VCAP_KF_L3_DMAC_DIP_MATCH] = "L3_DMAC_DIP_MATCH", + [VCAP_KF_L3_DPL_CLS] = "L3_DPL_CLS", + [VCAP_KF_L3_DSCP] = "L3_DSCP", + [VCAP_KF_L3_DST_IS] = "L3_DST_IS", + [VCAP_KF_L3_FRAGMENT_TYPE] = "L3_FRAGMENT_TYPE", + [VCAP_KF_L3_FRAG_INVLD_L4_LEN] = "L3_FRAG_INVLD_L4_LEN", + [VCAP_KF_L3_IP4_DIP] = "L3_IP4_DIP", + [VCAP_KF_L3_IP4_SIP] = "L3_IP4_SIP", + [VCAP_KF_L3_IP6_DIP] = "L3_IP6_DIP", + [VCAP_KF_L3_IP6_SIP] = "L3_IP6_SIP", + [VCAP_KF_L3_IP_PROTO] = "L3_IP_PROTO", + [VCAP_KF_L3_OPTIONS_IS] = "L3_OPTIONS_IS", + [VCAP_KF_L3_PAYLOAD] = "L3_PAYLOAD", + [VCAP_KF_L3_RT_IS] = "L3_RT_IS", + [VCAP_KF_L3_SMAC_SIP_MATCH] = "L3_SMAC_SIP_MATCH", + [VCAP_KF_L3_TOS] = "L3_TOS", + [VCAP_KF_L3_TTL_GT0] = "L3_TTL_GT0", + [VCAP_KF_L4_ACK] = "L4_ACK", + [VCAP_KF_L4_DPORT] = "L4_DPORT", + [VCAP_KF_L4_FIN] = "L4_FIN", + [VCAP_KF_L4_PAYLOAD] = "L4_PAYLOAD", + [VCAP_KF_L4_PSH] = "L4_PSH", + [VCAP_KF_L4_RNG] = "L4_RNG", + [VCAP_KF_L4_RST] = "L4_RST", + [VCAP_KF_L4_SEQUENCE_EQ0_IS] = "L4_SEQUENCE_EQ0_IS", + [VCAP_KF_L4_SPORT] = "L4_SPORT", + [VCAP_KF_L4_SPORT_EQ_DPORT_IS] = "L4_SPORT_EQ_DPORT_IS", + [VCAP_KF_L4_SYN] = "L4_SYN", + [VCAP_KF_L4_URG] = "L4_URG", + [VCAP_KF_LOOKUP_FIRST_IS] = "LOOKUP_FIRST_IS", + [VCAP_KF_LOOKUP_GEN_IDX] = "LOOKUP_GEN_IDX", + [VCAP_KF_LOOKUP_GEN_IDX_SEL] = "LOOKUP_GEN_IDX_SEL", + [VCAP_KF_LOOKUP_PAG] = "LOOKUP_PAG", + [VCAP_KF_MIRROR_ENA] = "MIRROR_ENA", + [VCAP_KF_OAM_CCM_CNTS_EQ0] = "OAM_CCM_CNTS_EQ0", + [VCAP_KF_OAM_MEL_FLAGS] = "OAM_MEL_FLAGS", + [VCAP_KF_OAM_Y1731_IS] = "OAM_Y1731_IS", + [VCAP_KF_PROT_ACTIVE] = "PROT_ACTIVE", + [VCAP_KF_TCP_IS] = "TCP_IS", + [VCAP_KF_TCP_UDP_IS] = "TCP_UDP_IS", + [VCAP_KF_TYPE] = "TYPE", +}; + +/* Actionfield names */ +static const char * const vcap_actionfield_names[] = { + [VCAP_AF_NO_VALUE] = "(None)", + [VCAP_AF_ACL_MAC] = "ACL_MAC", + [VCAP_AF_ACL_RT_MODE] = "ACL_RT_MODE", + [VCAP_AF_CLS_VID_SEL] = "CLS_VID_SEL", + [VCAP_AF_CNT_ID] = "CNT_ID", + [VCAP_AF_COPY_PORT_NUM] = "COPY_PORT_NUM", + [VCAP_AF_COPY_QUEUE_NUM] = "COPY_QUEUE_NUM", + [VCAP_AF_COSID_ENA] = "COSID_ENA", + [VCAP_AF_COSID_VAL] = "COSID_VAL", + [VCAP_AF_CPU_COPY_ENA] = "CPU_COPY_ENA", + [VCAP_AF_CPU_DIS] = "CPU_DIS", + [VCAP_AF_CPU_ENA] = "CPU_ENA", + [VCAP_AF_CPU_Q] = "CPU_Q", + [VCAP_AF_CPU_QUEUE_NUM] = "CPU_QUEUE_NUM", + [VCAP_AF_CUSTOM_ACE_ENA] = "CUSTOM_ACE_ENA", + [VCAP_AF_CUSTOM_ACE_OFFSET] = "CUSTOM_ACE_OFFSET", + [VCAP_AF_DEI_ENA] = "DEI_ENA", + [VCAP_AF_DEI_VAL] = "DEI_VAL", + [VCAP_AF_DLB_OFFSET] = "DLB_OFFSET", + [VCAP_AF_DMAC_OFFSET_ENA] = "DMAC_OFFSET_ENA", + [VCAP_AF_DP_ENA] = "DP_ENA", + [VCAP_AF_DP_VAL] = "DP_VAL", + [VCAP_AF_DSCP_ENA] = "DSCP_ENA", + [VCAP_AF_DSCP_VAL] = "DSCP_VAL", + [VCAP_AF_EGR_ACL_ENA] = "EGR_ACL_ENA", + [VCAP_AF_ES2_REW_CMD] = "ES2_REW_CMD", + [VCAP_AF_FWD_DIS] = "FWD_DIS", + [VCAP_AF_FWD_MODE] = "FWD_MODE", + [VCAP_AF_FWD_TYPE] = "FWD_TYPE", + [VCAP_AF_GVID_ADD_REPLACE_SEL] = "GVID_ADD_REPLACE_SEL", + [VCAP_AF_HIT_ME_ONCE] = "HIT_ME_ONCE", + [VCAP_AF_IGNORE_PIPELINE_CTRL] = "IGNORE_PIPELINE_CTRL", + [VCAP_AF_IGR_ACL_ENA] = "IGR_ACL_ENA", + [VCAP_AF_INJ_MASQ_ENA] = "INJ_MASQ_ENA", + [VCAP_AF_INJ_MASQ_LPORT] = "INJ_MASQ_LPORT", + [VCAP_AF_INJ_MASQ_PORT] = "INJ_MASQ_PORT", + [VCAP_AF_INTR_ENA] = "INTR_ENA", + [VCAP_AF_ISDX_ADD_REPLACE_SEL] = "ISDX_ADD_REPLACE_SEL", + [VCAP_AF_ISDX_VAL] = "ISDX_VAL", + [VCAP_AF_IS_INNER_ACL] = "IS_INNER_ACL", + [VCAP_AF_L3_MAC_UPDATE_DIS] = "L3_MAC_UPDATE_DIS", + [VCAP_AF_LOG_MSG_INTERVAL] = "LOG_MSG_INTERVAL", + [VCAP_AF_LPM_AFFIX_ENA] = "LPM_AFFIX_ENA", + [VCAP_AF_LPM_AFFIX_VAL] = "LPM_AFFIX_VAL", + [VCAP_AF_LPORT_ENA] = "LPORT_ENA", + [VCAP_AF_LRN_DIS] = "LRN_DIS", + [VCAP_AF_MAP_IDX] = "MAP_IDX", + [VCAP_AF_MAP_KEY] = "MAP_KEY", + [VCAP_AF_MAP_LOOKUP_SEL] = "MAP_LOOKUP_SEL", + [VCAP_AF_MASK_MODE] = "MASK_MODE", + [VCAP_AF_MATCH_ID] = "MATCH_ID", + [VCAP_AF_MATCH_ID_MASK] = "MATCH_ID_MASK", + [VCAP_AF_MIP_SEL] = "MIP_SEL", + [VCAP_AF_MIRROR_PROBE] = "MIRROR_PROBE", + [VCAP_AF_MIRROR_PROBE_ID] = "MIRROR_PROBE_ID", + [VCAP_AF_MPLS_IP_CTRL_ENA] = "MPLS_IP_CTRL_ENA", + [VCAP_AF_MPLS_MEP_ENA] = "MPLS_MEP_ENA", + [VCAP_AF_MPLS_MIP_ENA] = "MPLS_MIP_ENA", + [VCAP_AF_MPLS_OAM_FLAVOR] = "MPLS_OAM_FLAVOR", + [VCAP_AF_MPLS_OAM_TYPE] = "MPLS_OAM_TYPE", + [VCAP_AF_NUM_VLD_LABELS] = "NUM_VLD_LABELS", + [VCAP_AF_NXT_IDX] = "NXT_IDX", + [VCAP_AF_NXT_IDX_CTRL] = "NXT_IDX_CTRL", + [VCAP_AF_NXT_KEY_TYPE] = "NXT_KEY_TYPE", + [VCAP_AF_NXT_NORMALIZE] = "NXT_NORMALIZE", + [VCAP_AF_NXT_NORM_W16_OFFSET] = "NXT_NORM_W16_OFFSET", + [VCAP_AF_NXT_NORM_W32_OFFSET] = "NXT_NORM_W32_OFFSET", + [VCAP_AF_NXT_OFFSET_FROM_TYPE] = "NXT_OFFSET_FROM_TYPE", + [VCAP_AF_NXT_TYPE_AFTER_OFFSET] = "NXT_TYPE_AFTER_OFFSET", + [VCAP_AF_OAM_IP_BFD_ENA] = "OAM_IP_BFD_ENA", + [VCAP_AF_OAM_TWAMP_ENA] = "OAM_TWAMP_ENA", + [VCAP_AF_OAM_Y1731_SEL] = "OAM_Y1731_SEL", + [VCAP_AF_PAG_OVERRIDE_MASK] = "PAG_OVERRIDE_MASK", + [VCAP_AF_PAG_VAL] = "PAG_VAL", + [VCAP_AF_PCP_ENA] = "PCP_ENA", + [VCAP_AF_PCP_VAL] = "PCP_VAL", + [VCAP_AF_PIPELINE_ACT_SEL] = "PIPELINE_ACT_SEL", + [VCAP_AF_PIPELINE_FORCE_ENA] = "PIPELINE_FORCE_ENA", + [VCAP_AF_PIPELINE_PT] = "PIPELINE_PT", + [VCAP_AF_PIPELINE_PT_REDUCED] = "PIPELINE_PT_REDUCED", + [VCAP_AF_POLICE_ENA] = "POLICE_ENA", + [VCAP_AF_POLICE_IDX] = "POLICE_IDX", + [VCAP_AF_POLICE_REMARK] = "POLICE_REMARK", + [VCAP_AF_PORT_MASK] = "PORT_MASK", + [VCAP_AF_PTP_MASTER_SEL] = "PTP_MASTER_SEL", + [VCAP_AF_QOS_ENA] = "QOS_ENA", + [VCAP_AF_QOS_VAL] = "QOS_VAL", + [VCAP_AF_REW_CMD] = "REW_CMD", + [VCAP_AF_RLEG_DMAC_CHK_DIS] = "RLEG_DMAC_CHK_DIS", + [VCAP_AF_RLEG_STAT_IDX] = "RLEG_STAT_IDX", + [VCAP_AF_RSDX_ENA] = "RSDX_ENA", + [VCAP_AF_RSDX_VAL] = "RSDX_VAL", + [VCAP_AF_RSVD_LBL_VAL] = "RSVD_LBL_VAL", + [VCAP_AF_RT_DIS] = "RT_DIS", + [VCAP_AF_RT_SEL] = "RT_SEL", + [VCAP_AF_S2_KEY_SEL_ENA] = "S2_KEY_SEL_ENA", + [VCAP_AF_S2_KEY_SEL_IDX] = "S2_KEY_SEL_IDX", + [VCAP_AF_SAM_SEQ_ENA] = "SAM_SEQ_ENA", + [VCAP_AF_SIP_IDX] = "SIP_IDX", + [VCAP_AF_SWAP_MAC_ENA] = "SWAP_MAC_ENA", + [VCAP_AF_TCP_UDP_DPORT] = "TCP_UDP_DPORT", + [VCAP_AF_TCP_UDP_ENA] = "TCP_UDP_ENA", + [VCAP_AF_TCP_UDP_SPORT] = "TCP_UDP_SPORT", + [VCAP_AF_TC_ENA] = "TC_ENA", + [VCAP_AF_TC_LABEL] = "TC_LABEL", + [VCAP_AF_TPID_SEL] = "TPID_SEL", + [VCAP_AF_TTL_DECR_DIS] = "TTL_DECR_DIS", + [VCAP_AF_TTL_ENA] = "TTL_ENA", + [VCAP_AF_TTL_LABEL] = "TTL_LABEL", + [VCAP_AF_TTL_UPDATE_ENA] = "TTL_UPDATE_ENA", + [VCAP_AF_TYPE] = "TYPE", + [VCAP_AF_VID_VAL] = "VID_VAL", + [VCAP_AF_VLAN_POP_CNT] = "VLAN_POP_CNT", + [VCAP_AF_VLAN_POP_CNT_ENA] = "VLAN_POP_CNT_ENA", + [VCAP_AF_VLAN_PUSH_CNT] = "VLAN_PUSH_CNT", + [VCAP_AF_VLAN_PUSH_CNT_ENA] = "VLAN_PUSH_CNT_ENA", + [VCAP_AF_VLAN_WAS_TAGGED] = "VLAN_WAS_TAGGED", +}; + +/* VCAPs */ +const struct vcap_info kunit_test_vcaps[] = { + [VCAP_TYPE_IS0] = { + .name = "is0", + .rows = 1024, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 110, + .default_cnt = 140, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = is0_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is0_keyfield_set), + .actionfield_set = is0_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is0_actionfield_set), + .keyfield_set_map = is0_keyfield_set_map, + .keyfield_set_map_size = is0_keyfield_set_map_size, + .actionfield_set_map = is0_actionfield_set_map, + .actionfield_set_map_size = is0_actionfield_set_map_size, + .keyfield_set_typegroups = is0_keyfield_set_typegroups, + .actionfield_set_typegroups = is0_actionfield_set_typegroups, + }, + [VCAP_TYPE_IS2] = { + .name = "is2", + .rows = 256, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 110, + .default_cnt = 73, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = is2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(is2_keyfield_set), + .actionfield_set = is2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(is2_actionfield_set), + .keyfield_set_map = is2_keyfield_set_map, + .keyfield_set_map_size = is2_keyfield_set_map_size, + .actionfield_set_map = is2_actionfield_set_map, + .actionfield_set_map_size = is2_actionfield_set_map_size, + .keyfield_set_typegroups = is2_keyfield_set_typegroups, + .actionfield_set_typegroups = is2_actionfield_set_typegroups, + }, + [VCAP_TYPE_ES2] = { + .name = "es2", + .rows = 1024, + .sw_count = 12, + .sw_width = 52, + .sticky_width = 1, + .act_width = 21, + .default_cnt = 74, + .require_cnt_dis = 0, + .version = 1, + .keyfield_set = es2_keyfield_set, + .keyfield_set_size = ARRAY_SIZE(es2_keyfield_set), + .actionfield_set = es2_actionfield_set, + .actionfield_set_size = ARRAY_SIZE(es2_actionfield_set), + .keyfield_set_map = es2_keyfield_set_map, + .keyfield_set_map_size = es2_keyfield_set_map_size, + .actionfield_set_map = es2_actionfield_set_map, + .actionfield_set_map_size = es2_actionfield_set_map_size, + .keyfield_set_typegroups = es2_keyfield_set_typegroups, + .actionfield_set_typegroups = es2_actionfield_set_typegroups, + }, +}; + +const struct vcap_statistics kunit_test_vcap_stats = { + .name = "kunit_test", + .count = 3, + .keyfield_set_names = vcap_keyfield_set_names, + .actionfield_set_names = vcap_actionfield_set_names, + .keyfield_names = vcap_keyfield_names, + .actionfield_names = vcap_actionfield_names, +}; diff --git a/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h new file mode 100644 index 000000000000..b5a74f0eef9b --- /dev/null +++ b/drivers/net/ethernet/microchip/vcap/vcap_model_kunit.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries. + * Microchip VCAP test model interface for kunit testing + */ + +#ifndef __VCAP_MODEL_KUNIT_H__ +#define __VCAP_MODEL_KUNIT_H__ +extern const struct vcap_info kunit_test_vcaps[]; +extern const struct vcap_statistics kunit_test_vcap_stats; +#endif /* __VCAP_MODEL_KUNIT_H__ */ |