Commit 92ab6407 authored by Yana Esina's avatar Yana Esina Committed by David S. Miller

net: aquantia: implement EEE support

Support of Energy-Efficient Ethernet to aQuantia NIC's via ethtool
(according to the IEEE 802.3az specifications)
Signed-off-by: default avatarYana Esina <yana.esina@aquantia.com>
Signed-off-by: default avatarNikita Danilov <nikita.danilov@aquantia.com>
Tested-by: default avatarNikita Danilov <nikita.danilov@aquantia.com>
Signed-off-by: default avatarIgor Russkikh <igor.russkikh@aquantia.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a0da96c0
...@@ -57,4 +57,9 @@ ...@@ -57,4 +57,9 @@
#define AQ_NIC_RATE_1G BIT(4) #define AQ_NIC_RATE_1G BIT(4)
#define AQ_NIC_RATE_100M BIT(5) #define AQ_NIC_RATE_100M BIT(5)
#define AQ_NIC_RATE_EEE_10G BIT(6)
#define AQ_NIC_RATE_EEE_5G BIT(7)
#define AQ_NIC_RATE_EEE_2GS BIT(8)
#define AQ_NIC_RATE_EEE_1G BIT(9)
#endif /* AQ_COMMON_H */ #endif /* AQ_COMMON_H */
...@@ -315,6 +315,81 @@ static int aq_ethtool_set_wol(struct net_device *ndev, ...@@ -315,6 +315,81 @@ static int aq_ethtool_set_wol(struct net_device *ndev,
return err; return err;
} }
static enum hw_atl_fw2x_rate eee_mask_to_ethtool_mask(u32 speed)
{
u32 rate = 0;
if (speed & AQ_NIC_RATE_EEE_10G)
rate |= SUPPORTED_10000baseT_Full;
if (speed & AQ_NIC_RATE_EEE_2GS)
rate |= SUPPORTED_2500baseX_Full;
if (speed & AQ_NIC_RATE_EEE_1G)
rate |= SUPPORTED_1000baseT_Full;
return rate;
}
static int aq_ethtool_get_eee(struct net_device *ndev, struct ethtool_eee *eee)
{
struct aq_nic_s *aq_nic = netdev_priv(ndev);
u32 rate, supported_rates;
int err = 0;
if (!aq_nic->aq_fw_ops->get_eee_rate)
return -EOPNOTSUPP;
err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate,
&supported_rates);
if (err < 0)
return err;
eee->supported = eee_mask_to_ethtool_mask(supported_rates);
if (aq_nic->aq_nic_cfg.eee_speeds)
eee->advertised = eee->supported;
eee->lp_advertised = eee_mask_to_ethtool_mask(rate);
eee->eee_enabled = !!eee->advertised;
eee->tx_lpi_enabled = eee->eee_enabled;
if (eee->advertised & eee->lp_advertised)
eee->eee_active = true;
return 0;
}
static int aq_ethtool_set_eee(struct net_device *ndev, struct ethtool_eee *eee)
{
struct aq_nic_s *aq_nic = netdev_priv(ndev);
u32 rate, supported_rates;
struct aq_nic_cfg_s *cfg;
int err = 0;
cfg = aq_nic_get_cfg(aq_nic);
if (unlikely(!aq_nic->aq_fw_ops->get_eee_rate ||
!aq_nic->aq_fw_ops->set_eee_rate))
return -EOPNOTSUPP;
err = aq_nic->aq_fw_ops->get_eee_rate(aq_nic->aq_hw, &rate,
&supported_rates);
if (err < 0)
return err;
if (eee->eee_enabled) {
rate = supported_rates;
cfg->eee_speeds = rate;
} else {
rate = 0;
cfg->eee_speeds = 0;
}
return aq_nic->aq_fw_ops->set_eee_rate(aq_nic->aq_hw, rate);
}
static int aq_ethtool_nway_reset(struct net_device *ndev) static int aq_ethtool_nway_reset(struct net_device *ndev)
{ {
struct aq_nic_s *aq_nic = netdev_priv(ndev); struct aq_nic_s *aq_nic = netdev_priv(ndev);
...@@ -438,6 +513,8 @@ const struct ethtool_ops aq_ethtool_ops = { ...@@ -438,6 +513,8 @@ const struct ethtool_ops aq_ethtool_ops = {
.nway_reset = aq_ethtool_nway_reset, .nway_reset = aq_ethtool_nway_reset,
.get_ringparam = aq_get_ringparam, .get_ringparam = aq_get_ringparam,
.set_ringparam = aq_set_ringparam, .set_ringparam = aq_set_ringparam,
.get_eee = aq_ethtool_get_eee,
.set_eee = aq_ethtool_set_eee,
.get_pauseparam = aq_ethtool_get_pauseparam, .get_pauseparam = aq_ethtool_get_pauseparam,
.set_pauseparam = aq_ethtool_set_pauseparam, .set_pauseparam = aq_ethtool_set_pauseparam,
.get_rxfh_key_size = aq_ethtool_get_rss_key_size, .get_rxfh_key_size = aq_ethtool_get_rss_key_size,
......
...@@ -230,6 +230,11 @@ struct aq_fw_ops { ...@@ -230,6 +230,11 @@ struct aq_fw_ops {
int (*set_power)(struct aq_hw_s *self, unsigned int power_state, int (*set_power)(struct aq_hw_s *self, unsigned int power_state,
u8 *mac); u8 *mac);
int (*set_eee_rate)(struct aq_hw_s *self, u32 speed);
int (*get_eee_rate)(struct aq_hw_s *self, u32 *rate,
u32 *supported_rates);
}; };
#endif /* AQ_HW_H */ #endif /* AQ_HW_H */
...@@ -45,6 +45,7 @@ struct aq_nic_cfg_s { ...@@ -45,6 +45,7 @@ struct aq_nic_cfg_s {
bool is_lro; bool is_lro;
u8 tcs; u8 tcs;
struct aq_rss_parameters aq_rss; struct aq_rss_parameters aq_rss;
u32 eee_speeds;
}; };
#define AQ_NIC_FLAG_STARTED 0x00000004U #define AQ_NIC_FLAG_STARTED 0x00000004U
......
...@@ -916,5 +916,7 @@ const struct aq_fw_ops aq_fw_1x_ops = { ...@@ -916,5 +916,7 @@ const struct aq_fw_ops aq_fw_1x_ops = {
.update_link_status = hw_atl_utils_mpi_get_link_status, .update_link_status = hw_atl_utils_mpi_get_link_status,
.update_stats = hw_atl_utils_update_stats, .update_stats = hw_atl_utils_update_stats,
.set_power = aq_fw1x_set_power, .set_power = aq_fw1x_set_power,
.set_eee_rate = NULL,
.get_eee_rate = NULL,
.set_flow_control = NULL, .set_flow_control = NULL,
}; };
...@@ -171,9 +171,22 @@ struct __packed hw_aq_atl_utils_mbox_header { ...@@ -171,9 +171,22 @@ struct __packed hw_aq_atl_utils_mbox_header {
u32 error; u32 error;
}; };
struct __packed hw_aq_info {
u8 reserved[6];
u16 phy_fault_code;
u16 phy_temperature;
u8 cable_len;
u8 reserved1;
u32 cable_diag_data[4];
u8 reserved2[32];
u32 caps_lo;
u32 caps_hi;
};
struct __packed hw_aq_atl_utils_mbox { struct __packed hw_aq_atl_utils_mbox {
struct hw_aq_atl_utils_mbox_header header; struct hw_aq_atl_utils_mbox_header header;
struct hw_atl_stats_s stats; struct hw_atl_stats_s stats;
struct hw_aq_info info;
}; };
/* fw2x */ /* fw2x */
......
...@@ -40,6 +40,11 @@ ...@@ -40,6 +40,11 @@
#define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE) #define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE)
#define HW_ATL_FW2X_CTRL_FORCE_RECONNECT BIT(CTRL_FORCE_RECONNECT) #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)
#define HW_ATL_FW2X_CAP_EEE_2G5_MASK BIT(CAPS_HI_2P5GBASET_FD_EEE)
#define HW_ATL_FW2X_CAP_EEE_5G_MASK BIT(CAPS_HI_5GBASET_FD_EEE)
#define HW_ATL_FW2X_CAP_EEE_10G_MASK BIT(CAPS_HI_10GBASET_FD_EEE)
#define HAL_ATLANTIC_WOL_FILTERS_COUNT 8 #define HAL_ATLANTIC_WOL_FILTERS_COUNT 8
#define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL 0x0E #define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL 0x0E
...@@ -115,6 +120,38 @@ static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed) ...@@ -115,6 +120,38 @@ static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed)
return rate; return rate;
} }
static u32 fw2x_to_eee_mask(u32 speed)
{
u32 rate = 0;
if (speed & HW_ATL_FW2X_CAP_EEE_10G_MASK)
rate |= AQ_NIC_RATE_EEE_10G;
if (speed & HW_ATL_FW2X_CAP_EEE_5G_MASK)
rate |= AQ_NIC_RATE_EEE_5G;
if (speed & HW_ATL_FW2X_CAP_EEE_2G5_MASK)
rate |= AQ_NIC_RATE_EEE_2GS;
if (speed & HW_ATL_FW2X_CAP_EEE_1G_MASK)
rate |= AQ_NIC_RATE_EEE_1G;
return rate;
}
static u32 eee_mask_to_fw2x(u32 speed)
{
u32 rate = 0;
if (speed & AQ_NIC_RATE_EEE_10G)
rate |= HW_ATL_FW2X_CAP_EEE_10G_MASK;
if (speed & AQ_NIC_RATE_EEE_5G)
rate |= HW_ATL_FW2X_CAP_EEE_5G_MASK;
if (speed & AQ_NIC_RATE_EEE_2GS)
rate |= HW_ATL_FW2X_CAP_EEE_2G5_MASK;
if (speed & AQ_NIC_RATE_EEE_1G)
rate |= HW_ATL_FW2X_CAP_EEE_1G_MASK;
return rate;
}
static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed) static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed)
{ {
u32 val = link_speed_mask_2fw2x_ratemask(speed); u32 val = link_speed_mask_2fw2x_ratemask(speed);
...@@ -137,14 +174,27 @@ static void aq_fw2x_set_mpi_flow_control(struct aq_hw_s *self, u32 *mpi_state) ...@@ -137,14 +174,27 @@ static void aq_fw2x_set_mpi_flow_control(struct aq_hw_s *self, u32 *mpi_state)
*mpi_state &= ~BIT(CAPS_HI_ASYMMETRIC_PAUSE); *mpi_state &= ~BIT(CAPS_HI_ASYMMETRIC_PAUSE);
} }
static void aq_fw2x_upd_eee_rate_bits(struct aq_hw_s *self, u32 *mpi_opts,
u32 eee_speeds)
{
*mpi_opts &= ~(HW_ATL_FW2X_CAP_EEE_1G_MASK |
HW_ATL_FW2X_CAP_EEE_2G5_MASK |
HW_ATL_FW2X_CAP_EEE_5G_MASK |
HW_ATL_FW2X_CAP_EEE_10G_MASK);
*mpi_opts |= eee_mask_to_fw2x(eee_speeds);
}
static int aq_fw2x_set_state(struct aq_hw_s *self, static int aq_fw2x_set_state(struct aq_hw_s *self,
enum hal_atl_utils_fw_state_e state) enum hal_atl_utils_fw_state_e state)
{ {
u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
switch (state) { switch (state) {
case MPI_INIT: case MPI_INIT:
mpi_state &= ~BIT(CAPS_HI_LINK_DROP); mpi_state &= ~BIT(CAPS_HI_LINK_DROP);
aq_fw2x_upd_eee_rate_bits(self, &mpi_state, cfg->eee_speeds);
aq_fw2x_set_mpi_flow_control(self, &mpi_state); aq_fw2x_set_mpi_flow_control(self, &mpi_state);
break; break;
case MPI_DEINIT: case MPI_DEINIT:
...@@ -347,6 +397,40 @@ static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state, ...@@ -347,6 +397,40 @@ static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
return err; return err;
} }
static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed)
{
u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
aq_fw2x_upd_eee_rate_bits(self, &mpi_opts, speed);
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
return 0;
}
static int aq_fw2x_get_eee_rate(struct aq_hw_s *self, u32 *rate,
u32 *supported_rates)
{
u32 mpi_state;
u32 caps_hi;
int err = 0;
u32 addr = self->mbox_addr + offsetof(struct hw_aq_atl_utils_mbox, info) +
offsetof(struct hw_aq_info, caps_hi);
err = hw_atl_utils_fw_downld_dwords(self, addr, &caps_hi,
sizeof(caps_hi) / sizeof(u32));
if (err)
return err;
*supported_rates = fw2x_to_eee_mask(caps_hi);
mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR);
*rate = fw2x_to_eee_mask(mpi_state);
return err;
}
static int aq_fw2x_renegotiate(struct aq_hw_s *self) static int aq_fw2x_renegotiate(struct aq_hw_s *self)
{ {
u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
...@@ -380,5 +464,7 @@ const struct aq_fw_ops aq_fw_2x_ops = { ...@@ -380,5 +464,7 @@ const struct aq_fw_ops aq_fw_2x_ops = {
.update_link_status = aq_fw2x_update_link_status, .update_link_status = aq_fw2x_update_link_status,
.update_stats = aq_fw2x_update_stats, .update_stats = aq_fw2x_update_stats,
.set_power = aq_fw2x_set_power, .set_power = aq_fw2x_set_power,
.set_eee_rate = aq_fw2x_set_eee_rate,
.get_eee_rate = aq_fw2x_get_eee_rate,
.set_flow_control = aq_fw2x_set_flow_control, .set_flow_control = aq_fw2x_set_flow_control,
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment