// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (C) 2014-2017 aQuantia Corporation. */
+/* Copyright (C) 2014-2019 aQuantia Corporation. */
 
 /* File aq_filters.c: RX filters related functions. */
 
                                  struct aq_hw_rx_fltrs_s *rx_fltrs,
                                  struct ethtool_rx_flow_spec *fsp)
 {
+       u32 last_location = AQ_RX_LAST_LOC_FL3L4 -
+                           aq_nic->aq_hw_rx_fltrs.fl3l4.reserved_count;
+
        if (fsp->location < AQ_RX_FIRST_LOC_FL3L4 ||
-           fsp->location > AQ_RX_LAST_LOC_FL3L4) {
+           fsp->location > last_location) {
                netdev_err(aq_nic->ndev,
                           "ethtool: location must be in range [%d, %d]",
-                          AQ_RX_FIRST_LOC_FL3L4,
-                          AQ_RX_LAST_LOC_FL3L4);
+                          AQ_RX_FIRST_LOC_FL3L4, last_location);
                return -EINVAL;
        }
        if (rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv4) {
                     struct aq_hw_rx_fltrs_s *rx_fltrs,
                     struct ethtool_rx_flow_spec *fsp)
 {
+       u32 last_location = AQ_RX_LAST_LOC_FETHERT -
+                           aq_nic->aq_hw_rx_fltrs.fet_reserved_count;
+
        if (fsp->location < AQ_RX_FIRST_LOC_FETHERT ||
-           fsp->location > AQ_RX_LAST_LOC_FETHERT) {
+           fsp->location > last_location) {
                netdev_err(aq_nic->ndev,
                           "ethtool: location must be in range [%d, %d]",
                           AQ_RX_FIRST_LOC_FETHERT,
-                          AQ_RX_LAST_LOC_FETHERT);
+                          last_location);
                return -EINVAL;
        }
 
 
 #include "aq_pci_func.h"
 #include "aq_main.h"
 #include "aq_ptp.h"
+#include "aq_filters.h"
 
 #include <linux/moduleparam.h>
 #include <linux/netdevice.h>
 err_exit:
        rtnl_unlock();
 }
+
+u8 aq_nic_reserve_filter(struct aq_nic_s *self, enum aq_rx_filter_type type)
+{
+       u8 location = 0xFF;
+       u32 fltr_cnt;
+       u32 n_bit;
+
+       switch (type) {
+       case aq_rx_filter_ethertype:
+               location = AQ_RX_LAST_LOC_FETHERT - AQ_RX_FIRST_LOC_FETHERT -
+                          self->aq_hw_rx_fltrs.fet_reserved_count;
+               self->aq_hw_rx_fltrs.fet_reserved_count++;
+               break;
+       case aq_rx_filter_l3l4:
+               fltr_cnt = AQ_RX_LAST_LOC_FL3L4 - AQ_RX_FIRST_LOC_FL3L4;
+               n_bit = fltr_cnt - self->aq_hw_rx_fltrs.fl3l4.reserved_count;
+
+               self->aq_hw_rx_fltrs.fl3l4.active_ipv4 |= BIT(n_bit);
+               self->aq_hw_rx_fltrs.fl3l4.reserved_count++;
+               location = n_bit;
+               break;
+       default:
+               break;
+       }
+
+       return location;
+}
+
+void aq_nic_release_filter(struct aq_nic_s *self, enum aq_rx_filter_type type,
+                          u32 location)
+{
+       switch (type) {
+       case aq_rx_filter_ethertype:
+               self->aq_hw_rx_fltrs.fet_reserved_count--;
+               break;
+       case aq_rx_filter_l3l4:
+               self->aq_hw_rx_fltrs.fl3l4.reserved_count--;
+               self->aq_hw_rx_fltrs.fl3l4.active_ipv4 &= ~BIT(location);
+               break;
+       default:
+               break;
+       }
+}
 
 struct aq_fw_s;
 struct aq_vec_s;
 struct aq_ptp_s;
+enum aq_rx_filter_type;
 
 struct aq_nic_cfg_s {
        const struct aq_hw_caps_s *aq_hw_caps;
        u8   active_ipv4;
        u8   active_ipv6:2;
        u8 is_ipv6;
+       u8 reserved_count;
 };
 
 struct aq_hw_rx_fltrs_s {
        u16                   active_filters;
        struct aq_hw_rx_fl2   fl2;
        struct aq_hw_rx_fl3l4 fl3l4;
+       /*filter ether type */
+       u8 fet_reserved_count;
 };
 
 struct aq_nic_s {
 int aq_nic_change_pm_state(struct aq_nic_s *self, pm_message_t *pm_msg);
 int aq_nic_update_interrupt_moderation_settings(struct aq_nic_s *self);
 void aq_nic_shutdown(struct aq_nic_s *self);
-
+u8 aq_nic_reserve_filter(struct aq_nic_s *self, enum aq_rx_filter_type type);
+void aq_nic_release_filter(struct aq_nic_s *self, enum aq_rx_filter_type type,
+                          u32 location);
 #endif /* AQ_NIC_H */
 
  */
 
 #include <linux/ptp_clock_kernel.h>
+#include <linux/ptp_classify.h>
 #include <linux/interrupt.h>
 #include <linux/clocksource.h>
 
 #include "aq_nic.h"
 #include "aq_ptp.h"
 #include "aq_ring.h"
+#include "aq_filters.h"
 
 #define AQ_PTP_TX_TIMEOUT        (HZ *  10)
 
        struct aq_ring_s hwts_rx;
 
        struct ptp_skb_ring skb_ring;
+
+       struct aq_rx_filter_l3l4 udp_filter;
+       struct aq_rx_filter_l2 eth_type_filter;
 };
 
 struct ptp_tm_offset {
        aq_ptp_clock_init(aq_nic);
        mutex_unlock(&aq_nic->fwreq_mutex);
 
+       aq_ptp->eth_type_filter.location =
+                       aq_nic_reserve_filter(aq_nic, aq_rx_filter_ethertype);
+       aq_ptp->udp_filter.location =
+                       aq_nic_reserve_filter(aq_nic, aq_rx_filter_l3l4);
+
        return 0;
 
 err_exit:
        if (!aq_ptp)
                return;
 
+       aq_nic_release_filter(aq_nic, aq_rx_filter_ethertype,
+                             aq_ptp->eth_type_filter.location);
+       aq_nic_release_filter(aq_nic, aq_rx_filter_l3l4,
+                             aq_ptp->udp_filter.location);
        /* disable ptp */
        mutex_lock(&aq_nic->fwreq_mutex);
        aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 0);
 
 
        hw_atl_b0_hw_fl3l4_clear(self, data);
 
-       if (data->cmd) {
+       if (data->cmd & (HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3 |
+                        HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3)) {
                if (!data->is_ipv6) {
                        hw_atl_rpfl3l4_ipv4_dest_addr_set(self,
                                                          location,
                                                         data->ip_src);
                }
        }
-       hw_atl_rpf_l4_dpd_set(self, data->p_dst, location);
-       hw_atl_rpf_l4_spd_set(self, data->p_src, location);
+
+       if (data->cmd & (HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4 |
+                        HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4)) {
+               hw_atl_rpf_l4_dpd_set(self, data->p_dst, location);
+               hw_atl_rpf_l4_spd_set(self, data->p_src, location);
+       }
+
        hw_atl_rpfl3l4_cmd_set(self, location, data->cmd);
 
        return aq_hw_err_from_flags(self);
 
 /* default value of bitfield et_val{f}[f:0] */
 #define HW_ATL_RPF_ET_VALF_DEFAULT 0x0
 
+/* RX l3_l4_en{F} Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l3_l4_en{F}".
+ * Parameter: filter {F} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l3_l4_en_i[0]"
+ */
+
+#define HW_ATL_RPF_L3_REG_CTRL_ADR(filter) (0x00005380 + (filter) * 0x4)
+
+/* RX rpf_l3_sa{D}[1F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l3_sa{D}[1F:0]".
+ * Parameter: location {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l3_sa0_i[31:0]"
+ */
+
+/* Register address for bitfield pif_rpf_l3_sa0_i[31:0] */
+#define HW_ATL_RPF_L3_SRCA_ADR(filter) (0x000053B0 + (filter) * 0x4)
+/* Bitmask for bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_MSKN 0xFFFFFFFFu
+/* Lower bit position of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_SHIFT 0
+/* Width of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_WIDTH 32
+/* Default value of bitfield l3_sa0[1F:0] */
+#define HW_ATL_RPF_L3_SRCA_DEFAULT 0x0
+
+/* RX rpf_l3_da{D}[1F:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "l3_da{D}[1F:0]".
+ * Parameter: location {D} | stride size 0x4 | range [0, 7]
+ * PORT="pif_rpf_l3_da0_i[31:0]"
+ */
+
+ /* Register address for bitfield pif_rpf_l3_da0_i[31:0] */
+#define HW_ATL_RPF_L3_DSTA_ADR(filter) (0x000053B0 + (filter) * 0x4)
+/* Bitmask for bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_MSKN 0xFFFFFFFFu
+/* Lower bit position of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_SHIFT 0
+/* Width of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_WIDTH 32
+/* Default value of bitfield l3_da0[1F:0] */
+#define HW_ATL_RPF_L3_DSTA_DEFAULT 0x0
+
 /* RX l4_sp{D}[F:0] Bitfield Definitions
  * Preprocessor definitions for the bitfield "l4_sp{D}[F:0]".
  * Parameter: srcport {D} | stride size 0x4 | range [0, 7]
 /* default value of bitfield uP Force Interrupt */
 #define HW_ATL_MCP_UP_FORCE_INTERRUPT_DEFAULT 0x0
 
-#define HW_ATL_RX_CTRL_ADDR_BEGIN_FL3L4   0x00005380
-#define HW_ATL_RX_SRCA_ADDR_BEGIN_FL3L4   0x000053B0
-#define HW_ATL_RX_DESTA_ADDR_BEGIN_FL3L4  0x000053D0
-
-#define HW_ATL_RPF_L3_REG_CTRL_ADR(location) (0x00005380 + (location) * 0x4)
-
-/* RX rpf_l3_sa{D}[1F:0] Bitfield Definitions
- * Preprocessor definitions for the bitfield "l3_sa{D}[1F:0]".
- * Parameter: location {D} | stride size 0x4 | range [0, 7]
- * PORT="pif_rpf_l3_sa0_i[31:0]"
- */
-
-/* Register address for bitfield pif_rpf_l3_sa0_i[31:0] */
-#define HW_ATL_RPF_L3_SRCA_ADR(location) (0x000053B0 + (location) * 0x4)
-/* Bitmask for bitfield l3_sa0[1F:0] */
-#define HW_ATL_RPF_L3_SRCA_MSK 0xFFFFFFFFu
-/* Inverted bitmask for bitfield l3_sa0[1F:0] */
-#define HW_ATL_RPF_L3_SRCA_MSKN 0xFFFFFFFFu
-/* Lower bit position of bitfield l3_sa0[1F:0] */
-#define HW_ATL_RPF_L3_SRCA_SHIFT 0
-/* Width of bitfield l3_sa0[1F:0] */
-#define HW_ATL_RPF_L3_SRCA_WIDTH 32
-/* Default value of bitfield l3_sa0[1F:0] */
-#define HW_ATL_RPF_L3_SRCA_DEFAULT 0x0
-
-/* RX rpf_l3_da{D}[1F:0] Bitfield Definitions
- * Preprocessor definitions for the bitfield "l3_da{D}[1F:0]".
- * Parameter: location {D} | stride size 0x4 | range [0, 7]
- * PORT="pif_rpf_l3_da0_i[31:0]"
- */
-
- /* Register address for bitfield pif_rpf_l3_da0_i[31:0] */
-#define HW_ATL_RPF_L3_DSTA_ADR(location) (0x000053B0 + (location) * 0x4)
-/* Bitmask for bitfield l3_da0[1F:0] */
-#define HW_ATL_RPF_L3_DSTA_MSK 0xFFFFFFFFu
-/* Inverted bitmask for bitfield l3_da0[1F:0] */
-#define HW_ATL_RPF_L3_DSTA_MSKN 0xFFFFFFFFu
-/* Lower bit position of bitfield l3_da0[1F:0] */
-#define HW_ATL_RPF_L3_DSTA_SHIFT 0
-/* Width of bitfield l3_da0[1F:0] */
-#define HW_ATL_RPF_L3_DSTA_WIDTH 32
-/* Default value of bitfield l3_da0[1F:0] */
-#define HW_ATL_RPF_L3_DSTA_DEFAULT 0x0
-
 #define HW_ATL_FW_SM_RAM        0x2U
 
 #endif /* HW_ATL_LLH_INTERNAL_H */