From 288551de45aa39a751bc03e7976919aa896c5093 Mon Sep 17 00:00:00 2001 From: Igor Russkikh Date: Mon, 2 Jul 2018 17:03:37 +0300 Subject: net: aquantia: Implement rx/tx flow control ethtools callback Runtime change of pause frame configuration (rx/tx flow control) via ethtool. Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller --- .../net/ethernet/aquantia/atlantic/aq_ethtool.c | 42 ++++++++++++++++++++++ drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 6 +++- .../aquantia/atlantic/hw_atl/hw_atl_utils.c | 1 + .../aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c | 26 ++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c index 06242154a002..37f8460647ac 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c @@ -285,6 +285,46 @@ static int aq_ethtool_set_coalesce(struct net_device *ndev, return aq_nic_update_interrupt_moderation_settings(aq_nic); } +static void aq_ethtool_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct aq_nic_s *aq_nic = netdev_priv(ndev); + + pause->autoneg = 0; + + if (aq_nic->aq_hw->aq_nic_cfg->flow_control & AQ_NIC_FC_RX) + pause->rx_pause = 1; + if (aq_nic->aq_hw->aq_nic_cfg->flow_control & AQ_NIC_FC_TX) + pause->tx_pause = 1; +} + +static int aq_ethtool_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct aq_nic_s *aq_nic = netdev_priv(ndev); + int err = 0; + + if (!aq_nic->aq_fw_ops->set_flow_control) + return -EOPNOTSUPP; + + if (pause->autoneg == AUTONEG_ENABLE) + return -EOPNOTSUPP; + + if (pause->rx_pause) + aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_RX; + else + aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_RX; + + if (pause->tx_pause) + aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_TX; + else + aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_TX; + + err = aq_nic->aq_fw_ops->set_flow_control(aq_nic->aq_hw); + + return err; +} + static void aq_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *ring) { @@ -352,6 +392,8 @@ const struct ethtool_ops aq_ethtool_ops = { .get_rxfh_indir_size = aq_ethtool_get_rss_indir_size, .get_ringparam = aq_get_ringparam, .set_ringparam = aq_set_ringparam, + .get_pauseparam = aq_ethtool_get_pauseparam, + .set_pauseparam = aq_ethtool_set_pauseparam, .get_rxfh_key_size = aq_ethtool_get_rss_key_size, .get_rxfh = aq_ethtool_get_rss, .get_rxnfc = aq_ethtool_get_rxnfc, diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index e8cf93adc445..21cfb327d791 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -761,10 +761,14 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self, ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full); - if (self->aq_nic_cfg.flow_control) + if (self->aq_nic_cfg.flow_control & AQ_NIC_FC_RX) ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); + if (self->aq_nic_cfg.flow_control & AQ_NIC_FC_TX) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); + if (self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_FIBRE) ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); else diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c index 9d0a96dda8bc..e1feba5787d1 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c @@ -834,4 +834,5 @@ const struct aq_fw_ops aq_fw_1x_ops = { .set_state = hw_atl_utils_mpi_set_state, .update_link_status = hw_atl_utils_mpi_get_link_status, .update_stats = hw_atl_utils_update_stats, + .set_flow_control = NULL, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c index a3e95f076bf0..c1b671e604fe 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c @@ -87,6 +87,19 @@ static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed) return 0; } +static void aq_fw2x_set_mpi_flow_control(struct aq_hw_s *self, u32 *mpi_state) +{ + if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_RX) + *mpi_state |= BIT(CAPS_HI_PAUSE); + else + *mpi_state &= ~BIT(CAPS_HI_PAUSE); + + if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_TX) + *mpi_state |= BIT(CAPS_HI_ASYMMETRIC_PAUSE); + else + *mpi_state &= ~BIT(CAPS_HI_ASYMMETRIC_PAUSE); +} + static int aq_fw2x_set_state(struct aq_hw_s *self, enum hal_atl_utils_fw_state_e state) { @@ -95,6 +108,7 @@ static int aq_fw2x_set_state(struct aq_hw_s *self, switch (state) { case MPI_INIT: mpi_state &= ~BIT(CAPS_HI_LINK_DROP); + aq_fw2x_set_mpi_flow_control(self, &mpi_state); break; case MPI_DEINIT: mpi_state |= BIT(CAPS_HI_LINK_DROP); @@ -201,6 +215,17 @@ static int aq_fw2x_update_stats(struct aq_hw_s *self) return hw_atl_utils_update_stats(self); } +static int aq_fw2x_set_flow_control(struct aq_hw_s *self) +{ + u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + + aq_fw2x_set_mpi_flow_control(self, &mpi_state); + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state); + + return 0; +} + const struct aq_fw_ops aq_fw_2x_ops = { .init = aq_fw2x_init, .deinit = aq_fw2x_deinit, @@ -210,4 +235,5 @@ const struct aq_fw_ops aq_fw_2x_ops = { .set_state = aq_fw2x_set_state, .update_link_status = aq_fw2x_update_link_status, .update_stats = aq_fw2x_update_stats, + .set_flow_control = aq_fw2x_set_flow_control, }; -- cgit v1.2.3