diff options
author | Igor Russkikh <irusskikh@marvell.com> | 2020-04-30 11:04:41 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2020-05-01 15:37:58 -0700 |
commit | 3417368494db497c0426d1dcc46c4c459ff43ca7 (patch) | |
tree | 73a93a93edf97e22b6a52006a8be676bf8d5333e | |
parent | 57fe8fd2255cd97d2c2a9b69cb5172c0f15343b8 (diff) |
net: atlantic: add A2 RPF hw_ops
This patch adds RPF-related hw_ops, which are needed for basic
functionality.
Signed-off-by: Igor Russkikh <irusskikh@marvell.com>
Co-developed-by: Dmitry Bogdanov <dbogdanov@marvell.com>
Signed-off-by: Dmitry Bogdanov <dbogdanov@marvell.com>
Signed-off-by: Mark Starovoytov <mstarovoitov@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
3 files changed, 259 insertions, 0 deletions
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h index 5513254642b3..5db57ea9a5bd 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h @@ -360,6 +360,8 @@ struct aq_rx_filter_vlan { u8 queue; }; +#define HW_ATL_VLAN_MAX_FILTERS 16U + struct aq_rx_filter_l2 { s8 queue; u8 location; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c index 58c74a73b6cf..7dd5f9a1c505 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c @@ -4,9 +4,17 @@ */ #include "aq_hw.h" +#include "aq_hw_utils.h" +#include "aq_nic.h" +#include "hw_atl/hw_atl_utils.h" +#include "hw_atl/hw_atl_llh.h" #include "hw_atl2_utils.h" +#include "hw_atl2_llh.h" #include "hw_atl2_internal.h" +static int hw_atl2_act_rslvr_table_set(struct aq_hw_s *self, u8 location, + u32 tag, u32 mask, u32 action); + #define DEFAULT_BOARD_BASIC_CAPABILITIES \ .is_64_dma = true, \ .msix_irqs = 8U, \ @@ -55,6 +63,11 @@ const struct aq_hw_caps_s hw_atl2_caps_aqc113 = { AQ_NIC_RATE_10M, }; +static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self) +{ + return hw_atl_reg_glb_cpu_sem_get(self, HW_ATL2_FW_SM_ACT_RSLVR); +} + static int hw_atl2_hw_reset(struct aq_hw_s *self) { return -EOPNOTSUPP; @@ -78,6 +91,60 @@ static int hw_atl2_hw_offload_set(struct aq_hw_s *self, return -EOPNOTSUPP; } +static void hw_atl2_hw_new_rx_filter_vlan_promisc(struct aq_hw_s *self, + bool promisc) +{ + u16 off_action = (!promisc && + !hw_atl_rpfl2promiscuous_mode_en_get(self)) ? + HW_ATL2_ACTION_DROP : HW_ATL2_ACTION_DISABLE; + struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; + u8 index; + + index = priv->art_base_index + HW_ATL2_RPF_VLAN_PROMISC_OFF_INDEX; + hw_atl2_act_rslvr_table_set(self, index, 0, + HW_ATL2_RPF_TAG_VLAN_MASK | + HW_ATL2_RPF_TAG_UNTAG_MASK, off_action); +} + +static void hw_atl2_hw_new_rx_filter_promisc(struct aq_hw_s *self, bool promisc) +{ + u16 off_action = promisc ? HW_ATL2_ACTION_DISABLE : HW_ATL2_ACTION_DROP; + struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; + bool vlan_promisc_enable; + u8 index; + + index = priv->art_base_index + HW_ATL2_RPF_L2_PROMISC_OFF_INDEX; + hw_atl2_act_rslvr_table_set(self, index, 0, + HW_ATL2_RPF_TAG_UC_MASK | + HW_ATL2_RPF_TAG_ALLMC_MASK, + off_action); + + /* turn VLAN promisc mode too */ + vlan_promisc_enable = hw_atl_rpf_vlan_prom_mode_en_get(self); + hw_atl2_hw_new_rx_filter_vlan_promisc(self, promisc | + vlan_promisc_enable); +} + +static int hw_atl2_act_rslvr_table_set(struct aq_hw_s *self, u8 location, + u32 tag, u32 mask, u32 action) +{ + u32 val; + int err; + + err = readx_poll_timeout_atomic(hw_atl2_sem_act_rslvr_get, + self, val, val == 1, + 1, 10000U); + if (err) + return err; + + hw_atl2_rpf_act_rslvr_record_set(self, location, tag, mask, + action); + + hw_atl_reg_glb_cpu_sem_set(self, 1, HW_ATL2_FW_SM_ACT_RSLVR); + + return err; +} + static int hw_atl2_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr) { return -EOPNOTSUPP; @@ -170,6 +237,88 @@ static int hw_atl2_hw_irq_read(struct aq_hw_s *self, u64 *mask) return -EOPNOTSUPP; } +#define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U) + +static int hw_atl2_hw_packet_filter_set(struct aq_hw_s *self, + unsigned int packet_filter) +{ + struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; + u32 vlan_promisc; + u32 l2_promisc; + unsigned int i; + + l2_promisc = IS_FILTER_ENABLED(IFF_PROMISC) || + !!(cfg->priv_flags & BIT(AQ_HW_LOOPBACK_DMA_NET)); + vlan_promisc = l2_promisc || cfg->is_vlan_force_promisc; + + hw_atl_rpfl2promiscuous_mode_en_set(self, l2_promisc); + + hw_atl_rpf_vlan_prom_mode_en_set(self, vlan_promisc); + + hw_atl2_hw_new_rx_filter_promisc(self, IS_FILTER_ENABLED(IFF_PROMISC)); + + hw_atl_rpfl2multicast_flr_en_set(self, + IS_FILTER_ENABLED(IFF_ALLMULTI) && + IS_FILTER_ENABLED(IFF_MULTICAST), 0); + + hw_atl_rpfl2_accept_all_mc_packets_set(self, + IS_FILTER_ENABLED(IFF_ALLMULTI) && + IS_FILTER_ENABLED(IFF_MULTICAST)); + + hw_atl_rpfl2broadcast_en_set(self, IS_FILTER_ENABLED(IFF_BROADCAST)); + + for (i = HW_ATL2_MAC_MIN; i < HW_ATL2_MAC_MAX; ++i) + hw_atl_rpfl2_uc_flr_en_set(self, + (cfg->is_mc_list_enabled && + (i <= cfg->mc_list_count)) ? + 1U : 0U, i); + + return aq_hw_err_from_flags(self); +} + +#undef IS_FILTER_ENABLED + +static int hw_atl2_hw_multicast_list_set(struct aq_hw_s *self, + u8 ar_mac + [AQ_HW_MULTICAST_ADDRESS_MAX] + [ETH_ALEN], + u32 count) +{ + struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; + int err = 0; + + if (count > (HW_ATL2_MAC_MAX - HW_ATL2_MAC_MIN)) { + err = -EBADRQC; + goto err_exit; + } + for (cfg->mc_list_count = 0U; + cfg->mc_list_count < count; + ++cfg->mc_list_count) { + u32 i = cfg->mc_list_count; + u32 h = (ar_mac[i][0] << 8) | (ar_mac[i][1]); + u32 l = (ar_mac[i][2] << 24) | (ar_mac[i][3] << 16) | + (ar_mac[i][4] << 8) | ar_mac[i][5]; + + hw_atl_rpfl2_uc_flr_en_set(self, 0U, HW_ATL2_MAC_MIN + i); + + hw_atl_rpfl2unicast_dest_addresslsw_set(self, l, + HW_ATL2_MAC_MIN + i); + + hw_atl_rpfl2unicast_dest_addressmsw_set(self, h, + HW_ATL2_MAC_MIN + i); + + hw_atl2_rpfl2_uc_flr_tag_set(self, 1, HW_ATL2_MAC_MIN + i); + + hw_atl_rpfl2_uc_flr_en_set(self, (cfg->is_mc_list_enabled), + HW_ATL2_MAC_MIN + i); + } + + err = aq_hw_err_from_flags(self); + +err_exit: + return err; +} + static int hw_atl2_hw_interrupt_moderation_set(struct aq_hw_s *self) { return -EOPNOTSUPP; @@ -195,6 +344,61 @@ static struct aq_stats_s *hw_atl2_utils_get_hw_stats(struct aq_hw_s *self) return &self->curr_stats; } +static int hw_atl2_hw_vlan_set(struct aq_hw_s *self, + struct aq_rx_filter_vlan *aq_vlans) +{ + struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; + u32 queue; + u8 index; + int i; + + hw_atl_rpf_vlan_prom_mode_en_set(self, 1U); + + for (i = 0; i < HW_ATL_VLAN_MAX_FILTERS; i++) { + queue = HW_ATL2_ACTION_ASSIGN_QUEUE(aq_vlans[i].queue); + + hw_atl_rpf_vlan_flr_en_set(self, 0U, i); + hw_atl_rpf_vlan_rxq_en_flr_set(self, 0U, i); + index = priv->art_base_index + HW_ATL2_RPF_VLAN_USER_INDEX + i; + hw_atl2_act_rslvr_table_set(self, index, 0, 0, + HW_ATL2_ACTION_DISABLE); + if (aq_vlans[i].enable) { + hw_atl_rpf_vlan_id_flr_set(self, + aq_vlans[i].vlan_id, i); + hw_atl_rpf_vlan_flr_act_set(self, 1U, i); + hw_atl_rpf_vlan_flr_en_set(self, 1U, i); + + if (aq_vlans[i].queue != 0xFF) { + hw_atl_rpf_vlan_rxq_flr_set(self, + aq_vlans[i].queue, + i); + hw_atl_rpf_vlan_rxq_en_flr_set(self, 1U, i); + + hw_atl2_rpf_vlan_flr_tag_set(self, i + 2, i); + + index = priv->art_base_index + + HW_ATL2_RPF_VLAN_USER_INDEX + i; + hw_atl2_act_rslvr_table_set(self, index, + (i + 2) << HW_ATL2_RPF_TAG_VLAN_OFFSET, + HW_ATL2_RPF_TAG_VLAN_MASK, queue); + } else { + hw_atl2_rpf_vlan_flr_tag_set(self, 1, i); + } + } + } + + return aq_hw_err_from_flags(self); +} + +static int hw_atl2_hw_vlan_ctrl(struct aq_hw_s *self, bool enable) +{ + /* set promisc in case of disabing the vlan filter */ + hw_atl_rpf_vlan_prom_mode_en_set(self, !enable); + hw_atl2_hw_new_rx_filter_vlan_promisc(self, !enable); + + return aq_hw_err_from_flags(self); +} + const struct aq_hw_ops hw_atl2_ops = { .hw_set_mac_address = hw_atl2_hw_mac_addr_set, .hw_init = hw_atl2_hw_init, @@ -218,6 +422,10 @@ const struct aq_hw_ops hw_atl2_ops = { .hw_ring_rx_init = hw_atl2_hw_ring_rx_init, .hw_ring_tx_init = hw_atl2_hw_ring_tx_init, + .hw_packet_filter_set = hw_atl2_hw_packet_filter_set, + .hw_filter_vlan_set = hw_atl2_hw_vlan_set, + .hw_filter_vlan_ctrl = hw_atl2_hw_vlan_ctrl, + .hw_multicast_list_set = hw_atl2_hw_multicast_list_set, .hw_interrupt_moderation_set = hw_atl2_hw_interrupt_moderation_set, .hw_rss_set = hw_atl2_hw_rss_set, .hw_rss_hash_set = hw_atl2_hw_rss_hash_set, diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h index f82058484332..dccc89df2223 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h @@ -18,6 +18,10 @@ #define HW_ATL2_TXD_SIZE (16U) #define HW_ATL2_RXD_SIZE (16U) +#define HW_ATL2_MAC_UC 0U +#define HW_ATL2_MAC_MIN 1U +#define HW_ATL2_MAC_MAX 38U + #define HW_ATL2_TC_MAX 1U #define HW_ATL2_RSS_MAX 8U @@ -29,6 +33,51 @@ #define HW_ATL2_MAX_RXD 8184U #define HW_ATL2_MAX_TXD 8184U +#define HW_ATL2_FW_SM_ACT_RSLVR 0x3U + +#define HW_ATL2_RPF_TAG_UC_OFFSET 0x0 +#define HW_ATL2_RPF_TAG_ALLMC_OFFSET 0x6 +#define HW_ATL2_RPF_TAG_ET_OFFSET 0x7 +#define HW_ATL2_RPF_TAG_VLAN_OFFSET 0xA +#define HW_ATL2_RPF_TAG_UNTAG_OFFSET 0xE +#define HW_ATL2_RPF_TAG_L3_V4_OFFSET 0xF +#define HW_ATL2_RPF_TAG_L3_V6_OFFSET 0x12 +#define HW_ATL2_RPF_TAG_L4_OFFSET 0x15 +#define HW_ATL2_RPF_TAG_L4_FLEX_OFFSET 0x18 +#define HW_ATL2_RPF_TAG_FLEX_OFFSET 0x1B +#define HW_ATL2_RPF_TAG_PCP_OFFSET 0x1D + +#define HW_ATL2_RPF_TAG_UC_MASK (0x0000003F << HW_ATL2_RPF_TAG_UC_OFFSET) +#define HW_ATL2_RPF_TAG_ALLMC_MASK (0x00000001 << HW_ATL2_RPF_TAG_ALLMC_OFFSET) +#define HW_ATL2_RPF_TAG_UNTAG_MASK (0x00000001 << HW_ATL2_RPF_TAG_UNTAG_OFFSET) +#define HW_ATL2_RPF_TAG_VLAN_MASK (0x0000000F << HW_ATL2_RPF_TAG_VLAN_OFFSET) +#define HW_ATL2_RPF_TAG_ET_MASK (0x00000007 << HW_ATL2_RPF_TAG_ET_OFFSET) +#define HW_ATL2_RPF_TAG_L3_V4_MASK (0x00000007 << HW_ATL2_RPF_TAG_L3_V4_OFFSET) +#define HW_ATL2_RPF_TAG_L3_V6_MASK (0x00000007 << HW_ATL2_RPF_TAG_L3_V6_OFFSET) +#define HW_ATL2_RPF_TAG_L4_MASK (0x00000007 << HW_ATL2_RPF_TAG_L4_OFFSET) +#define HW_ATL2_RPF_TAG_PCP_MASK (0x00000007 << HW_ATL2_RPF_TAG_PCP_OFFSET) + +enum HW_ATL2_RPF_ART_INDEX { + HW_ATL2_RPF_L2_PROMISC_OFF_INDEX, + HW_ATL2_RPF_VLAN_PROMISC_OFF_INDEX, + HW_ATL2_RPF_L3L4_USER_INDEX = 8, + HW_ATL2_RPF_ET_PCP_USER_INDEX = HW_ATL2_RPF_L3L4_USER_INDEX + 16, + HW_ATL2_RPF_VLAN_USER_INDEX = HW_ATL2_RPF_ET_PCP_USER_INDEX + 16, + HW_ATL2_RPF_PCP_TO_TC_INDEX = HW_ATL2_RPF_VLAN_USER_INDEX + + HW_ATL_VLAN_MAX_FILTERS, +}; + +#define HW_ATL2_ACTION(ACTION, RSS, INDEX, VALID) \ + ((((ACTION) & 0x3U) << 8) | \ + (((RSS) & 0x1U) << 7) | \ + (((INDEX) & 0x3FU) << 2) | \ + (((VALID) & 0x1U) << 0)) + +#define HW_ATL2_ACTION_DROP HW_ATL2_ACTION(0, 0, 0, 1) +#define HW_ATL2_ACTION_DISABLE HW_ATL2_ACTION(0, 0, 0, 0) +#define HW_ATL2_ACTION_ASSIGN_QUEUE(QUEUE) HW_ATL2_ACTION(1, 0, (QUEUE), 1) +#define HW_ATL2_ACTION_ASSIGN_TC(TC) HW_ATL2_ACTION(1, 1, (TC), 1) + struct hw_atl2_priv { struct statistics_s last_stats; unsigned int art_base_index; |