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: Yana Esina <yana.esina@aquantia.com> Signed-off-by: Nikita Danilov <nikita.danilov@aquantia.com> Tested-by: Nikita Danilov <nikita.danilov@aquantia.com> Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
a0da96c08c
commit
92ab64079d
|
@ -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 @@ err_exit:
|
|||
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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue