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 @@
#define AQ_NIC_RATE_1G BIT(4)
#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 */
......@@ -315,6 +315,81 @@ static int aq_ethtool_set_wol(struct net_device *ndev,
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)
{
struct aq_nic_s *aq_nic = netdev_priv(ndev);
......@@ -438,6 +513,8 @@ const struct ethtool_ops aq_ethtool_ops = {
.nway_reset = aq_ethtool_nway_reset,
.get_ringparam = aq_get_ringparam,
.set_ringparam = aq_set_ringparam,
.get_eee = aq_ethtool_get_eee,
.set_eee = aq_ethtool_set_eee,
.get_pauseparam = aq_ethtool_get_pauseparam,
.set_pauseparam = aq_ethtool_set_pauseparam,
.get_rxfh_key_size = aq_ethtool_get_rss_key_size,
......
......@@ -230,6 +230,11 @@ struct aq_fw_ops {
int (*set_power)(struct aq_hw_s *self, unsigned int power_state,
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 */
......@@ -45,6 +45,7 @@ struct aq_nic_cfg_s {
bool is_lro;
u8 tcs;
struct aq_rss_parameters aq_rss;
u32 eee_speeds;
};
#define AQ_NIC_FLAG_STARTED 0x00000004U
......
......@@ -916,5 +916,7 @@ const struct aq_fw_ops aq_fw_1x_ops = {
.update_link_status = hw_atl_utils_mpi_get_link_status,
.update_stats = hw_atl_utils_update_stats,
.set_power = aq_fw1x_set_power,
.set_eee_rate = NULL,
.get_eee_rate = NULL,
.set_flow_control = NULL,
};
......@@ -171,9 +171,22 @@ struct __packed hw_aq_atl_utils_mbox_header {
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 hw_aq_atl_utils_mbox_header header;
struct hw_atl_stats_s stats;
struct hw_aq_info info;
};
/* fw2x */
......
......@@ -40,6 +40,11 @@
#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_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_UTILS_FW2X_MSG_WOL 0x0E
......@@ -115,6 +120,38 @@ static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed)
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)
{
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)
*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,
enum hal_atl_utils_fw_state_e state)
{
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) {
case MPI_INIT:
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);
break;
case MPI_DEINIT:
......@@ -347,6 +397,40 @@ static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
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)
{
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 = {
.update_link_status = aq_fw2x_update_link_status,
.update_stats = aq_fw2x_update_stats,
.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,
};
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