net: atlantic: loopback tests via private flags
authorIgor Russkikh <irusskikh@marvell.com>
Thu, 7 Nov 2019 22:41:58 +0000 (22:41 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 8 Nov 2019 03:54:43 +0000 (19:54 -0800)
Here we add a number of ethtool private flags
to allow enabling various loopbacks on HW.

Thats useful for verification and bringup works.

Signed-off-by: Igor Russkikh <irusskikh@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/device_drivers/aquantia/atlantic.txt
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
drivers/net/ethernet/aquantia/atlantic/aq_nic.c
drivers/net/ethernet/aquantia/atlantic/aq_nic.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c

index d235cbaeccc65afb4675ec3b8f1109694f3854bb..cf3e88ca885e566085ef882869c47dc04d7a0b2e 100644 (file)
@@ -325,6 +325,31 @@ Supported ethtool options
  Example:
  ethtool -N eth0 flow-type udp4 action 0 loc 32
 
+ Private flags (testing)
+ ---------------------------------
+
+ Atlantic driver supports private flags for hardware custom features:
+
+       $ ethtool --show-priv-flags ethX
+
+       Private flags for ethX:
+       DMASystemLoopback  : off
+       PKTSystemLoopback  : off
+       DMANetworkLoopback : off
+       PHYInternalLoopback: off
+       PHYExternalLoopback: off
+
+ Example:
+
+       $ ethtool --set-priv-flags ethX DMASystemLoopback on
+
+ DMASystemLoopback:   DMA Host loopback.
+ PKTSystemLoopback:   Packet buffer host loopback.
+ DMANetworkLoopback:  Network side loopback on DMA block.
+ PHYInternalLoopback: Internal loopback on Phy.
+ PHYExternalLoopback: External loopback on Phy (with loopback ethernet cable).
+
+
 Command Line Parameters
 =======================
 The following command line parameters are available on atlantic driver:
index 2f877fb46615d2ddd5c8a9a1628a23809f20dbdb..963bf6e6757395fe365937bea96132f748aa5af2 100644 (file)
@@ -92,6 +92,14 @@ static const char aq_ethtool_queue_stat_names[][ETH_GSTRING_LEN] = {
        "Queue[%d] InErrors",
 };
 
+static const char aq_ethtool_priv_flag_names[][ETH_GSTRING_LEN] = {
+       "DMASystemLoopback",
+       "PKTSystemLoopback",
+       "DMANetworkLoopback",
+       "PHYInternalLoopback",
+       "PHYExternalLoopback",
+};
+
 static void aq_ethtool_stats(struct net_device *ndev,
                             struct ethtool_stats *stats, u64 *data)
 {
@@ -137,7 +145,8 @@ static void aq_ethtool_get_strings(struct net_device *ndev,
        struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
        u8 *p = data;
 
-       if (stringset == ETH_SS_STATS) {
+       switch (stringset) {
+       case ETH_SS_STATS:
                memcpy(p, aq_ethtool_stat_names,
                       sizeof(aq_ethtool_stat_names));
                p = p + sizeof(aq_ethtool_stat_names);
@@ -150,6 +159,11 @@ static void aq_ethtool_get_strings(struct net_device *ndev,
                                p += ETH_GSTRING_LEN;
                        }
                }
+               break;
+       case ETH_SS_PRIV_FLAGS:
+               memcpy(p, aq_ethtool_priv_flag_names,
+                      sizeof(aq_ethtool_priv_flag_names));
+               break;
        }
 }
 
@@ -193,6 +207,9 @@ static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset)
                ret = ARRAY_SIZE(aq_ethtool_stat_names) +
                        cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names);
                break;
+       case ETH_SS_PRIV_FLAGS:
+               ret = ARRAY_SIZE(aq_ethtool_priv_flag_names);
+               break;
        default:
                ret = -EOPNOTSUPP;
        }
@@ -650,6 +667,40 @@ static void aq_set_msg_level(struct net_device *ndev, u32 data)
        aq_nic->msg_enable = data;
 }
 
+u32 aq_ethtool_get_priv_flags(struct net_device *ndev)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
+
+       return aq_nic->aq_nic_cfg.priv_flags;
+}
+
+int aq_ethtool_set_priv_flags(struct net_device *ndev, u32 flags)
+{
+       struct aq_nic_s *aq_nic = netdev_priv(ndev);
+       struct aq_nic_cfg_s *cfg;
+       u32 priv_flags;
+
+       cfg = aq_nic_get_cfg(aq_nic);
+       priv_flags = cfg->priv_flags;
+
+       if (flags & ~AQ_PRIV_FLAGS_MASK)
+               return -EOPNOTSUPP;
+
+       cfg->priv_flags = flags;
+
+       if ((priv_flags ^ flags) & BIT(AQ_HW_LOOPBACK_DMA_NET)) {
+               if (netif_running(ndev)) {
+                       dev_close(ndev);
+
+                       dev_open(ndev, NULL);
+               }
+       } else if ((priv_flags ^ flags) & AQ_HW_LOOPBACK_MASK) {
+               aq_nic_set_loopback(aq_nic);
+       }
+
+       return 0;
+}
+
 const struct ethtool_ops aq_ethtool_ops = {
        .get_link            = aq_ethtool_get_link,
        .get_regs_len        = aq_ethtool_get_regs_len,
@@ -676,6 +727,8 @@ const struct ethtool_ops aq_ethtool_ops = {
        .set_msglevel        = aq_set_msg_level,
        .get_sset_count      = aq_ethtool_get_sset_count,
        .get_ethtool_stats   = aq_ethtool_stats,
+       .get_priv_flags      = aq_ethtool_get_priv_flags,
+       .set_priv_flags      = aq_ethtool_set_priv_flags,
        .get_link_ksettings  = aq_ethtool_get_link_ksettings,
        .set_link_ksettings  = aq_ethtool_set_link_ksettings,
        .get_coalesce        = aq_ethtool_get_coalesce,
index 632b5531db4ac4cbc7e5465aeec81e92f2e2dcc3..6d5be5ebeb138c6c0039ce5f08e4bec4fef78bca 100644 (file)
@@ -12,5 +12,6 @@
 #include "aq_common.h"
 
 extern const struct ethtool_ops aq_ethtool_ops;
+#define AQ_PRIV_FLAGS_MASK   (AQ_HW_LOOPBACK_MASK)
 
 #endif /* AQ_ETHTOOL_H */
index 57396e5169391b0942de08f87b6bc80f417c9f25..cc70c606b6ef292fa61cd915942ee88f9ac40091 100644 (file)
@@ -122,6 +122,20 @@ struct aq_stats_s {
 #define AQ_HW_LED_BLINK    0x2U
 #define AQ_HW_LED_DEFAULT  0x0U
 
+enum aq_priv_flags {
+       AQ_HW_LOOPBACK_DMA_SYS,
+       AQ_HW_LOOPBACK_PKT_SYS,
+       AQ_HW_LOOPBACK_DMA_NET,
+       AQ_HW_LOOPBACK_PHYINT_SYS,
+       AQ_HW_LOOPBACK_PHYEXT_SYS,
+};
+
+#define AQ_HW_LOOPBACK_MASK    (BIT(AQ_HW_LOOPBACK_DMA_SYS) |\
+                                BIT(AQ_HW_LOOPBACK_PKT_SYS) |\
+                                BIT(AQ_HW_LOOPBACK_DMA_NET) |\
+                                BIT(AQ_HW_LOOPBACK_PHYINT_SYS) |\
+                                BIT(AQ_HW_LOOPBACK_PHYEXT_SYS))
+
 struct aq_hw_s {
        atomic_t flags;
        u8 rbl_enabled:1;
@@ -280,6 +294,8 @@ struct aq_hw_ops {
                            u64 *timestamp);
 
        int (*hw_set_fc)(struct aq_hw_s *self, u32 fc, u32 tc);
+
+       int (*hw_set_loopback)(struct aq_hw_s *self, u32 mode, bool enable);
 };
 
 struct aq_fw_ops {
@@ -310,6 +326,8 @@ struct aq_fw_ops {
 
        int (*led_control)(struct aq_hw_s *self, u32 mode);
 
+       int (*set_phyloopback)(struct aq_hw_s *self, u32 mode, bool enable);
+
        int (*set_power)(struct aq_hw_s *self, unsigned int power_state,
                         u8 *mac);
 
index 8f83e91f81469769be269aa5aab889553c7b9dbe..5462b7efcf2fcb5fcbc22c6f9b62b3d1098372da 100644 (file)
@@ -406,6 +406,8 @@ int aq_nic_start(struct aq_nic_s *self)
 
        INIT_WORK(&self->service_task, aq_nic_service_task);
 
+       aq_nic_set_loopback(self);
+
        timer_setup(&self->service_timer, aq_nic_service_timer_cb, 0);
        aq_nic_service_timer_cb(&self->service_timer);
 
@@ -625,6 +627,11 @@ int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb)
 
        aq_ring_update_queue_state(ring);
 
+       if (self->aq_nic_cfg.priv_flags & BIT(AQ_HW_LOOPBACK_DMA_NET)) {
+               err = NETDEV_TX_BUSY;
+               goto err_exit;
+       }
+
        /* Above status update may stop the queue. Check this. */
        if (__netif_subqueue_stopped(self->ndev, ring->idx)) {
                err = NETDEV_TX_BUSY;
@@ -973,6 +980,44 @@ u32 aq_nic_get_fw_version(struct aq_nic_s *self)
        return fw_version;
 }
 
+int aq_nic_set_loopback(struct aq_nic_s *self)
+{
+       struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg;
+
+       if (!self->aq_hw_ops->hw_set_loopback ||
+           !self->aq_fw_ops->set_phyloopback)
+               return -ENOTSUPP;
+
+       mutex_lock(&self->fwreq_mutex);
+       self->aq_hw_ops->hw_set_loopback(self->aq_hw,
+                                        AQ_HW_LOOPBACK_DMA_SYS,
+                                        !!(cfg->priv_flags &
+                                           BIT(AQ_HW_LOOPBACK_DMA_SYS)));
+
+       self->aq_hw_ops->hw_set_loopback(self->aq_hw,
+                                        AQ_HW_LOOPBACK_PKT_SYS,
+                                        !!(cfg->priv_flags &
+                                           BIT(AQ_HW_LOOPBACK_PKT_SYS)));
+
+       self->aq_hw_ops->hw_set_loopback(self->aq_hw,
+                                        AQ_HW_LOOPBACK_DMA_NET,
+                                        !!(cfg->priv_flags &
+                                           BIT(AQ_HW_LOOPBACK_DMA_NET)));
+
+       self->aq_fw_ops->set_phyloopback(self->aq_hw,
+                                        AQ_HW_LOOPBACK_PHYINT_SYS,
+                                        !!(cfg->priv_flags &
+                                           BIT(AQ_HW_LOOPBACK_PHYINT_SYS)));
+
+       self->aq_fw_ops->set_phyloopback(self->aq_hw,
+                                        AQ_HW_LOOPBACK_PHYEXT_SYS,
+                                        !!(cfg->priv_flags &
+                                           BIT(AQ_HW_LOOPBACK_PHYEXT_SYS)));
+       mutex_unlock(&self->fwreq_mutex);
+
+       return 0;
+}
+
 int aq_nic_stop(struct aq_nic_s *self)
 {
        struct aq_vec_s *aq_vec = NULL;
index 527273502d54001f4fbfdef409670583dafe9b10..bb4957a31498f2b02aa468300a9c94c24d50566d 100644 (file)
@@ -46,6 +46,7 @@ struct aq_nic_cfg_s {
        bool is_polling;
        bool is_rss;
        bool is_lro;
+       u32 priv_flags;
        u8  tcs;
        struct aq_rss_parameters aq_rss;
        u32 eee_speeds;
@@ -158,6 +159,7 @@ int aq_nic_set_link_ksettings(struct aq_nic_s *self,
                              const struct ethtool_link_ksettings *cmd);
 struct aq_nic_cfg_s *aq_nic_get_cfg(struct aq_nic_s *self);
 u32 aq_nic_get_fw_version(struct aq_nic_s *self);
+int aq_nic_set_loopback(struct aq_nic_s *self);
 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);
index c7297ca03624412e581871e76f821320c6dc7513..1165689af37d6d82f85deb7de5251bc0c2b6510a 100644 (file)
@@ -1427,6 +1427,30 @@ static int hw_atl_b0_hw_vlan_ctrl(struct aq_hw_s *self, bool enable)
        return aq_hw_err_from_flags(self);
 }
 
+static int hw_atl_b0_set_loopback(struct aq_hw_s *self, u32 mode, bool enable)
+{
+       switch (mode) {
+       case AQ_HW_LOOPBACK_DMA_SYS:
+               hw_atl_tpb_tx_dma_sys_lbk_en_set(self, enable);
+               hw_atl_rpb_dma_sys_lbk_set(self, enable);
+               break;
+       case AQ_HW_LOOPBACK_PKT_SYS:
+               hw_atl_tpo_tx_pkt_sys_lbk_en_set(self, enable);
+               hw_atl_rpf_tpo_to_rpf_sys_lbk_set(self, enable);
+               break;
+       case AQ_HW_LOOPBACK_DMA_NET:
+               hw_atl_rpf_vlan_prom_mode_en_set(self, enable);
+               hw_atl_rpfl2promiscuous_mode_en_set(self, enable);
+               hw_atl_tpb_tx_tx_clk_gate_en_set(self, !enable);
+               hw_atl_tpb_tx_dma_net_lbk_en_set(self, enable);
+               hw_atl_rpb_dma_net_lbk_set(self, enable);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 const struct aq_hw_ops hw_atl_ops_b0 = {
        .hw_set_mac_address   = hw_atl_b0_hw_mac_addr_set,
        .hw_init              = hw_atl_b0_hw_init,
@@ -1481,5 +1505,9 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
        .rx_extract_ts           = hw_atl_b0_rx_extract_ts,
        .extract_hwts            = hw_atl_b0_extract_hwts,
        .hw_set_offload          = hw_atl_b0_hw_offload_set,
-       .hw_set_fc                   = hw_atl_b0_set_fc,
+       .hw_get_hw_stats         = hw_atl_utils_get_hw_stats,
+       .hw_get_fw_version       = hw_atl_utils_get_fw_version,
+       .hw_set_offload          = hw_atl_b0_hw_offload_set,
+       .hw_set_loopback         = hw_atl_b0_set_loopback,
+       .hw_set_fc               = hw_atl_b0_set_fc,
 };
index 6cadc9054544f3c4613cac958eac3ba5ad517fc6..d1f68fc1629182038408e289f79d7d5434f572c3 100644 (file)
@@ -563,6 +563,13 @@ void hw_atl_rpb_dma_sys_lbk_set(struct aq_hw_s *aq_hw, u32 dma_sys_lbk)
                            HW_ATL_RPB_DMA_SYS_LBK_SHIFT, dma_sys_lbk);
 }
 
+void hw_atl_rpb_dma_net_lbk_set(struct aq_hw_s *aq_hw, u32 dma_net_lbk)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_RPB_DMA_NET_LBK_ADR,
+                           HW_ATL_RPB_DMA_NET_LBK_MSK,
+                           HW_ATL_RPB_DMA_NET_LBK_SHIFT, dma_net_lbk);
+}
+
 void hw_atl_rpb_rpf_rx_traf_class_mode_set(struct aq_hw_s *aq_hw,
                                           u32 rx_traf_class_mode)
 {
@@ -1341,7 +1348,26 @@ void hw_atl_tpb_tx_dma_sys_lbk_en_set(struct aq_hw_s *aq_hw, u32 tx_dma_sys_lbk_
                            tx_dma_sys_lbk_en);
 }
 
+void hw_atl_tpb_tx_dma_net_lbk_en_set(struct aq_hw_s *aq_hw,
+                                     u32 tx_dma_net_lbk_en)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_TPB_DMA_NET_LBK_ADR,
+                           HW_ATL_TPB_DMA_NET_LBK_MSK,
+                           HW_ATL_TPB_DMA_NET_LBK_SHIFT,
+                           tx_dma_net_lbk_en);
+}
+
+void hw_atl_tpb_tx_tx_clk_gate_en_set(struct aq_hw_s *aq_hw,
+                                     u32 tx_clk_gate_en)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_TPB_TX_CLK_GATE_EN_ADR,
+                           HW_ATL_TPB_TX_CLK_GATE_EN_MSK,
+                           HW_ATL_TPB_TX_CLK_GATE_EN_SHIFT,
+                           tx_clk_gate_en);
+}
+
 void hw_atl_tpb_tx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw,
+
                                            u32 tx_pkt_buff_size_per_tc, u32 buffer)
 {
        aq_hw_write_reg_bit(aq_hw, HW_ATL_TPB_TXBBUF_SIZE_ADR(buffer),
index 5750b0c9cae7204c650120eabccb9c5ab9e2cfd1..62992b23c0e88d6413211356e4b2842c307b2594 100644 (file)
@@ -288,6 +288,9 @@ void hw_atl_reg_glb_cpu_scratch_scp_set(struct aq_hw_s *aq_hw,
 /* set dma system loopback */
 void hw_atl_rpb_dma_sys_lbk_set(struct aq_hw_s *aq_hw, u32 dma_sys_lbk);
 
+/* set dma network loopback */
+void hw_atl_rpb_dma_net_lbk_set(struct aq_hw_s *aq_hw, u32 dma_net_lbk);
+
 /* set rx traffic class mode */
 void hw_atl_rpb_rpf_rx_traf_class_mode_set(struct aq_hw_s *aq_hw,
                                           u32 rx_traf_class_mode);
@@ -629,6 +632,14 @@ void hw_atl_tpb_tx_buff_lo_threshold_per_tc_set(struct aq_hw_s *aq_hw,
 /* set tx dma system loopback enable */
 void hw_atl_tpb_tx_dma_sys_lbk_en_set(struct aq_hw_s *aq_hw, u32 tx_dma_sys_lbk_en);
 
+/* set tx dma network loopback enable */
+void hw_atl_tpb_tx_dma_net_lbk_en_set(struct aq_hw_s *aq_hw,
+                                     u32 tx_dma_net_lbk_en);
+
+/* set tx clock gating enable */
+void hw_atl_tpb_tx_tx_clk_gate_en_set(struct aq_hw_s *aq_hw,
+                                     u32 tx_clk_gate_en);
+
 /* set tx packet buffer size (per tc) */
 void hw_atl_tpb_tx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw,
                                            u32 tx_pkt_buff_size_per_tc,
index ec3bcdcefc4dd8c95f56282e04b21549e2cfbf11..18de2f7b895938a439add1e01a42cea90e9904e5 100644 (file)
 /* default value of bitfield dma_sys_loopback */
 #define HW_ATL_RPB_DMA_SYS_LBK_DEFAULT 0x0
 
+/* rx dma_net_loopback bitfield definitions
+ * preprocessor definitions for the bitfield "dma_net_loopback".
+ * port="pif_rpb_dma_net_lbk_i"
+ */
+
+/* register address for bitfield dma_net_loopback */
+#define HW_ATL_RPB_DMA_NET_LBK_ADR 0x00005000
+/* bitmask for bitfield dma_net_loopback */
+#define HW_ATL_RPB_DMA_NET_LBK_MSK 0x00000010
+/* inverted bitmask for bitfield dma_net_loopback */
+#define HW_ATL_RPB_DMA_NET_LBK_MSKN 0xffffffef
+/* lower bit position of bitfield dma_net_loopback */
+#define HW_ATL_RPB_DMA_NET_LBK_SHIFT 4
+/* width of bitfield dma_net_loopback */
+#define HW_ATL_RPB_DMA_NET_LBK_WIDTH 1
+/* default value of bitfield dma_net_loopback */
+#define HW_ATL_RPB_DMA_NET_LBK_DEFAULT 0x0
+
 /* rx rx_tc_mode bitfield definitions
  * preprocessor definitions for the bitfield "rx_tc_mode".
  * port="pif_rpb_rx_tc_mode_i,pif_rpf_rx_tc_mode_i"
 /* default value of bitfield dma_sys_loopback */
 #define HW_ATL_TPB_DMA_SYS_LBK_DEFAULT 0x0
 
+/* tx dma_net_loopback bitfield definitions
+ * preprocessor definitions for the bitfield "dma_net_loopback".
+ * port="pif_tpb_dma_net_lbk_i"
+ */
+
+/* register address for bitfield dma_net_loopback */
+#define HW_ATL_TPB_DMA_NET_LBK_ADR 0x00007000
+/* bitmask for bitfield dma_net_loopback */
+#define HW_ATL_TPB_DMA_NET_LBK_MSK 0x00000010
+/* inverted bitmask for bitfield dma_net_loopback */
+#define HW_ATL_TPB_DMA_NET_LBK_MSKN 0xffffffef
+/* lower bit position of bitfield dma_net_loopback */
+#define HW_ATL_TPB_DMA_NET_LBK_SHIFT 4
+/* width of bitfield dma_net_loopback */
+#define HW_ATL_TPB_DMA_NET_LBK_WIDTH 1
+/* default value of bitfield dma_net_loopback */
+#define HW_ATL_TPB_DMA_NET_LBK_DEFAULT 0x0
+
 /* tx tx{b}_buf_size[7:0] bitfield definitions
  * preprocessor definitions for the bitfield "tx{b}_buf_size[7:0]".
  * parameter: buffer {b} | stride size 0x10 | range [0, 7]
 /* default value of bitfield tx_scp_ins_en */
 #define HW_ATL_TPB_TX_SCP_INS_EN_DEFAULT 0x0
 
+/* tx tx_clk_gate_en bitfield definitions
+ * preprocessor definitions for the bitfield "tx_clk_gate_en".
+ * port="pif_tpb_clk_gate_en_i"
+ */
+
+/* register address for bitfield tx_clk_gate_en */
+#define HW_ATL_TPB_TX_CLK_GATE_EN_ADR 0x00007900
+/* bitmask for bitfield tx_clk_gate_en */
+#define HW_ATL_TPB_TX_CLK_GATE_EN_MSK 0x00000010
+/* inverted bitmask for bitfield tx_clk_gate_en */
+#define HW_ATL_TPB_TX_CLK_GATE_EN_MSKN 0xffffffef
+/* lower bit position of bitfield tx_clk_gate_en */
+#define HW_ATL_TPB_TX_CLK_GATE_EN_SHIFT 4
+/* width of bitfield tx_clk_gate_en */
+#define HW_ATL_TPB_TX_CLK_GATE_EN_WIDTH 1
+/* default value of bitfield tx_clk_gate_en */
+#define HW_ATL_TPB_TX_CLK_GATE_EN_DEFAULT 0x1
+
 /* tx ipv4_chk_en bitfield definitions
  * preprocessor definitions for the bitfield "ipv4_chk_en".
  * port="pif_tpo_ipv4_chk_en_i"
index 3dbce03c5a942435a83f5c97133da3615b8b5756..feef2b0177b2ffbd0659bc9ad9495796cd577d4f 100644 (file)
@@ -42,6 +42,9 @@
 #define HW_ATL_FW2X_CTRL_PAUSE            BIT(CTRL_PAUSE)
 #define HW_ATL_FW2X_CTRL_TEMPERATURE      BIT(CTRL_TEMPERATURE)
 #define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE)
+#define HW_ATL_FW2X_CTRL_INT_LOOPBACK     BIT(CTRL_INT_LOOPBACK)
+#define HW_ATL_FW2X_CTRL_EXT_LOOPBACK     BIT(CTRL_EXT_LOOPBACK)
+#define HW_ATL_FW2X_CTRL_DOWNSHIFT        BIT(CTRL_DOWNSHIFT)
 #define HW_ATL_FW2X_CTRL_FORCE_RECONNECT  BIT(CTRL_FORCE_RECONNECT)
 
 #define HW_ATL_FW2X_CAP_EEE_1G_MASK      BIT(CAPS_HI_1000BASET_FD_EEE)
@@ -53,6 +56,7 @@
 #define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL  0x0E
 
 #define HW_ATL_FW_VER_LED                0x03010026U
+#define HW_ATL_FW_VER_MEDIA_CONTROL      0x0301005aU
 
 struct __packed fw2x_msg_wol_pattern {
        u8 mask[16];
@@ -539,6 +543,33 @@ static u32 aq_fw2x_get_flow_control(struct aq_hw_s *self, u32 *fcmode)
        return 0;
 }
 
+static int aq_fw2x_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable)
+{
+       u32 mpi_opts;
+
+       switch (mode) {
+       case AQ_HW_LOOPBACK_PHYINT_SYS:
+               mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
+               if (enable)
+                       mpi_opts |= HW_ATL_FW2X_CTRL_INT_LOOPBACK;
+               else
+                       mpi_opts &= ~HW_ATL_FW2X_CTRL_INT_LOOPBACK;
+               aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
+               break;
+       case AQ_HW_LOOPBACK_PHYEXT_SYS:
+               mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
+               if (enable)
+                       mpi_opts |= HW_ATL_FW2X_CTRL_EXT_LOOPBACK;
+               else
+                       mpi_opts &= ~HW_ATL_FW2X_CTRL_EXT_LOOPBACK;
+               aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static u32 aq_fw2x_mbox_get(struct aq_hw_s *self)
 {
        return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_MBOX_ADDR);
@@ -586,4 +617,5 @@ const struct aq_fw_ops aq_fw_2x_ops = {
        .send_fw_request    = aq_fw2x_send_fw_request,
        .enable_ptp         = aq_fw3x_enable_ptp,
        .led_control        = aq_fw2x_led_control,
+       .set_phyloopback    = aq_fw2x_set_phyloopback,
 };