net: atlantic: A2: half duplex support
authorIgor Russkikh <irusskikh@marvell.com>
Mon, 22 Jun 2020 14:53:04 +0000 (17:53 +0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 23 Jun 2020 04:10:22 +0000 (21:10 -0700)
This patch adds support for 10M/100M/1G half duplex rates, which are
supported by A2 in additional to full duplex rates supported by A1.

Signed-off-by: Igor Russkikh <irusskikh@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/aquantia/atlantic/aq_common.h
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
drivers/net/ethernet/aquantia/atlantic/aq_nic.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c

index 52ad943..1587528 100644 (file)
 #define AQ_NIC_RATE_1G         BIT(4)
 #define AQ_NIC_RATE_100M       BIT(5)
 #define AQ_NIC_RATE_10M                BIT(6)
+#define AQ_NIC_RATE_1G_HALF    BIT(7)
+#define AQ_NIC_RATE_100M_HALF  BIT(8)
+#define AQ_NIC_RATE_10M_HALF   BIT(9)
 
-#define AQ_NIC_RATE_EEE_10G    BIT(7)
-#define AQ_NIC_RATE_EEE_5G     BIT(8)
-#define AQ_NIC_RATE_EEE_2G5    BIT(9)
-#define AQ_NIC_RATE_EEE_1G     BIT(10)
-#define AQ_NIC_RATE_EEE_100M   BIT(11)
+#define AQ_NIC_RATE_EEE_10G    BIT(10)
+#define AQ_NIC_RATE_EEE_5G     BIT(11)
+#define AQ_NIC_RATE_EEE_2G5    BIT(12)
+#define AQ_NIC_RATE_EEE_1G     BIT(13)
+#define AQ_NIC_RATE_EEE_100M   BIT(14)
 
 #endif /* AQ_COMMON_H */
index ed5b465..1408a52 100644 (file)
@@ -1,7 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * aQuantia Corporation Network Driver
- * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
+/* Atlantic Network Driver
+ *
+ * Copyright (C) 2014-2019 aQuantia Corporation
+ * Copyright (C) 2019-2020 Marvell International Ltd.
  */
 
 /* File aq_hw.h: Declaration of abstract interface for NIC hardware specific
@@ -69,6 +70,7 @@ struct aq_hw_caps_s {
 
 struct aq_hw_link_status_s {
        unsigned int mbps;
+       bool full_duplex;
 };
 
 struct aq_stats_s {
index 4435c63..49528fc 100644 (file)
@@ -939,8 +939,11 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self,
                cmd->base.port = PORT_FIBRE;
        else
                cmd->base.port = PORT_TP;
-       /* This driver supports only 10G capable adapters, so DUPLEX_FULL */
-       cmd->base.duplex = DUPLEX_FULL;
+
+       cmd->base.duplex = DUPLEX_UNKNOWN;
+       if (self->link_status.mbps)
+               cmd->base.duplex = self->link_status.full_duplex ?
+                                  DUPLEX_FULL : DUPLEX_HALF;
        cmd->base.autoneg = self->aq_nic_cfg.is_autoneg;
 
        ethtool_link_ksettings_zero_link_mode(cmd, supported);
@@ -961,14 +964,26 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self,
                ethtool_link_ksettings_add_link_mode(cmd, supported,
                                                     1000baseT_Full);
 
+       if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_1G_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    1000baseT_Half);
+
        if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_100M)
                ethtool_link_ksettings_add_link_mode(cmd, supported,
                                                     100baseT_Full);
 
+       if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_100M_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    100baseT_Half);
+
        if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_10M)
                ethtool_link_ksettings_add_link_mode(cmd, supported,
                                                     10baseT_Full);
 
+       if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_10M_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    10baseT_Half);
+
        if (self->aq_nic_cfg.aq_hw_caps->flow_control) {
                ethtool_link_ksettings_add_link_mode(cmd, supported,
                                                     Pause);
@@ -988,30 +1003,42 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self,
        if (self->aq_nic_cfg.is_autoneg)
                ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
 
-       if (self->aq_nic_cfg.link_speed_msk  & AQ_NIC_RATE_10G)
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10G)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     10000baseT_Full);
 
-       if (self->aq_nic_cfg.link_speed_msk  & AQ_NIC_RATE_5G)
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_5G)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     5000baseT_Full);
 
-       if (self->aq_nic_cfg.link_speed_msk  & AQ_NIC_RATE_2G5)
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_2G5)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     2500baseT_Full);
 
-       if (self->aq_nic_cfg.link_speed_msk  & AQ_NIC_RATE_1G)
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_1G)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     1000baseT_Full);
 
-       if (self->aq_nic_cfg.link_speed_msk  & AQ_NIC_RATE_100M)
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_1G_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    1000baseT_Half);
+
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_100M)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     100baseT_Full);
 
-       if (self->aq_nic_cfg.link_speed_msk  & AQ_NIC_RATE_10M)
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_100M_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    100baseT_Half);
+
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10M)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     10baseT_Full);
 
+       if (self->aq_nic_cfg.link_speed_msk & AQ_NIC_RATE_10M_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    10baseT_Half);
+
        if (self->aq_nic_cfg.fc.cur & AQ_NIC_FC_RX)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     Pause);
@@ -1031,27 +1058,32 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self,
 int aq_nic_set_link_ksettings(struct aq_nic_s *self,
                              const struct ethtool_link_ksettings *cmd)
 {
-       u32 speed = 0U;
+       int fduplex = (cmd->base.duplex == DUPLEX_FULL);
+       u32 speed = cmd->base.speed;
        u32 rate = 0U;
        int err = 0;
 
+       if (!fduplex && speed > SPEED_1000) {
+               err = -EINVAL;
+               goto err_exit;
+       }
+
        if (cmd->base.autoneg == AUTONEG_ENABLE) {
                rate = self->aq_nic_cfg.aq_hw_caps->link_speed_msk;
                self->aq_nic_cfg.is_autoneg = true;
        } else {
-               speed = cmd->base.speed;
-
                switch (speed) {
                case SPEED_10:
-                       rate = AQ_NIC_RATE_10M;
+                       rate = fduplex ? AQ_NIC_RATE_10M : AQ_NIC_RATE_10M_HALF;
                        break;
 
                case SPEED_100:
-                       rate = AQ_NIC_RATE_100M;
+                       rate = fduplex ? AQ_NIC_RATE_100M
+                                      : AQ_NIC_RATE_100M_HALF;
                        break;
 
                case SPEED_1000:
-                       rate = AQ_NIC_RATE_1G;
+                       rate = fduplex ? AQ_NIC_RATE_1G : AQ_NIC_RATE_1G_HALF;
                        break;
 
                case SPEED_2500:
index 73c0f41..1d9dee4 100644 (file)
@@ -704,6 +704,7 @@ int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self)
                        return -EBUSY;
                }
        }
+       link_status->full_duplex = true;
 
        return 0;
 }
index eeedd8c..013676c 100644 (file)
@@ -274,6 +274,7 @@ static int aq_fw2x_update_link_status(struct aq_hw_s *self)
        } else {
                link_status->mbps = 0;
        }
+       link_status->full_duplex = true;
 
        return 0;
 }
index 8df9d4e..239d077 100644 (file)
@@ -64,8 +64,11 @@ const struct aq_hw_caps_s hw_atl2_caps_aqc113 = {
                          AQ_NIC_RATE_5G  |
                          AQ_NIC_RATE_2G5 |
                          AQ_NIC_RATE_1G  |
+                         AQ_NIC_RATE_1G_HALF   |
                          AQ_NIC_RATE_100M      |
-                         AQ_NIC_RATE_10M,
+                         AQ_NIC_RATE_100M_HALF |
+                         AQ_NIC_RATE_10M       |
+                         AQ_NIC_RATE_10M_HALF,
 };
 
 static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self)
index 0ffc33b..d64dfae 100644 (file)
@@ -135,6 +135,10 @@ static void a2_link_speed_mask2fw(u32 speed,
        link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G);
        link_options->rate_100M = !!(speed & AQ_NIC_RATE_100M);
        link_options->rate_10M = !!(speed & AQ_NIC_RATE_10M);
+
+       link_options->rate_1G_hd = !!(speed & AQ_NIC_RATE_1G_HALF);
+       link_options->rate_100M_hd = !!(speed & AQ_NIC_RATE_100M_HALF);
+       link_options->rate_10M_hd = !!(speed & AQ_NIC_RATE_10M_HALF);
 }
 
 static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed)
@@ -202,6 +206,7 @@ static int aq_a2_fw_update_link_status(struct aq_hw_s *self)
        default:
                self->aq_link_status.mbps = 0;
        }
+       self->aq_link_status.full_duplex = link_status.duplex;
 
        return 0;
 }