diff options
Diffstat (limited to 'drivers/net/ethernet/aquantia/atlantic')
27 files changed, 4005 insertions, 654 deletions
diff --git a/drivers/net/ethernet/aquantia/atlantic/Makefile b/drivers/net/ethernet/aquantia/atlantic/Makefile index 131cab855be7..6e0a6e234483 100644 --- a/drivers/net/ethernet/aquantia/atlantic/Makefile +++ b/drivers/net/ethernet/aquantia/atlantic/Makefile @@ -4,15 +4,8 @@ # aQuantia Ethernet Controller AQtion Linux Driver # Copyright(c) 2014-2017 aQuantia Corporation. # -# Contact Information: <rdc-drv@aquantia.com> -# aQuantia Corporation, 105 E. Tasman Dr. San Jose, CA 95134, USA -# ################################################################################ -# -# Makefile for the AQtion(tm) Ethernet driver -# - obj-$(CONFIG_AQTION) += atlantic.o atlantic-objs := aq_main.o \ @@ -24,8 +17,11 @@ atlantic-objs := aq_main.o \ aq_ethtool.o \ aq_drvinfo.o \ aq_filters.o \ + aq_phy.o \ hw_atl/hw_atl_a0.o \ hw_atl/hw_atl_b0.o \ hw_atl/hw_atl_utils.o \ hw_atl/hw_atl_utils_fw2x.o \ hw_atl/hw_atl_llh.o + +atlantic-$(CONFIG_PTP_1588_CLOCK) += aq_ptp.o
\ No newline at end of file diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h index 02f1b70c4e25..f0c41f7408e5 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_cfg.h: Definition of configuration parameters and constants. */ @@ -27,7 +27,7 @@ #define AQ_CFG_INTERRUPT_MODERATION_USEC_MAX (0x1FF * 2) -#define AQ_CFG_IRQ_MASK 0x1FFU +#define AQ_CFG_IRQ_MASK 0x3FFU #define AQ_CFG_VECS_MAX 8U #define AQ_CFG_TCS_MAX 8U @@ -70,14 +70,11 @@ /*#define AQ_CFG_MAC_ADDR_PERMANENT {0x30, 0x0E, 0xE3, 0x12, 0x34, 0x56}*/ -#define AQ_NIC_FC_OFF 0U -#define AQ_NIC_FC_TX 1U -#define AQ_NIC_FC_RX 2U -#define AQ_NIC_FC_FULL 3U -#define AQ_NIC_FC_AUTO 4U - #define AQ_CFG_FC_MODE AQ_NIC_FC_FULL +/* Default WOL modes used on initialization */ +#define AQ_CFG_WOL_MODES WAKE_MAGIC + #define AQ_CFG_SPEED_MSK 0xFFFFU /* 0xFFFFU==auto_neg */ #define AQ_CFG_IS_AUTONEG_DEF 1U diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c index 24df132384fb..a1f99bef4a68 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_ethtool.c: Definition of ethertool related functions. */ @@ -9,13 +9,18 @@ #include "aq_ethtool.h" #include "aq_nic.h" #include "aq_vec.h" +#include "aq_ptp.h" #include "aq_filters.h" +#include <linux/ptp_clock_kernel.h> + static void aq_ethtool_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *p) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - u32 regs_count = aq_nic_get_regs_count(aq_nic); + u32 regs_count; + + regs_count = aq_nic_get_regs_count(aq_nic); memset(p, 0, regs_count * sizeof(u32)); aq_nic_get_regs(aq_nic, regs, p); @@ -24,7 +29,9 @@ static void aq_ethtool_get_regs(struct net_device *ndev, static int aq_ethtool_get_regs_len(struct net_device *ndev) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - u32 regs_count = aq_nic_get_regs_count(aq_nic); + u32 regs_count; + + regs_count = aq_nic_get_regs_count(aq_nic); return regs_count * sizeof(u32); } @@ -89,11 +96,21 @@ static const char aq_ethtool_queue_stat_names[][ETH_GSTRING_LEN] = { "Queue[%d] InErrors", }; +static const char aq_ethtool_priv_flag_names[][ETH_GSTRING_LEN] = { + "DMASystemLoopback", + "PKTSystemLoopback", + "DMANetworkLoopback", + "PHYInternalLoopback", + "PHYExternalLoopback", +}; + static void aq_ethtool_stats(struct net_device *ndev, struct ethtool_stats *stats, u64 *data) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; + + cfg = aq_nic_get_cfg(aq_nic); memset(data, 0, (ARRAY_SIZE(aq_ethtool_stat_names) + ARRAY_SIZE(aq_ethtool_queue_stat_names) * @@ -104,11 +121,15 @@ static void aq_ethtool_stats(struct net_device *ndev, static void aq_ethtool_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *drvinfo) { - struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); struct pci_dev *pdev = to_pci_dev(ndev->dev.parent); - u32 firmware_version = aq_nic_get_fw_version(aq_nic); - u32 regs_count = aq_nic_get_regs_count(aq_nic); + struct aq_nic_s *aq_nic = netdev_priv(ndev); + struct aq_nic_cfg_s *cfg; + u32 firmware_version; + u32 regs_count; + + cfg = aq_nic_get_cfg(aq_nic); + firmware_version = aq_nic_get_fw_version(aq_nic); + regs_count = aq_nic_get_regs_count(aq_nic); strlcat(drvinfo->driver, AQ_CFG_DRV_NAME, sizeof(drvinfo->driver)); strlcat(drvinfo->version, AQ_CFG_DRV_VERSION, sizeof(drvinfo->version)); @@ -129,12 +150,15 @@ static void aq_ethtool_get_drvinfo(struct net_device *ndev, static void aq_ethtool_get_strings(struct net_device *ndev, u32 stringset, u8 *data) { - int i, si; struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; u8 *p = data; + int i, si; - if (stringset == ETH_SS_STATS) { + cfg = aq_nic_get_cfg(aq_nic); + + switch (stringset) { + case ETH_SS_STATS: memcpy(p, aq_ethtool_stat_names, sizeof(aq_ethtool_stat_names)); p = p + sizeof(aq_ethtool_stat_names); @@ -147,23 +171,63 @@ static void aq_ethtool_get_strings(struct net_device *ndev, p += ETH_GSTRING_LEN; } } + break; + case ETH_SS_PRIV_FLAGS: + memcpy(p, aq_ethtool_priv_flag_names, + sizeof(aq_ethtool_priv_flag_names)); + break; } } -static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset) +static int aq_ethtool_set_phys_id(struct net_device *ndev, + enum ethtool_phys_id_state state) { + struct aq_nic_s *aq_nic = netdev_priv(ndev); + struct aq_hw_s *hw = aq_nic->aq_hw; int ret = 0; + + if (!aq_nic->aq_fw_ops->led_control) + return -EOPNOTSUPP; + + mutex_lock(&aq_nic->fwreq_mutex); + + switch (state) { + case ETHTOOL_ID_ACTIVE: + ret = aq_nic->aq_fw_ops->led_control(hw, AQ_HW_LED_BLINK | + AQ_HW_LED_BLINK << 2 | AQ_HW_LED_BLINK << 4); + break; + case ETHTOOL_ID_INACTIVE: + ret = aq_nic->aq_fw_ops->led_control(hw, AQ_HW_LED_DEFAULT); + break; + default: + break; + } + + mutex_unlock(&aq_nic->fwreq_mutex); + + return ret; +} + +static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset) +{ struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; + int ret = 0; + + cfg = aq_nic_get_cfg(aq_nic); switch (stringset) { case ETH_SS_STATS: ret = ARRAY_SIZE(aq_ethtool_stat_names) + cfg->vecs * ARRAY_SIZE(aq_ethtool_queue_stat_names); break; + case ETH_SS_PRIV_FLAGS: + ret = ARRAY_SIZE(aq_ethtool_priv_flag_names); + break; default: ret = -EOPNOTSUPP; } + return ret; } @@ -175,7 +239,9 @@ static u32 aq_ethtool_get_rss_indir_size(struct net_device *ndev) static u32 aq_ethtool_get_rss_key_size(struct net_device *ndev) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; + + cfg = aq_nic_get_cfg(aq_nic); return sizeof(cfg->aq_rss.hash_secret_key); } @@ -184,9 +250,11 @@ static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key, u8 *hfunc) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; unsigned int i = 0U; + cfg = aq_nic_get_cfg(aq_nic); + if (hfunc) *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */ if (indir) { @@ -196,6 +264,7 @@ static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key, if (key) memcpy(key, cfg->aq_rss.hash_secret_key, sizeof(cfg->aq_rss.hash_secret_key)); + return 0; } @@ -239,9 +308,11 @@ static int aq_ethtool_get_rxnfc(struct net_device *ndev, u32 *rule_locs) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; int err = 0; + cfg = aq_nic_get_cfg(aq_nic); + switch (cmd->cmd) { case ETHTOOL_GRXRINGS: cmd->data = cfg->vecs; @@ -266,8 +337,8 @@ static int aq_ethtool_get_rxnfc(struct net_device *ndev, static int aq_ethtool_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *cmd) { - int err = 0; struct aq_nic_s *aq_nic = netdev_priv(ndev); + int err = 0; switch (cmd->cmd) { case ETHTOOL_SRXCLSRLINS: @@ -288,7 +359,9 @@ static int aq_ethtool_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; + + cfg = aq_nic_get_cfg(aq_nic); if (cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON || cfg->itr == AQ_CFG_INTERRUPT_MODERATION_AUTO) { @@ -302,6 +375,7 @@ static int aq_ethtool_get_coalesce(struct net_device *ndev, coal->rx_max_coalesced_frames = 1; coal->tx_max_coalesced_frames = 1; } + return 0; } @@ -309,7 +383,9 @@ static int aq_ethtool_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; + + cfg = aq_nic_get_cfg(aq_nic); /* This is not yet supported */ @@ -351,13 +427,12 @@ static void aq_ethtool_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; - wol->supported = WAKE_MAGIC; - wol->wolopts = 0; + cfg = aq_nic_get_cfg(aq_nic); - if (cfg->wol) - wol->wolopts |= WAKE_MAGIC; + wol->supported = AQ_NIC_WOL_MODES; + wol->wolopts = cfg->wol; } static int aq_ethtool_set_wol(struct net_device *ndev, @@ -365,18 +440,50 @@ static int aq_ethtool_set_wol(struct net_device *ndev, { struct pci_dev *pdev = to_pci_dev(ndev->dev.parent); struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; int err = 0; - if (wol->wolopts & WAKE_MAGIC) - cfg->wol |= AQ_NIC_WOL_ENABLED; - else - cfg->wol &= ~AQ_NIC_WOL_ENABLED; - err = device_set_wakeup_enable(&pdev->dev, wol->wolopts); + cfg = aq_nic_get_cfg(aq_nic); + + if (wol->wolopts & ~AQ_NIC_WOL_MODES) + return -EOPNOTSUPP; + + cfg->wol = wol->wolopts; + + err = device_set_wakeup_enable(&pdev->dev, !!cfg->wol); return err; } +static int aq_ethtool_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct aq_nic_s *aq_nic = netdev_priv(ndev); + + ethtool_op_get_ts_info(ndev, info); + + if (!aq_nic->aq_ptp) + return 0; + + info->so_timestamping |= + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->tx_types = BIT(HWTSTAMP_TX_OFF) | + BIT(HWTSTAMP_TX_ON); + + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE); + + info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT); + + info->phc_index = ptp_clock_index(aq_ptp_get_ptp_clock(aq_nic->aq_ptp)); + + return 0; +} + static enum hw_atl_fw2x_rate eee_mask_to_ethtool_mask(u32 speed) { u32 rate = 0; @@ -481,7 +588,7 @@ static void aq_ethtool_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *pause) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - u32 fc = aq_nic->aq_nic_cfg.flow_control; + u32 fc = aq_nic->aq_nic_cfg.fc.req; pause->autoneg = 0; @@ -503,14 +610,14 @@ static int aq_ethtool_set_pauseparam(struct net_device *ndev, return -EOPNOTSUPP; if (pause->rx_pause) - aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_RX; + aq_nic->aq_hw->aq_nic_cfg->fc.req |= AQ_NIC_FC_RX; else - aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_RX; + aq_nic->aq_hw->aq_nic_cfg->fc.req &= ~AQ_NIC_FC_RX; if (pause->tx_pause) - aq_nic->aq_hw->aq_nic_cfg->flow_control |= AQ_NIC_FC_TX; + aq_nic->aq_hw->aq_nic_cfg->fc.req |= AQ_NIC_FC_TX; else - aq_nic->aq_hw->aq_nic_cfg->flow_control &= ~AQ_NIC_FC_TX; + aq_nic->aq_hw->aq_nic_cfg->fc.req &= ~AQ_NIC_FC_TX; mutex_lock(&aq_nic->fwreq_mutex); err = aq_nic->aq_fw_ops->set_flow_control(aq_nic->aq_hw); @@ -523,23 +630,28 @@ static void aq_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *ring) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic); + struct aq_nic_cfg_s *cfg; - ring->rx_pending = aq_nic_cfg->rxds; - ring->tx_pending = aq_nic_cfg->txds; + cfg = aq_nic_get_cfg(aq_nic); + + ring->rx_pending = cfg->rxds; + ring->tx_pending = cfg->txds; - ring->rx_max_pending = aq_nic_cfg->aq_hw_caps->rxds_max; - ring->tx_max_pending = aq_nic_cfg->aq_hw_caps->txds_max; + ring->rx_max_pending = cfg->aq_hw_caps->rxds_max; + ring->tx_max_pending = cfg->aq_hw_caps->txds_max; } static int aq_set_ringparam(struct net_device *ndev, struct ethtool_ringparam *ring) { - int err = 0; - bool ndev_running = false; struct aq_nic_s *aq_nic = netdev_priv(ndev); - struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(aq_nic); - const struct aq_hw_caps_s *hw_caps = aq_nic_cfg->aq_hw_caps; + const struct aq_hw_caps_s *hw_caps; + bool ndev_running = false; + struct aq_nic_cfg_s *cfg; + int err = 0; + + cfg = aq_nic_get_cfg(aq_nic); + hw_caps = cfg->aq_hw_caps; if (ring->rx_mini_pending || ring->rx_jumbo_pending) { err = -EOPNOTSUPP; @@ -553,18 +665,18 @@ static int aq_set_ringparam(struct net_device *ndev, aq_nic_free_vectors(aq_nic); - aq_nic_cfg->rxds = max(ring->rx_pending, hw_caps->rxds_min); - aq_nic_cfg->rxds = min(aq_nic_cfg->rxds, hw_caps->rxds_max); - aq_nic_cfg->rxds = ALIGN(aq_nic_cfg->rxds, AQ_HW_RXD_MULTIPLE); + cfg->rxds = max(ring->rx_pending, hw_caps->rxds_min); + cfg->rxds = min(cfg->rxds, hw_caps->rxds_max); + cfg->rxds = ALIGN(cfg->rxds, AQ_HW_RXD_MULTIPLE); - aq_nic_cfg->txds = max(ring->tx_pending, hw_caps->txds_min); - aq_nic_cfg->txds = min(aq_nic_cfg->txds, hw_caps->txds_max); - aq_nic_cfg->txds = ALIGN(aq_nic_cfg->txds, AQ_HW_TXD_MULTIPLE); + cfg->txds = max(ring->tx_pending, hw_caps->txds_min); + cfg->txds = min(cfg->txds, hw_caps->txds_max); + cfg->txds = ALIGN(cfg->txds, AQ_HW_TXD_MULTIPLE); - for (aq_nic->aq_vecs = 0; aq_nic->aq_vecs < aq_nic_cfg->vecs; + for (aq_nic->aq_vecs = 0; aq_nic->aq_vecs < cfg->vecs; aq_nic->aq_vecs++) { aq_nic->aq_vec[aq_nic->aq_vecs] = - aq_vec_alloc(aq_nic, aq_nic->aq_vecs, aq_nic_cfg); + aq_vec_alloc(aq_nic, aq_nic->aq_vecs, cfg); if (unlikely(!aq_nic->aq_vec[aq_nic->aq_vecs])) { err = -ENOMEM; goto err_exit; @@ -577,12 +689,61 @@ err_exit: return err; } +static u32 aq_get_msg_level(struct net_device *ndev) +{ + struct aq_nic_s *aq_nic = netdev_priv(ndev); + + return aq_nic->msg_enable; +} + +static void aq_set_msg_level(struct net_device *ndev, u32 data) +{ + struct aq_nic_s *aq_nic = netdev_priv(ndev); + + aq_nic->msg_enable = data; +} + +static u32 aq_ethtool_get_priv_flags(struct net_device *ndev) +{ + struct aq_nic_s *aq_nic = netdev_priv(ndev); + + return aq_nic->aq_nic_cfg.priv_flags; +} + +static int aq_ethtool_set_priv_flags(struct net_device *ndev, u32 flags) +{ + struct aq_nic_s *aq_nic = netdev_priv(ndev); + struct aq_nic_cfg_s *cfg; + u32 priv_flags; + + cfg = aq_nic_get_cfg(aq_nic); + priv_flags = cfg->priv_flags; + + if (flags & ~AQ_PRIV_FLAGS_MASK) + return -EOPNOTSUPP; + + cfg->priv_flags = flags; + + if ((priv_flags ^ flags) & BIT(AQ_HW_LOOPBACK_DMA_NET)) { + if (netif_running(ndev)) { + dev_close(ndev); + + dev_open(ndev, NULL); + } + } else if ((priv_flags ^ flags) & AQ_HW_LOOPBACK_MASK) { + aq_nic_set_loopback(aq_nic); + } + + return 0; +} + const struct ethtool_ops aq_ethtool_ops = { .get_link = aq_ethtool_get_link, .get_regs_len = aq_ethtool_get_regs_len, .get_regs = aq_ethtool_get_regs, .get_drvinfo = aq_ethtool_get_drvinfo, .get_strings = aq_ethtool_get_strings, + .set_phys_id = aq_ethtool_set_phys_id, .get_rxfh_indir_size = aq_ethtool_get_rss_indir_size, .get_wol = aq_ethtool_get_wol, .set_wol = aq_ethtool_set_wol, @@ -598,10 +759,15 @@ const struct ethtool_ops aq_ethtool_ops = { .set_rxfh = aq_ethtool_set_rss, .get_rxnfc = aq_ethtool_get_rxnfc, .set_rxnfc = aq_ethtool_set_rxnfc, + .get_msglevel = aq_get_msg_level, + .set_msglevel = aq_set_msg_level, .get_sset_count = aq_ethtool_get_sset_count, .get_ethtool_stats = aq_ethtool_stats, + .get_priv_flags = aq_ethtool_get_priv_flags, + .set_priv_flags = aq_ethtool_set_priv_flags, .get_link_ksettings = aq_ethtool_get_link_ksettings, .set_link_ksettings = aq_ethtool_set_link_ksettings, .get_coalesce = aq_ethtool_get_coalesce, .set_coalesce = aq_ethtool_set_coalesce, + .get_ts_info = aq_ethtool_get_ts_info, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h index 632b5531db4a..6d5be5ebeb13 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.h @@ -12,5 +12,6 @@ #include "aq_common.h" extern const struct ethtool_ops aq_ethtool_ops; +#define AQ_PRIV_FLAGS_MASK (AQ_HW_LOOPBACK_MASK) #endif /* AQ_ETHTOOL_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c index aee827f07c16..6102251bb909 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_filters.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_filters.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (C) 2014-2017 aQuantia Corporation. */ +/* Copyright (C) 2014-2019 aQuantia Corporation. */ /* File aq_filters.c: RX filters related functions. */ @@ -89,12 +89,14 @@ static int aq_check_approve_fl3l4(struct aq_nic_s *aq_nic, struct aq_hw_rx_fltrs_s *rx_fltrs, struct ethtool_rx_flow_spec *fsp) { + u32 last_location = AQ_RX_LAST_LOC_FL3L4 - + aq_nic->aq_hw_rx_fltrs.fl3l4.reserved_count; + if (fsp->location < AQ_RX_FIRST_LOC_FL3L4 || - fsp->location > AQ_RX_LAST_LOC_FL3L4) { + fsp->location > last_location) { netdev_err(aq_nic->ndev, "ethtool: location must be in range [%d, %d]", - AQ_RX_FIRST_LOC_FL3L4, - AQ_RX_LAST_LOC_FL3L4); + AQ_RX_FIRST_LOC_FL3L4, last_location); return -EINVAL; } if (rx_fltrs->fl3l4.is_ipv6 && rx_fltrs->fl3l4.active_ipv4) { @@ -124,12 +126,15 @@ aq_check_approve_fl2(struct aq_nic_s *aq_nic, struct aq_hw_rx_fltrs_s *rx_fltrs, struct ethtool_rx_flow_spec *fsp) { + u32 last_location = AQ_RX_LAST_LOC_FETHERT - + aq_nic->aq_hw_rx_fltrs.fet_reserved_count; + if (fsp->location < AQ_RX_FIRST_LOC_FETHERT || - fsp->location > AQ_RX_LAST_LOC_FETHERT) { + fsp->location > last_location) { netdev_err(aq_nic->ndev, "ethtool: location must be in range [%d, %d]", AQ_RX_FIRST_LOC_FETHERT, - AQ_RX_LAST_LOC_FETHERT); + last_location); return -EINVAL; } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h index 53d7478689a0..cc70c606b6ef 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_hw.h: Declaration of abstract interface for NIC hardware specific @@ -15,6 +15,9 @@ #include "aq_rss.h" #include "hw_atl/hw_atl_utils.h" +#define AQ_HW_MAC_COUNTER_HZ 312500000ll +#define AQ_HW_PHY_COUNTER_HZ 160000000ll + #define AQ_RX_FIRST_LOC_FVLANID 0U #define AQ_RX_LAST_LOC_FVLANID 15U #define AQ_RX_FIRST_LOC_FETHERT 16U @@ -94,6 +97,7 @@ struct aq_stats_s { #define AQ_HW_FLAG_STOPPING 0x00000008U #define AQ_HW_FLAG_RESETTING 0x00000010U #define AQ_HW_FLAG_CLOSING 0x00000020U +#define AQ_HW_PTP_AVAILABLE 0x01000000U #define AQ_HW_LINK_DOWN 0x04000000U #define AQ_HW_FLAG_ERR_UNPLUG 0x40000000U #define AQ_HW_FLAG_ERR_HW 0x80000000U @@ -115,6 +119,23 @@ struct aq_stats_s { #define AQ_HW_MULTICAST_ADDRESS_MAX 32U +#define AQ_HW_LED_BLINK 0x2U +#define AQ_HW_LED_DEFAULT 0x0U + +enum aq_priv_flags { + AQ_HW_LOOPBACK_DMA_SYS, + AQ_HW_LOOPBACK_PKT_SYS, + AQ_HW_LOOPBACK_DMA_NET, + AQ_HW_LOOPBACK_PHYINT_SYS, + AQ_HW_LOOPBACK_PHYEXT_SYS, +}; + +#define AQ_HW_LOOPBACK_MASK (BIT(AQ_HW_LOOPBACK_DMA_SYS) |\ + BIT(AQ_HW_LOOPBACK_PKT_SYS) |\ + BIT(AQ_HW_LOOPBACK_DMA_NET) |\ + BIT(AQ_HW_LOOPBACK_PHYINT_SYS) |\ + BIT(AQ_HW_LOOPBACK_PHYEXT_SYS)) + struct aq_hw_s { atomic_t flags; u8 rbl_enabled:1; @@ -133,8 +154,11 @@ struct aq_hw_s { atomic_t dpc; u32 mbox_addr; u32 rpc_addr; + u32 settings_addr; u32 rpc_tid; struct hw_atl_utils_fw_rpc rpc; + s64 ptp_clk_offset; + u16 phy_id; }; struct aq_ring_s; @@ -235,7 +259,43 @@ struct aq_hw_ops { int (*hw_set_offload)(struct aq_hw_s *self, struct aq_nic_cfg_s *aq_nic_cfg); + int (*hw_tx_tc_mode_get)(struct aq_hw_s *self, u32 *tc_mode); + + int (*hw_rx_tc_mode_get)(struct aq_hw_s *self, u32 *tc_mode); + + int (*hw_ring_hwts_rx_fill)(struct aq_hw_s *self, + struct aq_ring_s *aq_ring); + + int (*hw_ring_hwts_rx_receive)(struct aq_hw_s *self, + struct aq_ring_s *ring); + + void (*hw_get_ptp_ts)(struct aq_hw_s *self, u64 *stamp); + + int (*hw_adj_clock_freq)(struct aq_hw_s *self, s32 delta); + + int (*hw_adj_sys_clock)(struct aq_hw_s *self, s64 delta); + + int (*hw_set_sys_clock)(struct aq_hw_s *self, u64 time, u64 ts); + + int (*hw_ts_to_sys_clock)(struct aq_hw_s *self, u64 ts, u64 *time); + + int (*hw_gpio_pulse)(struct aq_hw_s *self, u32 index, u64 start, + u32 period); + + int (*hw_extts_gpio_enable)(struct aq_hw_s *self, u32 index, + u32 enable); + + int (*hw_get_sync_ts)(struct aq_hw_s *self, u64 *ts); + + u16 (*rx_extract_ts)(struct aq_hw_s *self, u8 *p, unsigned int len, + u64 *timestamp); + + int (*extract_hwts)(struct aq_hw_s *self, u8 *p, unsigned int len, + u64 *timestamp); + int (*hw_set_fc)(struct aq_hw_s *self, u32 fc, u32 tc); + + int (*hw_set_loopback)(struct aq_hw_s *self, u32 mode, bool enable); }; struct aq_fw_ops { @@ -264,9 +324,19 @@ struct aq_fw_ops { int (*set_flow_control)(struct aq_hw_s *self); + int (*led_control)(struct aq_hw_s *self, u32 mode); + + int (*set_phyloopback)(struct aq_hw_s *self, u32 mode, bool enable); + int (*set_power)(struct aq_hw_s *self, unsigned int power_state, u8 *mac); + int (*send_fw_request)(struct aq_hw_s *self, + const struct hw_fw_request_iface *fw_req, + size_t size); + + void (*enable_ptp)(struct aq_hw_s *self, int enable); + int (*set_eee_rate)(struct aq_hw_s *self, u32 speed); int (*get_eee_rate)(struct aq_hw_s *self, u32 *rate, diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c index 9c7a226d81b6..7dbf49adcea6 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c @@ -59,6 +59,7 @@ u64 aq_hw_read_reg64(struct aq_hw_s *hw, u32 reg) u64 value = aq_hw_read_reg(hw, reg); value |= (u64)aq_hw_read_reg(hw, reg + 4) << 32; + return value; } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_main.c b/drivers/net/ethernet/aquantia/atlantic/aq_main.c index bb65dd39f847..538f460a3da7 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_main.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_main.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_main.c: Main file for aQuantia Linux driver. */ @@ -10,10 +10,13 @@ #include "aq_nic.h" #include "aq_pci_func.h" #include "aq_ethtool.h" +#include "aq_ptp.h" #include "aq_filters.h" #include <linux/netdevice.h> #include <linux/module.h> +#include <linux/ip.h> +#include <linux/udp.h> MODULE_LICENSE("GPL v2"); MODULE_VERSION(AQ_CFG_DRV_VERSION); @@ -50,8 +53,8 @@ struct net_device *aq_ndev_alloc(void) static int aq_ndev_open(struct net_device *ndev) { - int err = 0; struct aq_nic_s *aq_nic = netdev_priv(ndev); + int err = 0; err = aq_nic_init(aq_nic); if (err < 0) @@ -71,19 +74,20 @@ static int aq_ndev_open(struct net_device *ndev) err_exit: if (err < 0) - aq_nic_deinit(aq_nic); + aq_nic_deinit(aq_nic, true); + return err; } static int aq_ndev_close(struct net_device *ndev) { - int err = 0; struct aq_nic_s *aq_nic = netdev_priv(ndev); + int err = 0; err = aq_nic_stop(aq_nic); if (err < 0) goto err_exit; - aq_nic_deinit(aq_nic); + aq_nic_deinit(aq_nic, true); err_exit: return err; @@ -93,13 +97,33 @@ static int aq_ndev_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct aq_nic_s *aq_nic = netdev_priv(ndev); + if (unlikely(aq_utils_obj_test(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP))) { + /* Hardware adds the Timestamp for PTPv2 802.AS1 + * and PTPv2 IPv4 UDP. + * We have to push even general 320 port messages to the ptp + * queue explicitly. This is a limitation of current firmware + * and hardware PTP design of the chip. Otherwise ptp stream + * will fail to sync + */ + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) || + unlikely((ip_hdr(skb)->version == 4) && + (ip_hdr(skb)->protocol == IPPROTO_UDP) && + ((udp_hdr(skb)->dest == htons(319)) || + (udp_hdr(skb)->dest == htons(320)))) || + unlikely(eth_hdr(skb)->h_proto == htons(ETH_P_1588))) + return aq_ptp_xmit(aq_nic, skb); + } + + skb_tx_timestamp(skb); return aq_nic_xmit(aq_nic, skb); } static int aq_ndev_change_mtu(struct net_device *ndev, int new_mtu) { struct aq_nic_s *aq_nic = netdev_priv(ndev); - int err = aq_nic_set_mtu(aq_nic, new_mtu + ETH_HLEN); + int err; + + err = aq_nic_set_mtu(aq_nic, new_mtu + ETH_HLEN); if (err < 0) goto err_exit; @@ -112,8 +136,8 @@ err_exit: static int aq_ndev_set_features(struct net_device *ndev, netdev_features_t features) { - bool is_vlan_rx_strip = !!(features & NETIF_F_HW_VLAN_CTAG_RX); bool is_vlan_tx_insert = !!(features & NETIF_F_HW_VLAN_CTAG_TX); + bool is_vlan_rx_strip = !!(features & NETIF_F_HW_VLAN_CTAG_RX); struct aq_nic_s *aq_nic = netdev_priv(ndev); bool need_ndev_restart = false; struct aq_nic_cfg_s *aq_cfg; @@ -197,6 +221,87 @@ static void aq_ndev_set_multicast_settings(struct net_device *ndev) (void)aq_nic_set_multicast_list(aq_nic, ndev); } +static int aq_ndev_config_hwtstamp(struct aq_nic_s *aq_nic, + struct hwtstamp_config *config) +{ + if (config->flags) + return -EINVAL; + + switch (config->tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + break; + default: + return -ERANGE; + } + + switch (config->rx_filter) { + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_NONE: + break; + default: + return -ERANGE; + } + + return aq_ptp_hwtstamp_config_set(aq_nic->aq_ptp, config); +} + +static int aq_ndev_hwtstamp_set(struct aq_nic_s *aq_nic, struct ifreq *ifr) +{ + struct hwtstamp_config config; + int ret_val; + + if (!aq_nic->aq_ptp) + return -EOPNOTSUPP; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + ret_val = aq_ndev_config_hwtstamp(aq_nic, &config); + if (ret_val) + return ret_val; + + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static int aq_ndev_hwtstamp_get(struct aq_nic_s *aq_nic, struct ifreq *ifr) +{ + struct hwtstamp_config config; + + if (!aq_nic->aq_ptp) + return -EOPNOTSUPP; + + aq_ptp_hwtstamp_config_get(aq_nic->aq_ptp, &config); + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static int aq_ndev_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct aq_nic_s *aq_nic = netdev_priv(netdev); + + switch (cmd) { + case SIOCSHWTSTAMP: + return aq_ndev_hwtstamp_set(aq_nic, ifr); + + case SIOCGHWTSTAMP: + return aq_ndev_hwtstamp_get(aq_nic, ifr); + } + + return -EOPNOTSUPP; +} + static int aq_ndo_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid) { @@ -234,6 +339,7 @@ static const struct net_device_ops aq_ndev_ops = { .ndo_change_mtu = aq_ndev_change_mtu, .ndo_set_mac_address = aq_ndev_set_mac_address, .ndo_set_features = aq_ndev_set_features, + .ndo_do_ioctl = aq_ndev_ioctl, .ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 137c1de4c6ec..a17a4da7bc15 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_nic.c: Definition of common code for NIC. */ @@ -12,6 +12,9 @@ #include "aq_hw.h" #include "aq_pci_func.h" #include "aq_main.h" +#include "aq_phy.h" +#include "aq_ptp.h" +#include "aq_filters.h" #include <linux/moduleparam.h> #include <linux/netdevice.h> @@ -38,10 +41,6 @@ static void aq_nic_update_ndev_stats(struct aq_nic_s *self); static void aq_nic_rss_init(struct aq_nic_s *self, unsigned int num_rss_queues) { - struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg; - struct aq_rss_parameters *rss_params = &cfg->aq_rss; - int i = 0; - static u8 rss_key[AQ_CFG_RSS_HASHKEY_SIZE] = { 0x1e, 0xad, 0x71, 0x87, 0x65, 0xfc, 0x26, 0x7d, 0x0d, 0x45, 0x67, 0x74, 0xcd, 0x06, 0x1a, 0x18, @@ -49,6 +48,11 @@ static void aq_nic_rss_init(struct aq_nic_s *self, unsigned int num_rss_queues) 0x19, 0x13, 0x4b, 0xa9, 0xd0, 0x3e, 0xfe, 0x70, 0x25, 0x03, 0xab, 0x50, 0x6a, 0x8b, 0x82, 0x0c }; + struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg; + struct aq_rss_parameters *rss_params; + int i = 0; + + rss_params = &cfg->aq_rss; rss_params->hash_secret_key_size = sizeof(rss_key); memcpy(rss_params->hash_secret_key, rss_key, sizeof(rss_key)); @@ -75,7 +79,8 @@ void aq_nic_cfg_start(struct aq_nic_s *self) cfg->is_rss = AQ_CFG_IS_RSS_DEF; cfg->num_rss_queues = AQ_CFG_NUM_RSS_QUEUES_DEF; cfg->aq_rss.base_cpu_number = AQ_CFG_RSS_BASE_CPU_NUM_DEF; - cfg->flow_control = AQ_CFG_FC_MODE; + cfg->fc.req = AQ_CFG_FC_MODE; + cfg->wol = AQ_CFG_WOL_MODES; cfg->mtu = AQ_CFG_MTU_DEF; cfg->link_speed_msk = AQ_CFG_SPEED_MSK; @@ -139,18 +144,27 @@ static int aq_nic_update_link_status(struct aq_nic_s *self) if (err) return err; + if (self->aq_fw_ops->get_flow_control) + self->aq_fw_ops->get_flow_control(self->aq_hw, &fc); + self->aq_nic_cfg.fc.cur = fc; + if (self->link_status.mbps != self->aq_hw->aq_link_status.mbps) { - pr_info("%s: link change old %d new %d\n", - AQ_CFG_DRV_NAME, self->link_status.mbps, - self->aq_hw->aq_link_status.mbps); + netdev_info(self->ndev, "%s: link change old %d new %d\n", + AQ_CFG_DRV_NAME, self->link_status.mbps, + self->aq_hw->aq_link_status.mbps); aq_nic_update_interrupt_moderation_settings(self); + if (self->aq_ptp) { + aq_ptp_clock_init(self); + aq_ptp_tm_offset_set(self, + self->aq_hw->aq_link_status.mbps); + aq_ptp_link_change(self); + } + /* Driver has to update flow control settings on RX block * on any link event. * We should query FW whether it negotiated FC. */ - if (self->aq_fw_ops->get_flow_control) - self->aq_fw_ops->get_flow_control(self->aq_hw, &fc); if (self->aq_hw_ops->hw_set_fc) self->aq_hw_ops->hw_set_fc(self->aq_hw, fc, 0); } @@ -169,6 +183,7 @@ static int aq_nic_update_link_status(struct aq_nic_s *self) netif_tx_disable(self->ndev); aq_utils_obj_set(&self->flags, AQ_NIC_LINK_DOWN); } + return 0; } @@ -183,6 +198,7 @@ static irqreturn_t aq_linkstate_threaded_isr(int irq, void *private) self->aq_hw_ops->hw_irq_enable(self->aq_hw, BIT(self->aq_nic_cfg.link_irq_vec)); + return IRQ_HANDLED; } @@ -192,6 +208,8 @@ static void aq_nic_service_task(struct work_struct *work) service_task); int err; + aq_ptp_service_task(self); + if (aq_utils_obj_test(&self->flags, AQ_NIC_FLAGS_IS_NOT_READY)) return; @@ -211,7 +229,8 @@ static void aq_nic_service_timer_cb(struct timer_list *t) { struct aq_nic_s *self = from_timer(self, t, service_timer); - mod_timer(&self->service_timer, jiffies + AQ_CFG_SERVICE_TIMER_INTERVAL); + mod_timer(&self->service_timer, + jiffies + AQ_CFG_SERVICE_TIMER_INTERVAL); aq_ndev_schedule_work(&self->service_task); } @@ -290,9 +309,11 @@ void aq_nic_ndev_init(struct aq_nic_s *self) self->ndev->vlan_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_RXHASH | NETIF_F_SG | NETIF_F_LRO | NETIF_F_TSO; + self->ndev->gso_partial_features = NETIF_F_GSO_UDP_L4; self->ndev->priv_flags = aq_hw_caps->hw_priv_flags; self->ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + self->msg_enable = NETIF_MSG_DRV | NETIF_MSG_LINK; self->ndev->mtu = aq_nic_cfg->mtu - ETH_HLEN; self->ndev->max_mtu = aq_hw_caps->mtu - ETH_FCS_LEN - ETH_HLEN; @@ -312,8 +333,8 @@ struct net_device *aq_nic_get_ndev(struct aq_nic_s *self) int aq_nic_init(struct aq_nic_s *self) { struct aq_vec_s *aq_vec = NULL; - int err = 0; unsigned int i = 0U; + int err = 0; self->power_state = AQ_HW_POWER_STATE_D0; mutex_lock(&self->fwreq_mutex); @@ -327,10 +348,27 @@ int aq_nic_init(struct aq_nic_s *self) if (err < 0) goto err_exit; + if (self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) { + self->aq_hw->phy_id = HW_ATL_PHY_ID_MAX; + err = aq_phy_init(self->aq_hw); + } + for (i = 0U, aq_vec = self->aq_vec[0]; self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) aq_vec_init(aq_vec, self->aq_hw_ops, self->aq_hw); + err = aq_ptp_init(self, self->irqvecs - 1); + if (err < 0) + goto err_exit; + + err = aq_ptp_ring_alloc(self); + if (err < 0) + goto err_exit; + + err = aq_ptp_ring_init(self); + if (err < 0) + goto err_exit; + netif_carrier_off(self->ndev); err_exit: @@ -340,8 +378,8 @@ err_exit: int aq_nic_start(struct aq_nic_s *self) { struct aq_vec_s *aq_vec = NULL; - int err = 0; unsigned int i = 0U; + int err = 0; err = self->aq_hw_ops->hw_multicast_list_set(self->aq_hw, self->mc_list.ar, @@ -361,6 +399,10 @@ int aq_nic_start(struct aq_nic_s *self) goto err_exit; } + err = aq_ptp_ring_start(self); + if (err < 0) + goto err_exit; + err = self->aq_hw_ops->hw_start(self->aq_hw); if (err < 0) goto err_exit; @@ -371,6 +413,8 @@ int aq_nic_start(struct aq_nic_s *self) INIT_WORK(&self->service_task, aq_nic_service_task); + aq_nic_set_loopback(self); + timer_setup(&self->service_timer, aq_nic_service_timer_cb, 0); aq_nic_service_timer_cb(&self->service_timer); @@ -388,6 +432,10 @@ int aq_nic_start(struct aq_nic_s *self) goto err_exit; } + err = aq_ptp_irq_alloc(self); + if (err < 0) + goto err_exit; + if (self->aq_nic_cfg.link_irq_vec) { int irqvec = pci_irq_vector(self->pdev, self->aq_nic_cfg.link_irq_vec); @@ -420,30 +468,48 @@ err_exit: return err; } -static unsigned int aq_nic_map_skb(struct aq_nic_s *self, - struct sk_buff *skb, - struct aq_ring_s *ring) +unsigned int aq_nic_map_skb(struct aq_nic_s *self, struct sk_buff *skb, + struct aq_ring_s *ring) { - unsigned int ret = 0U; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; - unsigned int frag_count = 0U; - unsigned int dx = ring->sw_tail; struct aq_ring_buff_s *first = NULL; - struct aq_ring_buff_s *dx_buff = &ring->buff_ring[dx]; + u8 ipver = ip_hdr(skb)->version; + struct aq_ring_buff_s *dx_buff; bool need_context_tag = false; + unsigned int frag_count = 0U; + unsigned int ret = 0U; + unsigned int dx; + u8 l4proto = 0; + + if (ipver == 4) + l4proto = ip_hdr(skb)->protocol; + else if (ipver == 6) + l4proto = ipv6_hdr(skb)->nexthdr; + dx = ring->sw_tail; + dx_buff = &ring->buff_ring[dx]; dx_buff->flags = 0U; if (unlikely(skb_is_gso(skb))) { dx_buff->mss = skb_shinfo(skb)->gso_size; - dx_buff->is_gso = 1U; + if (l4proto == IPPROTO_TCP) { + dx_buff->is_gso_tcp = 1U; + dx_buff->len_l4 = tcp_hdrlen(skb); + } else if (l4proto == IPPROTO_UDP) { + dx_buff->is_gso_udp = 1U; + dx_buff->len_l4 = sizeof(struct udphdr); + /* UDP GSO Hardware does not replace packet length. */ + udp_hdr(skb)->len = htons(dx_buff->mss + + dx_buff->len_l4); + } else { + WARN_ONCE(true, "Bad GSO mode"); + goto exit; + } dx_buff->len_pkt = skb->len; dx_buff->len_l2 = ETH_HLEN; - dx_buff->len_l3 = ip_hdrlen(skb); - dx_buff->len_l4 = tcp_hdrlen(skb); + dx_buff->len_l3 = skb_network_header_len(skb); dx_buff->eop_index = 0xffffU; - dx_buff->is_ipv6 = - (ip_hdr(skb)->version == 6) ? 1U : 0U; + dx_buff->is_ipv6 = (ipver == 6); need_context_tag = true; } @@ -477,24 +543,9 @@ static unsigned int aq_nic_map_skb(struct aq_nic_s *self, ++ret; if (skb->ip_summed == CHECKSUM_PARTIAL) { - dx_buff->is_ip_cso = (htons(ETH_P_IP) == skb->protocol) ? - 1U : 0U; - - if (ip_hdr(skb)->version == 4) { - dx_buff->is_tcp_cso = - (ip_hdr(skb)->protocol == IPPROTO_TCP) ? - 1U : 0U; - dx_buff->is_udp_cso = - (ip_hdr(skb)->protocol == IPPROTO_UDP) ? - 1U : 0U; - } else if (ip_hdr(skb)->version == 6) { - dx_buff->is_tcp_cso = - (ipv6_hdr(skb)->nexthdr == NEXTHDR_TCP) ? - 1U : 0U; - dx_buff->is_udp_cso = - (ipv6_hdr(skb)->nexthdr == NEXTHDR_UDP) ? - 1U : 0U; - } + dx_buff->is_ip_cso = (htons(ETH_P_IP) == skb->protocol); + dx_buff->is_tcp_cso = (l4proto == IPPROTO_TCP); + dx_buff->is_udp_cso = (l4proto == IPPROTO_UDP); } for (; nr_frags--; ++frag_count) { @@ -549,7 +600,8 @@ mapping_error: --ret, dx = aq_ring_next_dx(ring, dx)) { dx_buff = &ring->buff_ring[dx]; - if (!dx_buff->is_gso && !dx_buff->is_vlan && dx_buff->pa) { + if (!(dx_buff->is_gso_tcp || dx_buff->is_gso_udp) && + !dx_buff->is_vlan && dx_buff->pa) { if (unlikely(dx_buff->is_sop)) { dma_unmap_single(aq_nic_get_dev(self), dx_buff->pa, @@ -570,11 +622,11 @@ exit: int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb) { + unsigned int vec = skb->queue_mapping % self->aq_nic_cfg.vecs; struct aq_ring_s *ring = NULL; unsigned int frags = 0U; - unsigned int vec = skb->queue_mapping % self->aq_nic_cfg.vecs; - unsigned int tc = 0U; int err = NETDEV_TX_OK; + unsigned int tc = 0U; frags = skb_shinfo(skb)->nr_frags + 1; @@ -587,6 +639,11 @@ int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb) aq_ring_update_queue_state(ring); + if (self->aq_nic_cfg.priv_flags & BIT(AQ_HW_LOOPBACK_DMA_NET)) { + err = NETDEV_TX_BUSY; + goto err_exit; + } + /* Above status update may stop the queue. Check this. */ if (__netif_subqueue_stopped(self->ndev, ring->idx)) { err = NETDEV_TX_BUSY; @@ -667,6 +724,7 @@ int aq_nic_set_multicast_list(struct aq_nic_s *self, struct net_device *ndev) if (err < 0) return err; } + return aq_nic_set_packet_filter(self, packet_filter); } @@ -711,10 +769,10 @@ int aq_nic_get_regs_count(struct aq_nic_s *self) void aq_nic_get_stats(struct aq_nic_s *self, u64 *data) { - unsigned int i = 0U; - unsigned int count = 0U; struct aq_vec_s *aq_vec = NULL; struct aq_stats_s *stats; + unsigned int count = 0U; + unsigned int i = 0U; if (self->aq_fw_ops->update_stats) { mutex_lock(&self->fwreq_mutex); @@ -764,8 +822,8 @@ err_exit:; static void aq_nic_update_ndev_stats(struct aq_nic_s *self) { - struct net_device *ndev = self->ndev; struct aq_stats_s *stats = self->aq_hw_ops->hw_get_hw_stats(self->aq_hw); + struct net_device *ndev = self->ndev; ndev->stats.rx_packets = stats->dma_pkt_rc; ndev->stats.rx_bytes = stats->dma_oct_rc; @@ -810,9 +868,12 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self, ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Full); - if (self->aq_nic_cfg.aq_hw_caps->flow_control) + if (self->aq_nic_cfg.aq_hw_caps->flow_control) { ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); + ethtool_link_ksettings_add_link_mode(cmd, supported, + Asym_Pause); + } ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); @@ -846,13 +907,13 @@ 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 & AQ_NIC_FC_RX) + if (self->aq_nic_cfg.fc.cur & AQ_NIC_FC_RX) ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); /* Asym is when either RX or TX, but not both */ - if (!!(self->aq_nic_cfg.flow_control & AQ_NIC_FC_TX) ^ - !!(self->aq_nic_cfg.flow_control & AQ_NIC_FC_RX)) + if (!!(self->aq_nic_cfg.fc.cur & AQ_NIC_FC_TX) ^ + !!(self->aq_nic_cfg.fc.cur & AQ_NIC_FC_RX)) ethtool_link_ksettings_add_link_mode(cmd, advertising, Asym_Pause); @@ -935,6 +996,44 @@ u32 aq_nic_get_fw_version(struct aq_nic_s *self) return fw_version; } +int aq_nic_set_loopback(struct aq_nic_s *self) +{ + struct aq_nic_cfg_s *cfg = &self->aq_nic_cfg; + + if (!self->aq_hw_ops->hw_set_loopback || + !self->aq_fw_ops->set_phyloopback) + return -ENOTSUPP; + + mutex_lock(&self->fwreq_mutex); + self->aq_hw_ops->hw_set_loopback(self->aq_hw, + AQ_HW_LOOPBACK_DMA_SYS, + !!(cfg->priv_flags & + BIT(AQ_HW_LOOPBACK_DMA_SYS))); + + self->aq_hw_ops->hw_set_loopback(self->aq_hw, + AQ_HW_LOOPBACK_PKT_SYS, + !!(cfg->priv_flags & + BIT(AQ_HW_LOOPBACK_PKT_SYS))); + + self->aq_hw_ops->hw_set_loopback(self->aq_hw, + AQ_HW_LOOPBACK_DMA_NET, + !!(cfg->priv_flags & + BIT(AQ_HW_LOOPBACK_DMA_NET))); + + self->aq_fw_ops->set_phyloopback(self->aq_hw, + AQ_HW_LOOPBACK_PHYINT_SYS, + !!(cfg->priv_flags & + BIT(AQ_HW_LOOPBACK_PHYINT_SYS))); + + self->aq_fw_ops->set_phyloopback(self->aq_hw, + AQ_HW_LOOPBACK_PHYEXT_SYS, + !!(cfg->priv_flags & + BIT(AQ_HW_LOOPBACK_PHYEXT_SYS))); + mutex_unlock(&self->fwreq_mutex); + + return 0; +} + int aq_nic_stop(struct aq_nic_s *self) { struct aq_vec_s *aq_vec = NULL; @@ -953,14 +1052,31 @@ int aq_nic_stop(struct aq_nic_s *self) else aq_pci_func_free_irqs(self); + aq_ptp_irq_free(self); + for (i = 0U, aq_vec = self->aq_vec[0]; self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) aq_vec_stop(aq_vec); + aq_ptp_ring_stop(self); + return self->aq_hw_ops->hw_stop(self->aq_hw); } -void aq_nic_deinit(struct aq_nic_s *self) +void aq_nic_set_power(struct aq_nic_s *self) +{ + if (self->power_state != AQ_HW_POWER_STATE_D0 || + self->aq_hw->aq_nic_cfg->wol) + if (likely(self->aq_fw_ops->set_power)) { + mutex_lock(&self->fwreq_mutex); + self->aq_fw_ops->set_power(self->aq_hw, + self->power_state, + self->ndev->dev_addr); + mutex_unlock(&self->fwreq_mutex); + } +} + +void aq_nic_deinit(struct aq_nic_s *self, bool link_down) { struct aq_vec_s *aq_vec = NULL; unsigned int i = 0U; @@ -972,23 +1088,17 @@ void aq_nic_deinit(struct aq_nic_s *self) self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) aq_vec_deinit(aq_vec); - if (likely(self->aq_fw_ops->deinit)) { + aq_ptp_unregister(self); + aq_ptp_ring_deinit(self); + aq_ptp_ring_free(self); + aq_ptp_free(self); + + if (likely(self->aq_fw_ops->deinit) && link_down) { mutex_lock(&self->fwreq_mutex); self->aq_fw_ops->deinit(self->aq_hw); mutex_unlock(&self->fwreq_mutex); } - if (self->power_state != AQ_HW_POWER_STATE_D0 || - self->aq_hw->aq_nic_cfg->wol) - if (likely(self->aq_fw_ops->set_power)) { - mutex_lock(&self->fwreq_mutex); - self->aq_fw_ops->set_power(self->aq_hw, - self->power_state, - self->ndev->dev_addr); - mutex_unlock(&self->fwreq_mutex); - } - - err_exit:; } @@ -1009,44 +1119,6 @@ void aq_nic_free_vectors(struct aq_nic_s *self) err_exit:; } -int aq_nic_change_pm_state(struct aq_nic_s *self, pm_message_t *pm_msg) -{ - int err = 0; - - if (!netif_running(self->ndev)) { - err = 0; - goto out; - } - rtnl_lock(); - if (pm_msg->event & PM_EVENT_SLEEP || pm_msg->event & PM_EVENT_FREEZE) { - self->power_state = AQ_HW_POWER_STATE_D3; - netif_device_detach(self->ndev); - netif_tx_stop_all_queues(self->ndev); - - err = aq_nic_stop(self); - if (err < 0) - goto err_exit; - - aq_nic_deinit(self); - } else { - err = aq_nic_init(self); - if (err < 0) - goto err_exit; - - err = aq_nic_start(self); - if (err < 0) - goto err_exit; - - netif_device_attach(self->ndev); - netif_tx_start_all_queues(self->ndev); - } - -err_exit: - rtnl_unlock(); -out: - return err; -} - void aq_nic_shutdown(struct aq_nic_s *self) { int err = 0; @@ -1063,8 +1135,52 @@ void aq_nic_shutdown(struct aq_nic_s *self) if (err < 0) goto err_exit; } - aq_nic_deinit(self); + aq_nic_deinit(self, !self->aq_hw->aq_nic_cfg->wol); + aq_nic_set_power(self); err_exit: rtnl_unlock(); } + +u8 aq_nic_reserve_filter(struct aq_nic_s *self, enum aq_rx_filter_type type) +{ + u8 location = 0xFF; + u32 fltr_cnt; + u32 n_bit; + + switch (type) { + case aq_rx_filter_ethertype: + location = AQ_RX_LAST_LOC_FETHERT - AQ_RX_FIRST_LOC_FETHERT - + self->aq_hw_rx_fltrs.fet_reserved_count; + self->aq_hw_rx_fltrs.fet_reserved_count++; + break; + case aq_rx_filter_l3l4: + fltr_cnt = AQ_RX_LAST_LOC_FL3L4 - AQ_RX_FIRST_LOC_FL3L4; + n_bit = fltr_cnt - self->aq_hw_rx_fltrs.fl3l4.reserved_count; + + self->aq_hw_rx_fltrs.fl3l4.active_ipv4 |= BIT(n_bit); + self->aq_hw_rx_fltrs.fl3l4.reserved_count++; + location = n_bit; + break; + default: + break; + } + + return location; +} + +void aq_nic_release_filter(struct aq_nic_s *self, enum aq_rx_filter_type type, + u32 location) +{ + switch (type) { + case aq_rx_filter_ethertype: + self->aq_hw_rx_fltrs.fet_reserved_count--; + break; + case aq_rx_filter_l3l4: + self->aq_hw_rx_fltrs.fl3l4.reserved_count--; + self->aq_hw_rx_fltrs.fl3l4.active_ipv4 &= ~BIT(location); + break; + default: + break; + } +} diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h index 255b54a6ae07..a752f8bb4b08 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_nic.h: Declaration of common code for NIC. */ @@ -17,6 +17,20 @@ struct aq_ring_s; struct aq_hw_ops; struct aq_fw_s; struct aq_vec_s; +struct aq_ptp_s; +enum aq_rx_filter_type; + +enum aq_fc_mode { + AQ_NIC_FC_OFF = 0, + AQ_NIC_FC_TX, + AQ_NIC_FC_RX, + AQ_NIC_FC_FULL, +}; + +struct aq_fc_info { + enum aq_fc_mode req; + enum aq_fc_mode cur; +}; struct aq_nic_cfg_s { const struct aq_hw_caps_s *aq_hw_caps; @@ -32,7 +46,7 @@ struct aq_nic_cfg_s { u32 rxpageorder; u32 num_rss_queues; u32 mtu; - u32 flow_control; + struct aq_fc_info fc; u32 link_speed_msk; u32 wol; u8 is_vlan_rx_strip; @@ -44,6 +58,7 @@ struct aq_nic_cfg_s { bool is_polling; bool is_rss; bool is_lro; + u32 priv_flags; u8 tcs; struct aq_rss_parameters aq_rss; u32 eee_speeds; @@ -53,11 +68,13 @@ struct aq_nic_cfg_s { #define AQ_NIC_FLAG_STOPPING 0x00000008U #define AQ_NIC_FLAG_RESETTING 0x00000010U #define AQ_NIC_FLAG_CLOSING 0x00000020U +#define AQ_NIC_PTP_DPATH_UP 0x02000000U #define AQ_NIC_LINK_DOWN 0x04000000U #define AQ_NIC_FLAG_ERR_UNPLUG 0x40000000U #define AQ_NIC_FLAG_ERR_HW 0x80000000U -#define AQ_NIC_WOL_ENABLED BIT(0) +#define AQ_NIC_WOL_MODES (WAKE_MAGIC |\ + WAKE_PHY) #define AQ_NIC_TCVEC2RING(_NIC_, _TC_, _VEC_) \ ((_TC_) * AQ_CFG_TCS_MAX + (_VEC_)) @@ -67,9 +84,10 @@ struct aq_hw_rx_fl2 { }; struct aq_hw_rx_fl3l4 { - u8 active_ipv4; - u8 active_ipv6:2; + u8 active_ipv4; + u8 active_ipv6:2; u8 is_ipv6; + u8 reserved_count; }; struct aq_hw_rx_fltrs_s { @@ -77,10 +95,13 @@ struct aq_hw_rx_fltrs_s { u16 active_filters; struct aq_hw_rx_fl2 fl2; struct aq_hw_rx_fl3l4 fl3l4; + /*filter ether type */ + u8 fet_reserved_count; }; struct aq_nic_s { atomic_t flags; + u32 msg_enable; struct aq_vec_s *aq_vec[AQ_CFG_VECS_MAX]; struct aq_ring_s *aq_ring_tx[AQ_CFG_VECS_MAX * AQ_CFG_TCS_MAX]; struct aq_hw_s *aq_hw; @@ -108,6 +129,8 @@ struct aq_nic_s { u32 irqvecs; /* mutex to serialize FW interface access operations */ struct mutex fwreq_mutex; + /* PTP support */ + struct aq_ptp_s *aq_ptp; struct aq_hw_rx_fltrs_s aq_hw_rx_fltrs; }; @@ -126,12 +149,15 @@ void aq_nic_cfg_start(struct aq_nic_s *self); int aq_nic_ndev_register(struct aq_nic_s *self); void aq_nic_ndev_free(struct aq_nic_s *self); int aq_nic_start(struct aq_nic_s *self); +unsigned int aq_nic_map_skb(struct aq_nic_s *self, struct sk_buff *skb, + struct aq_ring_s *ring); int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb); int aq_nic_get_regs(struct aq_nic_s *self, struct ethtool_regs *regs, void *p); int aq_nic_get_regs_count(struct aq_nic_s *self); void aq_nic_get_stats(struct aq_nic_s *self, u64 *data); int aq_nic_stop(struct aq_nic_s *self); -void aq_nic_deinit(struct aq_nic_s *self); +void aq_nic_deinit(struct aq_nic_s *self, bool link_down); +void aq_nic_set_power(struct aq_nic_s *self); void aq_nic_free_hot_resources(struct aq_nic_s *self); void aq_nic_free_vectors(struct aq_nic_s *self); int aq_nic_set_mtu(struct aq_nic_s *self, int new_mtu); @@ -145,8 +171,10 @@ int aq_nic_set_link_ksettings(struct aq_nic_s *self, const struct ethtool_link_ksettings *cmd); struct aq_nic_cfg_s *aq_nic_get_cfg(struct aq_nic_s *self); u32 aq_nic_get_fw_version(struct aq_nic_s *self); -int aq_nic_change_pm_state(struct aq_nic_s *self, pm_message_t *pm_msg); +int aq_nic_set_loopback(struct aq_nic_s *self); int aq_nic_update_interrupt_moderation_settings(struct aq_nic_s *self); void aq_nic_shutdown(struct aq_nic_s *self); - +u8 aq_nic_reserve_filter(struct aq_nic_s *self, enum aq_rx_filter_type type); +void aq_nic_release_filter(struct aq_nic_s *self, enum aq_rx_filter_type type, + u32 location); #endif /* AQ_NIC_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c index 74b9f3f1da81..2bb329606794 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_pci_func.c: Definition of PCI functions. */ @@ -185,6 +185,7 @@ unsigned int aq_pci_func_get_irq_type(struct aq_nic_s *self) return AQ_HW_IRQ_MSIX; if (self->pdev->msi_enabled) return AQ_HW_IRQ_MSI; + return AQ_HW_IRQ_LEGACY; } @@ -196,12 +197,12 @@ static void aq_pci_free_irq_vectors(struct aq_nic_s *self) static int aq_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { - struct aq_nic_s *self; - int err; struct net_device *ndev; resource_size_t mmio_pa; - u32 bar; + struct aq_nic_s *self; u32 numvecs; + u32 bar; + int err; err = pci_enable_device(pdev); if (err) @@ -269,6 +270,9 @@ static int aq_pci_probe(struct pci_dev *pdev, numvecs = min((u8)AQ_CFG_VECS_DEF, aq_nic_get_cfg(self)->aq_hw_caps->msix_irqs); numvecs = min(numvecs, num_online_cpus()); + /* Request IRQ vector for PTP */ + numvecs += 1; + numvecs += AQ_HW_SERVICE_IRQS; /*enable interrupts */ #if !AQ_CFG_FORCE_LEGACY_INT @@ -308,6 +312,7 @@ err_ndev: pci_release_regions(pdev); err_pci_func: pci_disable_device(pdev); + return err; } @@ -344,29 +349,98 @@ static void aq_pci_shutdown(struct pci_dev *pdev) } } -static int aq_pci_suspend(struct pci_dev *pdev, pm_message_t pm_msg) +static int aq_suspend_common(struct device *dev, bool deep) { - struct aq_nic_s *self = pci_get_drvdata(pdev); + struct aq_nic_s *nic = pci_get_drvdata(to_pci_dev(dev)); + + rtnl_lock(); - return aq_nic_change_pm_state(self, &pm_msg); + nic->power_state = AQ_HW_POWER_STATE_D3; + netif_device_detach(nic->ndev); + netif_tx_stop_all_queues(nic->ndev); + + aq_nic_stop(nic); + + if (deep) { + aq_nic_deinit(nic, !nic->aq_hw->aq_nic_cfg->wol); + aq_nic_set_power(nic); + } + + rtnl_unlock(); + + return 0; } -static int aq_pci_resume(struct pci_dev *pdev) +static int atl_resume_common(struct device *dev, bool deep) { - struct aq_nic_s *self = pci_get_drvdata(pdev); - pm_message_t pm_msg = PMSG_RESTORE; + struct pci_dev *pdev = to_pci_dev(dev); + struct aq_nic_s *nic; + int ret; + + nic = pci_get_drvdata(pdev); + + rtnl_lock(); - return aq_nic_change_pm_state(self, &pm_msg); + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + if (deep) { + ret = aq_nic_init(nic); + if (ret) + goto err_exit; + } + + ret = aq_nic_start(nic); + if (ret) + goto err_exit; + + netif_device_attach(nic->ndev); + netif_tx_start_all_queues(nic->ndev); + +err_exit: + rtnl_unlock(); + + return ret; } +static int aq_pm_freeze(struct device *dev) +{ + return aq_suspend_common(dev, false); +} + +static int aq_pm_suspend_poweroff(struct device *dev) +{ + return aq_suspend_common(dev, true); +} + +static int aq_pm_thaw(struct device *dev) +{ + return atl_resume_common(dev, false); +} + +static int aq_pm_resume_restore(struct device *dev) +{ + return atl_resume_common(dev, true); +} + +static const struct dev_pm_ops aq_pm_ops = { + .suspend = aq_pm_suspend_poweroff, + .poweroff = aq_pm_suspend_poweroff, + .freeze = aq_pm_freeze, + .resume = aq_pm_resume_restore, + .restore = aq_pm_resume_restore, + .thaw = aq_pm_thaw, +}; + static struct pci_driver aq_pci_ops = { .name = AQ_CFG_DRV_NAME, .id_table = aq_pci_tbl, .probe = aq_pci_probe, .remove = aq_pci_remove, - .suspend = aq_pci_suspend, - .resume = aq_pci_resume, .shutdown = aq_pci_shutdown, +#ifdef CONFIG_PM + .driver.pm = &aq_pm_ops, +#endif }; int aq_pci_func_register_driver(void) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_phy.c b/drivers/net/ethernet/aquantia/atlantic/aq_phy.c new file mode 100644 index 000000000000..51ae921e3e1f --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic/aq_phy.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* aQuantia Corporation Network Driver + * Copyright (C) 2018-2019 aQuantia Corporation. All rights reserved + */ + +#include "aq_phy.h" + +bool aq_mdio_busy_wait(struct aq_hw_s *aq_hw) +{ + int err = 0; + u32 val; + + err = readx_poll_timeout_atomic(hw_atl_mdio_busy_get, aq_hw, + val, val == 0U, 10U, 100000U); + + if (err < 0) + return false; + + return true; +} + +u16 aq_mdio_read_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr) +{ + u16 phy_addr = aq_hw->phy_id << 5 | mmd; + + /* Set Address register. */ + hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) << + HW_ATL_MDIO_ADDRESS_SHIFT); + /* Send Address command. */ + hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK | + (3 << HW_ATL_MDIO_OP_MODE_SHIFT) | + ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) << + HW_ATL_MDIO_PHY_ADDRESS_SHIFT)); + + aq_mdio_busy_wait(aq_hw); + + /* Send Read command. */ + hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK | + (1 << HW_ATL_MDIO_OP_MODE_SHIFT) | + ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) << + HW_ATL_MDIO_PHY_ADDRESS_SHIFT)); + /* Read result. */ + aq_mdio_busy_wait(aq_hw); + + return (u16)hw_atl_glb_mdio_iface5_get(aq_hw); +} + +void aq_mdio_write_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr, u16 data) +{ + u16 phy_addr = aq_hw->phy_id << 5 | mmd; + + /* Set Address register. */ + hw_atl_glb_mdio_iface4_set(aq_hw, (addr & HW_ATL_MDIO_ADDRESS_MSK) << + HW_ATL_MDIO_ADDRESS_SHIFT); + /* Send Address command. */ + hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK | + (3 << HW_ATL_MDIO_OP_MODE_SHIFT) | + ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) << + HW_ATL_MDIO_PHY_ADDRESS_SHIFT)); + + aq_mdio_busy_wait(aq_hw); + + hw_atl_glb_mdio_iface3_set(aq_hw, (data & HW_ATL_MDIO_WRITE_DATA_MSK) << + HW_ATL_MDIO_WRITE_DATA_SHIFT); + /* Send Write command. */ + hw_atl_glb_mdio_iface2_set(aq_hw, HW_ATL_MDIO_EXECUTE_OPERATION_MSK | + (2 << HW_ATL_MDIO_OP_MODE_SHIFT) | + ((phy_addr & HW_ATL_MDIO_PHY_ADDRESS_MSK) << + HW_ATL_MDIO_PHY_ADDRESS_SHIFT)); + + aq_mdio_busy_wait(aq_hw); +} + +u16 aq_phy_read_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address) +{ + int err = 0; + u32 val; + + err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw, + val, val == 1U, 10U, 100000U); + + if (err < 0) { + err = 0xffff; + goto err_exit; + } + + err = aq_mdio_read_word(aq_hw, mmd, address); + + hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO); + +err_exit: + return err; +} + +void aq_phy_write_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address, u16 data) +{ + int err = 0; + u32 val; + + err = readx_poll_timeout_atomic(hw_atl_sem_mdio_get, aq_hw, + val, val == 1U, 10U, 100000U); + if (err < 0) + return; + + aq_mdio_write_word(aq_hw, mmd, address, data); + hw_atl_reg_glb_cpu_sem_set(aq_hw, 1U, HW_ATL_FW_SM_MDIO); +} + +bool aq_phy_init_phy_id(struct aq_hw_s *aq_hw) +{ + u16 val; + + for (aq_hw->phy_id = 0; aq_hw->phy_id < HW_ATL_PHY_ID_MAX; + ++aq_hw->phy_id) { + /* PMA Standard Device Identifier 2: Address 1.3 */ + val = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3); + + if (val != 0xffff) + return true; + } + + return false; +} + +bool aq_phy_init(struct aq_hw_s *aq_hw) +{ + u32 dev_id; + + if (aq_hw->phy_id == HW_ATL_PHY_ID_MAX) + if (!aq_phy_init_phy_id(aq_hw)) + return false; + + /* PMA Standard Device Identifier: + * Address 1.2 = MSW, + * Address 1.3 = LSW + */ + dev_id = aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 2); + dev_id <<= 16; + dev_id |= aq_phy_read_reg(aq_hw, MDIO_MMD_PMAPMD, 3); + + if (dev_id == 0xffffffff) { + aq_hw->phy_id = HW_ATL_PHY_ID_MAX; + return false; + } + + return true; +} diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_phy.h b/drivers/net/ethernet/aquantia/atlantic/aq_phy.h new file mode 100644 index 000000000000..84b72ad04a4a --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic/aq_phy.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* aQuantia Corporation Network Driver + * Copyright (C) 2018-2019 aQuantia Corporation. All rights reserved + */ + +#ifndef AQ_PHY_H +#define AQ_PHY_H + +#include <linux/mdio.h> + +#include "hw_atl/hw_atl_llh.h" +#include "hw_atl/hw_atl_llh_internal.h" +#include "aq_hw_utils.h" +#include "aq_hw.h" + +#define HW_ATL_PHY_ID_MAX 32U + +bool aq_mdio_busy_wait(struct aq_hw_s *aq_hw); + +u16 aq_mdio_read_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr); + +void aq_mdio_write_word(struct aq_hw_s *aq_hw, u16 mmd, u16 addr, u16 data); + +u16 aq_phy_read_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address); + +void aq_phy_write_reg(struct aq_hw_s *aq_hw, u16 mmd, u16 address, u16 data); + +bool aq_phy_init_phy_id(struct aq_hw_s *aq_hw); + +bool aq_phy_init(struct aq_hw_s *aq_hw); + +#endif /* AQ_PHY_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c new file mode 100644 index 000000000000..58e8c641e8b3 --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c @@ -0,0 +1,1392 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Aquantia Corporation Network Driver + * Copyright (C) 2014-2019 Aquantia Corporation. All rights reserved + */ + +/* File aq_ptp.c: + * Definition of functions for Linux PTP support. + */ + +#include <linux/ptp_clock_kernel.h> +#include <linux/ptp_classify.h> +#include <linux/interrupt.h> +#include <linux/clocksource.h> + +#include "aq_nic.h" +#include "aq_ptp.h" +#include "aq_ring.h" +#include "aq_phy.h" +#include "aq_filters.h" + +#define AQ_PTP_TX_TIMEOUT (HZ * 10) + +#define POLL_SYNC_TIMER_MS 15 + +enum ptp_speed_offsets { + ptp_offset_idx_10 = 0, + ptp_offset_idx_100, + ptp_offset_idx_1000, + ptp_offset_idx_2500, + ptp_offset_idx_5000, + ptp_offset_idx_10000, +}; + +struct ptp_skb_ring { + struct sk_buff **buff; + spinlock_t lock; + unsigned int size; + unsigned int head; + unsigned int tail; +}; + +struct ptp_tx_timeout { + spinlock_t lock; + bool active; + unsigned long tx_start; +}; + +struct aq_ptp_s { + struct aq_nic_s *aq_nic; + struct hwtstamp_config hwtstamp_config; + spinlock_t ptp_lock; + spinlock_t ptp_ring_lock; + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_info; + + atomic_t offset_egress; + atomic_t offset_ingress; + + struct aq_ring_param_s ptp_ring_param; + + struct ptp_tx_timeout ptp_tx_timeout; + + unsigned int idx_vector; + struct napi_struct napi; + + struct aq_ring_s ptp_tx; + struct aq_ring_s ptp_rx; + struct aq_ring_s hwts_rx; + + struct ptp_skb_ring skb_ring; + + struct aq_rx_filter_l3l4 udp_filter; + struct aq_rx_filter_l2 eth_type_filter; + + struct delayed_work poll_sync; + u32 poll_timeout_ms; + + bool extts_pin_enabled; + u64 last_sync1588_ts; +}; + +struct ptp_tm_offset { + unsigned int mbps; + int egress; + int ingress; +}; + +static struct ptp_tm_offset ptp_offset[6]; + +void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, unsigned int mbps) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + int i, egress, ingress; + + if (!aq_ptp) + return; + + egress = 0; + ingress = 0; + + for (i = 0; i < ARRAY_SIZE(ptp_offset); i++) { + if (mbps == ptp_offset[i].mbps) { + egress = ptp_offset[i].egress; + ingress = ptp_offset[i].ingress; + break; + } + } + + atomic_set(&aq_ptp->offset_egress, egress); + atomic_set(&aq_ptp->offset_ingress, ingress); +} + +static int __aq_ptp_skb_put(struct ptp_skb_ring *ring, struct sk_buff *skb) +{ + unsigned int next_head = (ring->head + 1) % ring->size; + + if (next_head == ring->tail) + return -ENOMEM; + + ring->buff[ring->head] = skb_get(skb); + ring->head = next_head; + + return 0; +} + +static int aq_ptp_skb_put(struct ptp_skb_ring *ring, struct sk_buff *skb) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&ring->lock, flags); + ret = __aq_ptp_skb_put(ring, skb); + spin_unlock_irqrestore(&ring->lock, flags); + + return ret; +} + +static struct sk_buff *__aq_ptp_skb_get(struct ptp_skb_ring *ring) +{ + struct sk_buff *skb; + + if (ring->tail == ring->head) + return NULL; + + skb = ring->buff[ring->tail]; + ring->tail = (ring->tail + 1) % ring->size; + + return skb; +} + +static struct sk_buff *aq_ptp_skb_get(struct ptp_skb_ring *ring) +{ + unsigned long flags; + struct sk_buff *skb; + + spin_lock_irqsave(&ring->lock, flags); + skb = __aq_ptp_skb_get(ring); + spin_unlock_irqrestore(&ring->lock, flags); + + return skb; +} + +static unsigned int aq_ptp_skb_buf_len(struct ptp_skb_ring *ring) +{ + unsigned long flags; + unsigned int len; + + spin_lock_irqsave(&ring->lock, flags); + len = (ring->head >= ring->tail) ? + ring->head - ring->tail : + ring->size - ring->tail + ring->head; + spin_unlock_irqrestore(&ring->lock, flags); + + return len; +} + +static int aq_ptp_skb_ring_init(struct ptp_skb_ring *ring, unsigned int size) +{ + struct sk_buff **buff = kmalloc(sizeof(*buff) * size, GFP_KERNEL); + + if (!buff) + return -ENOMEM; + + spin_lock_init(&ring->lock); + + ring->buff = buff; + ring->size = size; + ring->head = 0; + ring->tail = 0; + + return 0; +} + +static void aq_ptp_skb_ring_clean(struct ptp_skb_ring *ring) +{ + struct sk_buff *skb; + + while ((skb = aq_ptp_skb_get(ring)) != NULL) + dev_kfree_skb_any(skb); +} + +static void aq_ptp_skb_ring_release(struct ptp_skb_ring *ring) +{ + if (ring->buff) { + aq_ptp_skb_ring_clean(ring); + kfree(ring->buff); + ring->buff = NULL; + } +} + +static void aq_ptp_tx_timeout_init(struct ptp_tx_timeout *timeout) +{ + spin_lock_init(&timeout->lock); + timeout->active = false; +} + +static void aq_ptp_tx_timeout_start(struct aq_ptp_s *aq_ptp) +{ + struct ptp_tx_timeout *timeout = &aq_ptp->ptp_tx_timeout; + unsigned long flags; + + spin_lock_irqsave(&timeout->lock, flags); + timeout->active = true; + timeout->tx_start = jiffies; + spin_unlock_irqrestore(&timeout->lock, flags); +} + +static void aq_ptp_tx_timeout_update(struct aq_ptp_s *aq_ptp) +{ + if (!aq_ptp_skb_buf_len(&aq_ptp->skb_ring)) { + struct ptp_tx_timeout *timeout = &aq_ptp->ptp_tx_timeout; + unsigned long flags; + + spin_lock_irqsave(&timeout->lock, flags); + timeout->active = false; + spin_unlock_irqrestore(&timeout->lock, flags); + } +} + +static void aq_ptp_tx_timeout_check(struct aq_ptp_s *aq_ptp) +{ + struct ptp_tx_timeout *timeout = &aq_ptp->ptp_tx_timeout; + unsigned long flags; + bool timeout_flag; + + timeout_flag = false; + + spin_lock_irqsave(&timeout->lock, flags); + if (timeout->active) { + timeout_flag = time_is_before_jiffies(timeout->tx_start + + AQ_PTP_TX_TIMEOUT); + /* reset active flag if timeout detected */ + if (timeout_flag) + timeout->active = false; + } + spin_unlock_irqrestore(&timeout->lock, flags); + + if (timeout_flag) { + aq_ptp_skb_ring_clean(&aq_ptp->skb_ring); + netdev_err(aq_ptp->aq_nic->ndev, + "PTP Timeout. Clearing Tx Timestamp SKBs\n"); + } +} + +/* aq_ptp_adjfine + * @ptp: the ptp clock structure + * @ppb: parts per billion adjustment from base + * + * adjust the frequency of the ptp cycle counter by the + * indicated ppb from the base frequency. + */ +static int aq_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + + mutex_lock(&aq_nic->fwreq_mutex); + aq_nic->aq_hw_ops->hw_adj_clock_freq(aq_nic->aq_hw, + scaled_ppm_to_ppb(scaled_ppm)); + mutex_unlock(&aq_nic->fwreq_mutex); + + return 0; +} + +/* aq_ptp_adjtime + * @ptp: the ptp clock structure + * @delta: offset to adjust the cycle counter by + * + * adjust the timer by resetting the timecounter structure. + */ +static int aq_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + unsigned long flags; + + spin_lock_irqsave(&aq_ptp->ptp_lock, flags); + aq_nic->aq_hw_ops->hw_adj_sys_clock(aq_nic->aq_hw, delta); + spin_unlock_irqrestore(&aq_ptp->ptp_lock, flags); + + return 0; +} + +/* aq_ptp_gettime + * @ptp: the ptp clock structure + * @ts: timespec structure to hold the current time value + * + * read the timecounter and return the correct value on ns, + * after converting it into a struct timespec. + */ +static int aq_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + unsigned long flags; + u64 ns; + + spin_lock_irqsave(&aq_ptp->ptp_lock, flags); + aq_nic->aq_hw_ops->hw_get_ptp_ts(aq_nic->aq_hw, &ns); + spin_unlock_irqrestore(&aq_ptp->ptp_lock, flags); + + *ts = ns_to_timespec64(ns); + + return 0; +} + +/* aq_ptp_settime + * @ptp: the ptp clock structure + * @ts: the timespec containing the new time for the cycle counter + * + * reset the timecounter to use a new base value instead of the kernel + * wall timer value. + */ +static int aq_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + unsigned long flags; + u64 ns = timespec64_to_ns(ts); + u64 now; + + spin_lock_irqsave(&aq_ptp->ptp_lock, flags); + aq_nic->aq_hw_ops->hw_get_ptp_ts(aq_nic->aq_hw, &now); + aq_nic->aq_hw_ops->hw_adj_sys_clock(aq_nic->aq_hw, (s64)ns - (s64)now); + + spin_unlock_irqrestore(&aq_ptp->ptp_lock, flags); + + return 0; +} + +static void aq_ptp_convert_to_hwtstamp(struct aq_ptp_s *aq_ptp, + struct skb_shared_hwtstamps *hwtstamp, + u64 timestamp) +{ + memset(hwtstamp, 0, sizeof(*hwtstamp)); + hwtstamp->hwtstamp = ns_to_ktime(timestamp); +} + +static int aq_ptp_hw_pin_conf(struct aq_nic_s *aq_nic, u32 pin_index, u64 start, + u64 period) +{ + if (period) + netdev_dbg(aq_nic->ndev, + "Enable GPIO %d pulsing, start time %llu, period %u\n", + pin_index, start, (u32)period); + else + netdev_dbg(aq_nic->ndev, + "Disable GPIO %d pulsing, start time %llu, period %u\n", + pin_index, start, (u32)period); + + /* Notify hardware of request to being sending pulses. + * If period is ZERO then pulsen is disabled. + */ + mutex_lock(&aq_nic->fwreq_mutex); + aq_nic->aq_hw_ops->hw_gpio_pulse(aq_nic->aq_hw, pin_index, + start, (u32)period); + mutex_unlock(&aq_nic->fwreq_mutex); + + return 0; +} + +static int aq_ptp_perout_pin_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct ptp_clock_time *t = &rq->perout.period; + struct ptp_clock_time *s = &rq->perout.start; + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 start, period; + u32 pin_index = rq->perout.index; + + /* verify the request channel is there */ + if (pin_index >= ptp->n_per_out) + return -EINVAL; + + /* we cannot support periods greater + * than 4 seconds due to reg limit + */ + if (t->sec > 4 || t->sec < 0) + return -ERANGE; + + /* convert to unsigned 64b ns, + * verify we can put it in a 32b register + */ + period = on ? t->sec * NSEC_PER_SEC + t->nsec : 0; + + /* verify the value is in range supported by hardware */ + if (period > U32_MAX) + return -ERANGE; + /* convert to unsigned 64b ns */ + /* TODO convert to AQ time */ + start = on ? s->sec * NSEC_PER_SEC + s->nsec : 0; + + aq_ptp_hw_pin_conf(aq_nic, pin_index, start, period); + + return 0; +} + +static int aq_ptp_pps_pin_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 start, period; + u32 pin_index = 0; + u32 rest = 0; + + /* verify the request channel is there */ + if (pin_index >= ptp->n_per_out) + return -EINVAL; + + aq_nic->aq_hw_ops->hw_get_ptp_ts(aq_nic->aq_hw, &start); + div_u64_rem(start, NSEC_PER_SEC, &rest); + period = on ? NSEC_PER_SEC : 0; /* PPS - pulse per second */ + start = on ? start - rest + NSEC_PER_SEC * + (rest > 990000000LL ? 2 : 1) : 0; + + aq_ptp_hw_pin_conf(aq_nic, pin_index, start, period); + + return 0; +} + +static void aq_ptp_extts_pin_ctrl(struct aq_ptp_s *aq_ptp) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u32 enable = aq_ptp->extts_pin_enabled; + + if (aq_nic->aq_hw_ops->hw_extts_gpio_enable) + aq_nic->aq_hw_ops->hw_extts_gpio_enable(aq_nic->aq_hw, 0, + enable); +} + +static int aq_ptp_extts_pin_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info); + + u32 pin_index = rq->extts.index; + + if (pin_index >= ptp->n_ext_ts) + return -EINVAL; + + aq_ptp->extts_pin_enabled = !!on; + if (on) { + aq_ptp->poll_timeout_ms = POLL_SYNC_TIMER_MS; + cancel_delayed_work_sync(&aq_ptp->poll_sync); + schedule_delayed_work(&aq_ptp->poll_sync, + msecs_to_jiffies(aq_ptp->poll_timeout_ms)); + } + + aq_ptp_extts_pin_ctrl(aq_ptp); + return 0; +} + +/* aq_ptp_gpio_feature_enable + * @ptp: the ptp clock structure + * @rq: the requested feature to change + * @on: whether to enable or disable the feature + */ +static int aq_ptp_gpio_feature_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + return aq_ptp_extts_pin_configure(ptp, rq, on); + case PTP_CLK_REQ_PEROUT: + return aq_ptp_perout_pin_configure(ptp, rq, on); + case PTP_CLK_REQ_PPS: + return aq_ptp_pps_pin_configure(ptp, rq, on); + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* aq_ptp_verify + * @ptp: the ptp clock structure + * @pin: index of the pin in question + * @func: the desired function to use + * @chan: the function channel index to use + */ +static int aq_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) +{ + /* verify the requested pin is there */ + if (!ptp->pin_config || pin >= ptp->n_pins) + return -EINVAL; + + /* enforce locked channels, no changing them */ + if (chan != ptp->pin_config[pin].chan) + return -EINVAL; + + /* we want to keep the functions locked as well */ + if (func != ptp->pin_config[pin].func) + return -EINVAL; + + return 0; +} + +/* aq_ptp_tx_hwtstamp - utility function which checks for TX time stamp + * @adapter: the private adapter struct + * + * if the timestamp is valid, we convert it into the timecounter ns + * value, then store that result into the hwtstamps structure which + * is passed up the network stack + */ +void aq_ptp_tx_hwtstamp(struct aq_nic_s *aq_nic, u64 timestamp) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + struct sk_buff *skb = aq_ptp_skb_get(&aq_ptp->skb_ring); + struct skb_shared_hwtstamps hwtstamp; + + if (!skb) { + netdev_err(aq_nic->ndev, "have timestamp but tx_queues empty\n"); + return; + } + + timestamp += atomic_read(&aq_ptp->offset_egress); + aq_ptp_convert_to_hwtstamp(aq_ptp, &hwtstamp, timestamp); + skb_tstamp_tx(skb, &hwtstamp); + dev_kfree_skb_any(skb); + + aq_ptp_tx_timeout_update(aq_ptp); +} + +/* aq_ptp_rx_hwtstamp - utility function which checks for RX time stamp + * @adapter: pointer to adapter struct + * @skb: particular skb to send timestamp with + * + * if the timestamp is valid, we convert it into the timecounter ns + * value, then store that result into the hwtstamps structure which + * is passed up the network stack + */ +static void aq_ptp_rx_hwtstamp(struct aq_ptp_s *aq_ptp, struct sk_buff *skb, + u64 timestamp) +{ + timestamp -= atomic_read(&aq_ptp->offset_ingress); + aq_ptp_convert_to_hwtstamp(aq_ptp, skb_hwtstamps(skb), timestamp); +} + +void aq_ptp_hwtstamp_config_get(struct aq_ptp_s *aq_ptp, + struct hwtstamp_config *config) +{ + *config = aq_ptp->hwtstamp_config; +} + +static void aq_ptp_prepare_filters(struct aq_ptp_s *aq_ptp) +{ + aq_ptp->udp_filter.cmd = HW_ATL_RX_ENABLE_FLTR_L3L4 | + HW_ATL_RX_ENABLE_CMP_PROT_L4 | + HW_ATL_RX_UDP | + HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4 | + HW_ATL_RX_HOST << HW_ATL_RX_ACTION_FL3F4_SHIFT | + HW_ATL_RX_ENABLE_QUEUE_L3L4 | + aq_ptp->ptp_rx.idx << HW_ATL_RX_QUEUE_FL3L4_SHIFT; + aq_ptp->udp_filter.p_dst = PTP_EV_PORT; + + aq_ptp->eth_type_filter.ethertype = ETH_P_1588; + aq_ptp->eth_type_filter.queue = aq_ptp->ptp_rx.idx; +} + +int aq_ptp_hwtstamp_config_set(struct aq_ptp_s *aq_ptp, + struct hwtstamp_config *config) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + const struct aq_hw_ops *hw_ops; + int err = 0; + + hw_ops = aq_nic->aq_hw_ops; + if (config->tx_type == HWTSTAMP_TX_ON || + config->rx_filter == HWTSTAMP_FILTER_PTP_V2_EVENT) { + aq_ptp_prepare_filters(aq_ptp); + if (hw_ops->hw_filter_l3l4_set) { + err = hw_ops->hw_filter_l3l4_set(aq_nic->aq_hw, + &aq_ptp->udp_filter); + } + if (!err && hw_ops->hw_filter_l2_set) { + err = hw_ops->hw_filter_l2_set(aq_nic->aq_hw, + &aq_ptp->eth_type_filter); + } + aq_utils_obj_set(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP); + } else { + aq_ptp->udp_filter.cmd &= ~HW_ATL_RX_ENABLE_FLTR_L3L4; + if (hw_ops->hw_filter_l3l4_set) { + err = hw_ops->hw_filter_l3l4_set(aq_nic->aq_hw, + &aq_ptp->udp_filter); + } + if (!err && hw_ops->hw_filter_l2_clear) { + err = hw_ops->hw_filter_l2_clear(aq_nic->aq_hw, + &aq_ptp->eth_type_filter); + } + aq_utils_obj_clear(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP); + } + + if (err) + return -EREMOTEIO; + + aq_ptp->hwtstamp_config = *config; + + return 0; +} + +bool aq_ptp_ring(struct aq_nic_s *aq_nic, struct aq_ring_s *ring) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return false; + + return &aq_ptp->ptp_tx == ring || + &aq_ptp->ptp_rx == ring || &aq_ptp->hwts_rx == ring; +} + +u16 aq_ptp_extract_ts(struct aq_nic_s *aq_nic, struct sk_buff *skb, u8 *p, + unsigned int len) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + u64 timestamp = 0; + u16 ret = aq_nic->aq_hw_ops->rx_extract_ts(aq_nic->aq_hw, + p, len, ×tamp); + + if (ret > 0) + aq_ptp_rx_hwtstamp(aq_ptp, skb, timestamp); + + return ret; +} + +static int aq_ptp_poll(struct napi_struct *napi, int budget) +{ + struct aq_ptp_s *aq_ptp = container_of(napi, struct aq_ptp_s, napi); + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + bool was_cleaned = false; + int work_done = 0; + int err; + + /* Processing PTP TX traffic */ + err = aq_nic->aq_hw_ops->hw_ring_tx_head_update(aq_nic->aq_hw, + &aq_ptp->ptp_tx); + if (err < 0) + goto err_exit; + + if (aq_ptp->ptp_tx.sw_head != aq_ptp->ptp_tx.hw_head) { + aq_ring_tx_clean(&aq_ptp->ptp_tx); + + was_cleaned = true; + } + + /* Processing HW_TIMESTAMP RX traffic */ + err = aq_nic->aq_hw_ops->hw_ring_hwts_rx_receive(aq_nic->aq_hw, + &aq_ptp->hwts_rx); + if (err < 0) + goto err_exit; + + if (aq_ptp->hwts_rx.sw_head != aq_ptp->hwts_rx.hw_head) { + aq_ring_hwts_rx_clean(&aq_ptp->hwts_rx, aq_nic); + + err = aq_nic->aq_hw_ops->hw_ring_hwts_rx_fill(aq_nic->aq_hw, + &aq_ptp->hwts_rx); + if (err < 0) + goto err_exit; + + was_cleaned = true; + } + + /* Processing PTP RX traffic */ + err = aq_nic->aq_hw_ops->hw_ring_rx_receive(aq_nic->aq_hw, + &aq_ptp->ptp_rx); + if (err < 0) + goto err_exit; + + if (aq_ptp->ptp_rx.sw_head != aq_ptp->ptp_rx.hw_head) { + unsigned int sw_tail_old; + + err = aq_ring_rx_clean(&aq_ptp->ptp_rx, napi, &work_done, budget); + if (err < 0) + goto err_exit; + + sw_tail_old = aq_ptp->ptp_rx.sw_tail; + err = aq_ring_rx_fill(&aq_ptp->ptp_rx); + if (err < 0) + goto err_exit; + + err = aq_nic->aq_hw_ops->hw_ring_rx_fill(aq_nic->aq_hw, + &aq_ptp->ptp_rx, + sw_tail_old); + if (err < 0) + goto err_exit; + } + + if (was_cleaned) + work_done = budget; + + if (work_done < budget) { + napi_complete_done(napi, work_done); + aq_nic->aq_hw_ops->hw_irq_enable(aq_nic->aq_hw, + BIT_ULL(aq_ptp->ptp_ring_param.vec_idx)); + } + +err_exit: + return work_done; +} + +static irqreturn_t aq_ptp_isr(int irq, void *private) +{ + struct aq_ptp_s *aq_ptp = private; + int err = 0; + + if (!aq_ptp) { + err = -EINVAL; + goto err_exit; + } + napi_schedule(&aq_ptp->napi); + +err_exit: + return err >= 0 ? IRQ_HANDLED : IRQ_NONE; +} + +int aq_ptp_xmit(struct aq_nic_s *aq_nic, struct sk_buff *skb) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + struct aq_ring_s *ring = &aq_ptp->ptp_tx; + unsigned long irq_flags; + int err = NETDEV_TX_OK; + unsigned int frags; + + if (skb->len <= 0) { + dev_kfree_skb_any(skb); + goto err_exit; + } + + frags = skb_shinfo(skb)->nr_frags + 1; + /* Frags cannot be bigger 16KB + * because PTP usually works + * without Jumbo even in a background + */ + if (frags > AQ_CFG_SKB_FRAGS_MAX || frags > aq_ring_avail_dx(ring)) { + /* Drop packet because it doesn't make sence to delay it */ + dev_kfree_skb_any(skb); + goto err_exit; + } + + err = aq_ptp_skb_put(&aq_ptp->skb_ring, skb); + if (err) { + netdev_err(aq_nic->ndev, "SKB Ring is overflow (%u)!\n", + ring->size); + return NETDEV_TX_BUSY; + } + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + aq_ptp_tx_timeout_start(aq_ptp); + skb_tx_timestamp(skb); + + spin_lock_irqsave(&aq_nic->aq_ptp->ptp_ring_lock, irq_flags); + frags = aq_nic_map_skb(aq_nic, skb, ring); + + if (likely(frags)) { + err = aq_nic->aq_hw_ops->hw_ring_tx_xmit(aq_nic->aq_hw, + ring, frags); + if (err >= 0) { + ++ring->stats.tx.packets; + ring->stats.tx.bytes += skb->len; + } + } else { + err = NETDEV_TX_BUSY; + } + spin_unlock_irqrestore(&aq_nic->aq_ptp->ptp_ring_lock, irq_flags); + +err_exit: + return err; +} + +void aq_ptp_service_task(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return; + + aq_ptp_tx_timeout_check(aq_ptp); +} + +int aq_ptp_irq_alloc(struct aq_nic_s *aq_nic) +{ + struct pci_dev *pdev = aq_nic->pdev; + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + int err = 0; + + if (!aq_ptp) + return 0; + + if (pdev->msix_enabled || pdev->msi_enabled) { + err = request_irq(pci_irq_vector(pdev, aq_ptp->idx_vector), + aq_ptp_isr, 0, aq_nic->ndev->name, aq_ptp); + } else { + err = -EINVAL; + goto err_exit; + } + +err_exit: + return err; +} + +void aq_ptp_irq_free(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + struct pci_dev *pdev = aq_nic->pdev; + + if (!aq_ptp) + return; + + free_irq(pci_irq_vector(pdev, aq_ptp->idx_vector), aq_ptp); +} + +int aq_ptp_ring_init(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + int err = 0; + + if (!aq_ptp) + return 0; + + err = aq_ring_init(&aq_ptp->ptp_tx); + if (err < 0) + goto err_exit; + err = aq_nic->aq_hw_ops->hw_ring_tx_init(aq_nic->aq_hw, + &aq_ptp->ptp_tx, + &aq_ptp->ptp_ring_param); + if (err < 0) + goto err_exit; + + err = aq_ring_init(&aq_ptp->ptp_rx); + if (err < 0) + goto err_exit; + err = aq_nic->aq_hw_ops->hw_ring_rx_init(aq_nic->aq_hw, + &aq_ptp->ptp_rx, + &aq_ptp->ptp_ring_param); + if (err < 0) + goto err_exit; + + err = aq_ring_rx_fill(&aq_ptp->ptp_rx); + if (err < 0) + goto err_rx_free; + err = aq_nic->aq_hw_ops->hw_ring_rx_fill(aq_nic->aq_hw, + &aq_ptp->ptp_rx, + 0U); + if (err < 0) + goto err_rx_free; + + err = aq_ring_init(&aq_ptp->hwts_rx); + if (err < 0) + goto err_rx_free; + err = aq_nic->aq_hw_ops->hw_ring_rx_init(aq_nic->aq_hw, + &aq_ptp->hwts_rx, + &aq_ptp->ptp_ring_param); + if (err < 0) + goto err_exit; + err = aq_nic->aq_hw_ops->hw_ring_hwts_rx_fill(aq_nic->aq_hw, + &aq_ptp->hwts_rx); + if (err < 0) + goto err_exit; + + return err; + +err_rx_free: + aq_ring_rx_deinit(&aq_ptp->ptp_rx); +err_exit: + return err; +} + +int aq_ptp_ring_start(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + int err = 0; + + if (!aq_ptp) + return 0; + + err = aq_nic->aq_hw_ops->hw_ring_tx_start(aq_nic->aq_hw, &aq_ptp->ptp_tx); + if (err < 0) + goto err_exit; + + err = aq_nic->aq_hw_ops->hw_ring_rx_start(aq_nic->aq_hw, &aq_ptp->ptp_rx); + if (err < 0) + goto err_exit; + + err = aq_nic->aq_hw_ops->hw_ring_rx_start(aq_nic->aq_hw, + &aq_ptp->hwts_rx); + if (err < 0) + goto err_exit; + + napi_enable(&aq_ptp->napi); + +err_exit: + return err; +} + +void aq_ptp_ring_stop(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return; + + aq_nic->aq_hw_ops->hw_ring_tx_stop(aq_nic->aq_hw, &aq_ptp->ptp_tx); + aq_nic->aq_hw_ops->hw_ring_rx_stop(aq_nic->aq_hw, &aq_ptp->ptp_rx); + + aq_nic->aq_hw_ops->hw_ring_rx_stop(aq_nic->aq_hw, &aq_ptp->hwts_rx); + + napi_disable(&aq_ptp->napi); +} + +void aq_ptp_ring_deinit(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp || !aq_ptp->ptp_tx.aq_nic || !aq_ptp->ptp_rx.aq_nic) + return; + + aq_ring_tx_clean(&aq_ptp->ptp_tx); + aq_ring_rx_deinit(&aq_ptp->ptp_rx); +} + +#define PTP_8TC_RING_IDX 8 +#define PTP_4TC_RING_IDX 16 +#define PTP_HWST_RING_IDX 31 + +int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + unsigned int tx_ring_idx, rx_ring_idx; + struct aq_ring_s *hwts; + u32 tx_tc_mode, rx_tc_mode; + struct aq_ring_s *ring; + int err; + + if (!aq_ptp) + return 0; + + /* Index must to be 8 (8 TCs) or 16 (4 TCs). + * It depends from Traffic Class mode. + */ + aq_nic->aq_hw_ops->hw_tx_tc_mode_get(aq_nic->aq_hw, &tx_tc_mode); + if (tx_tc_mode == 0) + tx_ring_idx = PTP_8TC_RING_IDX; + else + tx_ring_idx = PTP_4TC_RING_IDX; + + ring = aq_ring_tx_alloc(&aq_ptp->ptp_tx, aq_nic, + tx_ring_idx, &aq_nic->aq_nic_cfg); + if (!ring) { + err = -ENOMEM; + goto err_exit; + } + + aq_nic->aq_hw_ops->hw_rx_tc_mode_get(aq_nic->aq_hw, &rx_tc_mode); + if (rx_tc_mode == 0) + rx_ring_idx = PTP_8TC_RING_IDX; + else + rx_ring_idx = PTP_4TC_RING_IDX; + + ring = aq_ring_rx_alloc(&aq_ptp->ptp_rx, aq_nic, + rx_ring_idx, &aq_nic->aq_nic_cfg); + if (!ring) { + err = -ENOMEM; + goto err_exit_ptp_tx; + } + + hwts = aq_ring_hwts_rx_alloc(&aq_ptp->hwts_rx, aq_nic, PTP_HWST_RING_IDX, + aq_nic->aq_nic_cfg.rxds, + aq_nic->aq_nic_cfg.aq_hw_caps->rxd_size); + if (!hwts) { + err = -ENOMEM; + goto err_exit_ptp_rx; + } + + err = aq_ptp_skb_ring_init(&aq_ptp->skb_ring, aq_nic->aq_nic_cfg.rxds); + if (err != 0) { + err = -ENOMEM; + goto err_exit_hwts_rx; + } + + aq_ptp->ptp_ring_param.vec_idx = aq_ptp->idx_vector; + aq_ptp->ptp_ring_param.cpu = aq_ptp->ptp_ring_param.vec_idx + + aq_nic_get_cfg(aq_nic)->aq_rss.base_cpu_number; + cpumask_set_cpu(aq_ptp->ptp_ring_param.cpu, + &aq_ptp->ptp_ring_param.affinity_mask); + + return 0; + +err_exit_hwts_rx: + aq_ring_free(&aq_ptp->hwts_rx); +err_exit_ptp_rx: + aq_ring_free(&aq_ptp->ptp_rx); +err_exit_ptp_tx: + aq_ring_free(&aq_ptp->ptp_tx); +err_exit: + return err; +} + +void aq_ptp_ring_free(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return; + + aq_ring_free(&aq_ptp->ptp_tx); + aq_ring_free(&aq_ptp->ptp_rx); + aq_ring_free(&aq_ptp->hwts_rx); + + aq_ptp_skb_ring_release(&aq_ptp->skb_ring); +} + +#define MAX_PTP_GPIO_COUNT 4 + +static struct ptp_clock_info aq_ptp_clock = { + .owner = THIS_MODULE, + .name = "atlantic ptp", + .max_adj = 999999999, + .n_ext_ts = 0, + .pps = 0, + .adjfine = aq_ptp_adjfine, + .adjtime = aq_ptp_adjtime, + .gettime64 = aq_ptp_gettime, + .settime64 = aq_ptp_settime, + .n_per_out = 0, + .enable = aq_ptp_gpio_feature_enable, + .n_pins = 0, + .verify = aq_ptp_verify, + .pin_config = NULL, +}; + +#define ptp_offset_init(__idx, __mbps, __egress, __ingress) do { \ + ptp_offset[__idx].mbps = (__mbps); \ + ptp_offset[__idx].egress = (__egress); \ + ptp_offset[__idx].ingress = (__ingress); } \ + while (0) + +static void aq_ptp_offset_init_from_fw(const struct hw_atl_ptp_offset *offsets) +{ + int i; + + /* Load offsets for PTP */ + for (i = 0; i < ARRAY_SIZE(ptp_offset); i++) { + switch (i) { + /* 100M */ + case ptp_offset_idx_100: + ptp_offset_init(i, 100, + offsets->egress_100, + offsets->ingress_100); + break; + /* 1G */ + case ptp_offset_idx_1000: + ptp_offset_init(i, 1000, + offsets->egress_1000, + offsets->ingress_1000); + break; + /* 2.5G */ + case ptp_offset_idx_2500: + ptp_offset_init(i, 2500, + offsets->egress_2500, + offsets->ingress_2500); + break; + /* 5G */ + case ptp_offset_idx_5000: + ptp_offset_init(i, 5000, + offsets->egress_5000, + offsets->ingress_5000); + break; + /* 10G */ + case ptp_offset_idx_10000: + ptp_offset_init(i, 10000, + offsets->egress_10000, + offsets->ingress_10000); + break; + } + } +} + +static void aq_ptp_offset_init(const struct hw_atl_ptp_offset *offsets) +{ + memset(ptp_offset, 0, sizeof(ptp_offset)); + + aq_ptp_offset_init_from_fw(offsets); +} + +static void aq_ptp_gpio_init(struct ptp_clock_info *info, + struct hw_atl_info *hw_info) +{ + struct ptp_pin_desc pin_desc[MAX_PTP_GPIO_COUNT]; + u32 extts_pin_cnt = 0; + u32 out_pin_cnt = 0; + u32 i; + + memset(pin_desc, 0, sizeof(pin_desc)); + + for (i = 0; i < MAX_PTP_GPIO_COUNT - 1; i++) { + if (hw_info->gpio_pin[i] == + (GPIO_PIN_FUNCTION_PTP0 + out_pin_cnt)) { + snprintf(pin_desc[out_pin_cnt].name, + sizeof(pin_desc[out_pin_cnt].name), + "AQ_GPIO%d", i); + pin_desc[out_pin_cnt].index = out_pin_cnt; + pin_desc[out_pin_cnt].chan = out_pin_cnt; + pin_desc[out_pin_cnt++].func = PTP_PF_PEROUT; + } + } + + info->n_per_out = out_pin_cnt; + + if (hw_info->caps_ex & BIT(CAPS_EX_PHY_CTRL_TS_PIN)) { + extts_pin_cnt += 1; + + snprintf(pin_desc[out_pin_cnt].name, + sizeof(pin_desc[out_pin_cnt].name), + "AQ_GPIO%d", out_pin_cnt); + pin_desc[out_pin_cnt].index = out_pin_cnt; + pin_desc[out_pin_cnt].chan = 0; + pin_desc[out_pin_cnt].func = PTP_PF_EXTTS; + } + + info->n_pins = out_pin_cnt + extts_pin_cnt; + info->n_ext_ts = extts_pin_cnt; + + if (!info->n_pins) + return; + + info->pin_config = kcalloc(info->n_pins, sizeof(struct ptp_pin_desc), + GFP_KERNEL); + + if (!info->pin_config) + return; + + memcpy(info->pin_config, &pin_desc, + sizeof(struct ptp_pin_desc) * info->n_pins); +} + +void aq_ptp_clock_init(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + aq_ptp_settime(&aq_ptp->ptp_info, &ts); +} + +static void aq_ptp_poll_sync_work_cb(struct work_struct *w); + +int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) +{ + struct hw_atl_utils_mbox mbox; + struct ptp_clock *clock; + struct aq_ptp_s *aq_ptp; + int err = 0; + + if (!aq_nic->aq_hw_ops->hw_get_ptp_ts) { + aq_nic->aq_ptp = NULL; + return 0; + } + + if (!aq_nic->aq_fw_ops->enable_ptp) { + aq_nic->aq_ptp = NULL; + return 0; + } + + hw_atl_utils_mpi_read_stats(aq_nic->aq_hw, &mbox); + + if (!(mbox.info.caps_ex & BIT(CAPS_EX_PHY_PTP_EN))) { + aq_nic->aq_ptp = NULL; + return 0; + } + + aq_ptp_offset_init(&mbox.info.ptp_offset); + + aq_ptp = kzalloc(sizeof(*aq_ptp), GFP_KERNEL); + if (!aq_ptp) { + err = -ENOMEM; + goto err_exit; + } + + aq_ptp->aq_nic = aq_nic; + + spin_lock_init(&aq_ptp->ptp_lock); + spin_lock_init(&aq_ptp->ptp_ring_lock); + + aq_ptp->ptp_info = aq_ptp_clock; + aq_ptp_gpio_init(&aq_ptp->ptp_info, &mbox.info); + clock = ptp_clock_register(&aq_ptp->ptp_info, &aq_nic->ndev->dev); + if (IS_ERR(clock)) { + netdev_err(aq_nic->ndev, "ptp_clock_register failed\n"); + err = PTR_ERR(clock); + goto err_exit; + } + aq_ptp->ptp_clock = clock; + aq_ptp_tx_timeout_init(&aq_ptp->ptp_tx_timeout); + + atomic_set(&aq_ptp->offset_egress, 0); + atomic_set(&aq_ptp->offset_ingress, 0); + + netif_napi_add(aq_nic_get_ndev(aq_nic), &aq_ptp->napi, + aq_ptp_poll, AQ_CFG_NAPI_WEIGHT); + + aq_ptp->idx_vector = idx_vec; + + aq_nic->aq_ptp = aq_ptp; + + /* enable ptp counter */ + aq_utils_obj_set(&aq_nic->aq_hw->flags, AQ_HW_PTP_AVAILABLE); + mutex_lock(&aq_nic->fwreq_mutex); + aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 1); + aq_ptp_clock_init(aq_nic); + mutex_unlock(&aq_nic->fwreq_mutex); + + INIT_DELAYED_WORK(&aq_ptp->poll_sync, &aq_ptp_poll_sync_work_cb); + aq_ptp->eth_type_filter.location = + aq_nic_reserve_filter(aq_nic, aq_rx_filter_ethertype); + aq_ptp->udp_filter.location = + aq_nic_reserve_filter(aq_nic, aq_rx_filter_l3l4); + + return 0; + +err_exit: + if (aq_ptp) + kfree(aq_ptp->ptp_info.pin_config); + kfree(aq_ptp); + aq_nic->aq_ptp = NULL; + return err; +} + +void aq_ptp_unregister(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return; + + ptp_clock_unregister(aq_ptp->ptp_clock); +} + +void aq_ptp_free(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return; + + aq_nic_release_filter(aq_nic, aq_rx_filter_ethertype, + aq_ptp->eth_type_filter.location); + aq_nic_release_filter(aq_nic, aq_rx_filter_l3l4, + aq_ptp->udp_filter.location); + cancel_delayed_work_sync(&aq_ptp->poll_sync); + /* disable ptp */ + mutex_lock(&aq_nic->fwreq_mutex); + aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 0); + mutex_unlock(&aq_nic->fwreq_mutex); + + kfree(aq_ptp->ptp_info.pin_config); + + netif_napi_del(&aq_ptp->napi); + kfree(aq_ptp); + aq_nic->aq_ptp = NULL; +} + +struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp) +{ + return aq_ptp->ptp_clock; +} + +/* PTP external GPIO nanoseconds count */ +static uint64_t aq_ptp_get_sync1588_ts(struct aq_nic_s *aq_nic) +{ + u64 ts = 0; + + if (aq_nic->aq_hw_ops->hw_get_sync_ts) + aq_nic->aq_hw_ops->hw_get_sync_ts(aq_nic->aq_hw, &ts); + + return ts; +} + +static void aq_ptp_start_work(struct aq_ptp_s *aq_ptp) +{ + if (aq_ptp->extts_pin_enabled) { + aq_ptp->poll_timeout_ms = POLL_SYNC_TIMER_MS; + aq_ptp->last_sync1588_ts = + aq_ptp_get_sync1588_ts(aq_ptp->aq_nic); + schedule_delayed_work(&aq_ptp->poll_sync, + msecs_to_jiffies(aq_ptp->poll_timeout_ms)); + } +} + +int aq_ptp_link_change(struct aq_nic_s *aq_nic) +{ + struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp; + + if (!aq_ptp) + return 0; + + if (aq_nic->aq_hw->aq_link_status.mbps) + aq_ptp_start_work(aq_ptp); + else + cancel_delayed_work_sync(&aq_ptp->poll_sync); + + return 0; +} + +static bool aq_ptp_sync_ts_updated(struct aq_ptp_s *aq_ptp, u64 *new_ts) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 sync_ts2; + u64 sync_ts; + + sync_ts = aq_ptp_get_sync1588_ts(aq_nic); + + if (sync_ts != aq_ptp->last_sync1588_ts) { + sync_ts2 = aq_ptp_get_sync1588_ts(aq_nic); + if (sync_ts != sync_ts2) { + sync_ts = sync_ts2; + sync_ts2 = aq_ptp_get_sync1588_ts(aq_nic); + if (sync_ts != sync_ts2) { + netdev_err(aq_nic->ndev, + "%s: Unable to get correct GPIO TS", + __func__); + sync_ts = 0; + } + } + + *new_ts = sync_ts; + return true; + } + return false; +} + +static int aq_ptp_check_sync1588(struct aq_ptp_s *aq_ptp) +{ + struct aq_nic_s *aq_nic = aq_ptp->aq_nic; + u64 sync_ts; + + /* Sync1588 pin was triggered */ + if (aq_ptp_sync_ts_updated(aq_ptp, &sync_ts)) { + if (aq_ptp->extts_pin_enabled) { + struct ptp_clock_event ptp_event; + u64 time = 0; + + aq_nic->aq_hw_ops->hw_ts_to_sys_clock(aq_nic->aq_hw, + sync_ts, &time); + ptp_event.index = aq_ptp->ptp_info.n_pins - 1; + ptp_event.timestamp = time; + + ptp_event.type = PTP_CLOCK_EXTTS; + ptp_clock_event(aq_ptp->ptp_clock, &ptp_event); + } + + aq_ptp->last_sync1588_ts = sync_ts; + } + + return 0; +} + +static void aq_ptp_poll_sync_work_cb(struct work_struct *w) +{ + struct delayed_work *dw = to_delayed_work(w); + struct aq_ptp_s *aq_ptp = container_of(dw, struct aq_ptp_s, poll_sync); + + aq_ptp_check_sync1588(aq_ptp); + + if (aq_ptp->extts_pin_enabled) { + unsigned long timeout = msecs_to_jiffies(aq_ptp->poll_timeout_ms); + + schedule_delayed_work(&aq_ptp->poll_sync, timeout); + } +} diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h new file mode 100644 index 000000000000..231906431a48 --- /dev/null +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Aquantia Corporation Network Driver + * Copyright (C) 2014-2019 Aquantia Corporation. All rights reserved + */ + +/* File aq_ptp.h: Declaration of PTP functions. + */ +#ifndef AQ_PTP_H +#define AQ_PTP_H + +#include <linux/net_tstamp.h> + +#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK) + +/* Common functions */ +int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec); + +void aq_ptp_unregister(struct aq_nic_s *aq_nic); +void aq_ptp_free(struct aq_nic_s *aq_nic); + +int aq_ptp_irq_alloc(struct aq_nic_s *aq_nic); +void aq_ptp_irq_free(struct aq_nic_s *aq_nic); + +int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic); +void aq_ptp_ring_free(struct aq_nic_s *aq_nic); + +int aq_ptp_ring_init(struct aq_nic_s *aq_nic); +int aq_ptp_ring_start(struct aq_nic_s *aq_nic); +void aq_ptp_ring_stop(struct aq_nic_s *aq_nic); +void aq_ptp_ring_deinit(struct aq_nic_s *aq_nic); + +void aq_ptp_service_task(struct aq_nic_s *aq_nic); + +void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, unsigned int mbps); + +void aq_ptp_clock_init(struct aq_nic_s *aq_nic); + +/* Traffic processing functions */ +int aq_ptp_xmit(struct aq_nic_s *aq_nic, struct sk_buff *skb); +void aq_ptp_tx_hwtstamp(struct aq_nic_s *aq_nic, u64 timestamp); + +/* Must be to check available of PTP before call */ +void aq_ptp_hwtstamp_config_get(struct aq_ptp_s *aq_ptp, + struct hwtstamp_config *config); +int aq_ptp_hwtstamp_config_set(struct aq_ptp_s *aq_ptp, + struct hwtstamp_config *config); + +/* Return either ring is belong to PTP or not*/ +bool aq_ptp_ring(struct aq_nic_s *aq_nic, struct aq_ring_s *ring); + +u16 aq_ptp_extract_ts(struct aq_nic_s *aq_nic, struct sk_buff *skb, u8 *p, + unsigned int len); + +struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp); + +int aq_ptp_link_change(struct aq_nic_s *aq_nic); + +#else + +static inline int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec) +{ + return 0; +} + +static inline void aq_ptp_unregister(struct aq_nic_s *aq_nic) {} + +static inline void aq_ptp_free(struct aq_nic_s *aq_nic) +{ +} + +static inline int aq_ptp_irq_alloc(struct aq_nic_s *aq_nic) +{ + return 0; +} + +static inline void aq_ptp_irq_free(struct aq_nic_s *aq_nic) +{ +} + +static inline int aq_ptp_ring_alloc(struct aq_nic_s *aq_nic) +{ + return 0; +} + +static inline void aq_ptp_ring_free(struct aq_nic_s *aq_nic) {} + +static inline int aq_ptp_ring_init(struct aq_nic_s *aq_nic) +{ + return 0; +} + +static inline int aq_ptp_ring_start(struct aq_nic_s *aq_nic) +{ + return 0; +} + +static inline void aq_ptp_ring_stop(struct aq_nic_s *aq_nic) {} +static inline void aq_ptp_ring_deinit(struct aq_nic_s *aq_nic) {} +static inline void aq_ptp_service_task(struct aq_nic_s *aq_nic) {} +static inline void aq_ptp_tm_offset_set(struct aq_nic_s *aq_nic, + unsigned int mbps) {} +static inline void aq_ptp_clock_init(struct aq_nic_s *aq_nic) {} +static inline int aq_ptp_xmit(struct aq_nic_s *aq_nic, struct sk_buff *skb) +{ + return -EOPNOTSUPP; +} + +static inline void aq_ptp_tx_hwtstamp(struct aq_nic_s *aq_nic, u64 timestamp) {} +static inline void aq_ptp_hwtstamp_config_get(struct aq_ptp_s *aq_ptp, + struct hwtstamp_config *config) {} +static inline int aq_ptp_hwtstamp_config_set(struct aq_ptp_s *aq_ptp, + struct hwtstamp_config *config) +{ + return 0; +} + +static inline bool aq_ptp_ring(struct aq_nic_s *aq_nic, struct aq_ring_s *ring) +{ + return false; +} + +static inline u16 aq_ptp_extract_ts(struct aq_nic_s *aq_nic, + struct sk_buff *skb, u8 *p, + unsigned int len) +{ + return 0; +} + +static inline struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp) +{ + return NULL; +} + +static inline int aq_ptp_link_change(struct aq_nic_s *aq_nic) +{ + return 0; +} +#endif + +#endif /* AQ_PTP_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 76bdbe1596d6..951d86f8b66e 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_ring.c: Definition of functions for Rx/Tx rings. */ @@ -10,6 +10,7 @@ #include "aq_nic.h" #include "aq_hw.h" #include "aq_hw_utils.h" +#include "aq_ptp.h" #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -29,8 +30,8 @@ static int aq_get_rxpage(struct aq_rxpage *rxpage, unsigned int order, struct device *dev) { struct page *page; - dma_addr_t daddr; int ret = -ENOMEM; + dma_addr_t daddr; page = dev_alloc_pages(order); if (unlikely(!page)) @@ -117,6 +118,7 @@ err_exit: aq_ring_free(self); self = NULL; } + return self; } @@ -143,6 +145,7 @@ err_exit: aq_ring_free(self); self = NULL; } + return self; } @@ -174,6 +177,31 @@ err_exit: aq_ring_free(self); self = NULL; } + + return self; +} + +struct aq_ring_s * +aq_ring_hwts_rx_alloc(struct aq_ring_s *self, struct aq_nic_s *aq_nic, + unsigned int idx, unsigned int size, unsigned int dx_size) +{ + struct device *dev = aq_nic_get_dev(aq_nic); + size_t sz = size * dx_size + AQ_CFG_RXDS_DEF; + + memset(self, 0, sizeof(*self)); + + self->aq_nic = aq_nic; + self->idx = idx; + self->size = size; + self->dx_size = dx_size; + + self->dx_ring = dma_alloc_coherent(dev, sz, &self->dx_ring_pa, + GFP_KERNEL); + if (!self->dx_ring) { + aq_ring_free(self); + return NULL; + } + return self; } @@ -182,6 +210,7 @@ int aq_ring_init(struct aq_ring_s *self) self->hw_head = 0; self->sw_head = 0; self->sw_tail = 0; + return 0; } @@ -290,6 +319,7 @@ int aq_ring_rx_clean(struct aq_ring_s *self, self->sw_head = aq_ring_next_dx(self, self->sw_head), --budget, ++(*work_done)) { struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; + bool is_ptp_ring = aq_ptp_ring(self->aq_nic, self); struct aq_ring_buff_s *buff_ = NULL; struct sk_buff *skb = NULL; unsigned int next_ = 0U; @@ -354,6 +384,11 @@ int aq_ring_rx_clean(struct aq_ring_s *self, err = -ENOMEM; goto err_exit; } + if (is_ptp_ring) + buff->len -= + aq_ptp_extract_ts(self->aq_nic, skb, + aq_buf_vaddr(&buff->rxdata), + buff->len); skb_put(skb, buff->len); page_ref_inc(buff->rxdata.page); } else { @@ -362,6 +397,11 @@ int aq_ring_rx_clean(struct aq_ring_s *self, err = -ENOMEM; goto err_exit; } + if (is_ptp_ring) + buff->len -= + aq_ptp_extract_ts(self->aq_nic, skb, + aq_buf_vaddr(&buff->rxdata), + buff->len); hdr_len = buff->len; if (hdr_len > AQ_CFG_RX_HDR_SIZE) @@ -421,8 +461,8 @@ int aq_ring_rx_clean(struct aq_ring_s *self, skb_set_hash(skb, buff->rss_hash, buff->is_hash_l4 ? PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_NONE); - - skb_record_rx_queue(skb, self->idx); + /* Send all PTP traffic to 0 queue */ + skb_record_rx_queue(skb, is_ptp_ring ? 0 : self->idx); ++self->stats.rx.packets; self->stats.rx.bytes += skb->len; @@ -434,6 +474,21 @@ err_exit: return err; } +void aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic) +{ + while (self->sw_head != self->hw_head) { + u64 ns; + + aq_nic->aq_hw_ops->extract_hwts(aq_nic->aq_hw, + self->dx_ring + + (self->sw_head * self->dx_size), + self->dx_size, &ns); + aq_ptp_tx_hwtstamp(aq_nic, ns); + + self->sw_head = aq_ring_next_dx(self, self->sw_head); + } +} + int aq_ring_rx_fill(struct aq_ring_s *self) { unsigned int page_order = self->page_order; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index 47abd09d06c2..991e4d31b094 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File aq_ring.h: Declaration of functions for Rx/Tx rings. */ @@ -65,19 +65,20 @@ struct __packed aq_ring_buff_s { }; union { struct { - u16 len; + u32 len:16; u32 is_ip_cso:1; u32 is_udp_cso:1; u32 is_tcp_cso:1; u32 is_cso_err:1; u32 is_sop:1; u32 is_eop:1; - u32 is_gso:1; + u32 is_gso_tcp:1; + u32 is_gso_udp:1; u32 is_mapped:1; u32 is_cleaned:1; u32 is_error:1; u32 is_vlan:1; - u32 rsvd3:5; + u32 rsvd3:4; u16 eop_index; u16 rsvd4; }; @@ -174,4 +175,9 @@ int aq_ring_rx_clean(struct aq_ring_s *self, int budget); int aq_ring_rx_fill(struct aq_ring_s *self); +struct aq_ring_s *aq_ring_hwts_rx_alloc(struct aq_ring_s *self, + struct aq_nic_s *aq_nic, unsigned int idx, + unsigned int size, unsigned int dx_size); +void aq_ring_hwts_rx_clean(struct aq_ring_s *self, struct aq_nic_s *aq_nic); + #endif /* AQ_RING_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index a95c263a45aa..f40a427970dc 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -103,8 +103,8 @@ err_exit: struct aq_vec_s *aq_vec_alloc(struct aq_nic_s *aq_nic, unsigned int idx, struct aq_nic_cfg_s *aq_nic_cfg) { - struct aq_vec_s *self = NULL; struct aq_ring_s *ring = NULL; + struct aq_vec_s *self = NULL; unsigned int i = 0U; int err = 0; @@ -159,6 +159,7 @@ err_exit: aq_vec_free(self); self = NULL; } + return self; } @@ -263,6 +264,7 @@ void aq_vec_deinit(struct aq_vec_s *self) aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]); aq_ring_rx_deinit(&ring[AQ_VEC_RX_ID]); } + err_exit:; } @@ -361,9 +363,9 @@ void aq_vec_add_stats(struct aq_vec_s *self, int aq_vec_get_sw_stats(struct aq_vec_s *self, u64 *data, unsigned int *p_count) { - unsigned int count = 0U; struct aq_ring_stats_rx_s stats_rx; struct aq_ring_stats_tx_s stats_tx; + unsigned int count = 0U; memset(&stats_rx, 0U, sizeof(struct aq_ring_stats_rx_s)); memset(&stats_tx, 0U, sizeof(struct aq_ring_stats_tx_s)); diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c index 359a4d387185..9b1062b8af64 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c @@ -119,10 +119,10 @@ err_exit: static int hw_atl_a0_hw_qos_set(struct aq_hw_s *self) { - u32 tc = 0U; - u32 buff_size = 0U; - unsigned int i_priority = 0U; bool is_rx_flow_control = false; + unsigned int i_priority = 0U; + u32 buff_size = 0U; + u32 tc = 0U; /* TPS Descriptor rate init */ hw_atl_tps_tx_pkt_shed_desc_rate_curr_time_res_set(self, 0x0U); @@ -155,7 +155,7 @@ static int hw_atl_a0_hw_qos_set(struct aq_hw_s *self) /* QoS Rx buf size per TC */ tc = 0; - is_rx_flow_control = (AQ_NIC_FC_RX & self->aq_nic_cfg->flow_control); + is_rx_flow_control = (AQ_NIC_FC_RX & self->aq_nic_cfg->fc.req); buff_size = HW_ATL_A0_RXBUF_MAX; hw_atl_rpb_rx_pkt_buff_size_per_tc_set(self, buff_size, tc); @@ -180,9 +180,9 @@ static int hw_atl_a0_hw_rss_hash_set(struct aq_hw_s *self, struct aq_rss_parameters *rss_params) { struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; - int err = 0; - unsigned int i = 0U; unsigned int addr = 0U; + unsigned int i = 0U; + int err = 0; u32 val; for (i = 10, addr = 0U; i--; ++addr) { @@ -207,12 +207,12 @@ err_exit: static int hw_atl_a0_hw_rss_set(struct aq_hw_s *self, struct aq_rss_parameters *rss_params) { - u8 *indirection_table = rss_params->indirection_table; - u32 i = 0U; u32 num_rss_queues = max(1U, self->aq_nic_cfg->num_rss_queues); - int err = 0; + u8 *indirection_table = rss_params->indirection_table; u16 bitary[1 + (HW_ATL_A0_RSS_REDIRECTION_MAX * HW_ATL_A0_RSS_REDIRECTION_BITS / 16U)]; + int err = 0; + u32 i = 0U; u32 val; memset(bitary, 0, sizeof(bitary)); @@ -321,9 +321,9 @@ static int hw_atl_a0_hw_init_rx_path(struct aq_hw_s *self) static int hw_atl_a0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr) { - int err = 0; unsigned int h = 0U; unsigned int l = 0U; + int err = 0; if (!mac_addr) { err = -EINVAL; @@ -352,10 +352,9 @@ static int hw_atl_a0_hw_init(struct aq_hw_s *self, u8 *mac_addr) [AQ_HW_IRQ_MSI] = { 0x20000021U, 0x20000025U }, [AQ_HW_IRQ_MSIX] = { 0x20000022U, 0x20000026U }, }; - + struct aq_nic_cfg_s *aq_nic_cfg = self->aq_nic_cfg; int err = 0; - struct aq_nic_cfg_s *aq_nic_cfg = self->aq_nic_cfg; hw_atl_a0_hw_init_tx_path(self); hw_atl_a0_hw_init_rx_path(self); @@ -404,6 +403,7 @@ static int hw_atl_a0_hw_ring_tx_start(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_tdm_tx_desc_en_set(self, 1, ring->idx); + return aq_hw_err_from_flags(self); } @@ -411,6 +411,7 @@ static int hw_atl_a0_hw_ring_rx_start(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_rdm_rx_desc_en_set(self, 1, ring->idx); + return aq_hw_err_from_flags(self); } @@ -418,6 +419,7 @@ static int hw_atl_a0_hw_start(struct aq_hw_s *self) { hw_atl_tpb_tx_buff_en_set(self, 1); hw_atl_rpb_rx_buff_en_set(self, 1); + return aq_hw_err_from_flags(self); } @@ -425,6 +427,7 @@ static int hw_atl_a0_hw_tx_ring_tail_update(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_reg_tx_dma_desc_tail_ptr_set(self, ring->sw_tail, ring->idx); + return 0; } @@ -435,8 +438,8 @@ static int hw_atl_a0_hw_ring_tx_xmit(struct aq_hw_s *self, struct aq_ring_buff_s *buff = NULL; struct hw_atl_txd_s *txd = NULL; unsigned int buff_pa_len = 0U; - unsigned int pkt_len = 0U; unsigned int frag_count = 0U; + unsigned int pkt_len = 0U; bool is_gso = false; buff = &ring->buff_ring[ring->sw_tail]; @@ -451,7 +454,7 @@ static int hw_atl_a0_hw_ring_tx_xmit(struct aq_hw_s *self, buff = &ring->buff_ring[ring->sw_tail]; - if (buff->is_gso) { + if (buff->is_gso_tcp) { txd->ctl |= (buff->len_l3 << 31) | (buff->len_l2 << 24) | HW_ATL_A0_TXD_CTL_CMD_TCP | @@ -500,6 +503,7 @@ static int hw_atl_a0_hw_ring_tx_xmit(struct aq_hw_s *self, } hw_atl_a0_hw_tx_ring_tail_update(self, ring); + return aq_hw_err_from_flags(self); } @@ -507,8 +511,8 @@ static int hw_atl_a0_hw_ring_rx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring, struct aq_ring_param_s *aq_ring_param) { - u32 dma_desc_addr_lsw = (u32)aq_ring->dx_ring_pa; u32 dma_desc_addr_msw = (u32)(((u64)aq_ring->dx_ring_pa) >> 32); + u32 dma_desc_addr_lsw = (u32)aq_ring->dx_ring_pa; hw_atl_rdm_rx_desc_en_set(self, false, aq_ring->idx); @@ -549,8 +553,8 @@ static int hw_atl_a0_hw_ring_tx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring, struct aq_ring_param_s *aq_ring_param) { - u32 dma_desc_lsw_addr = (u32)aq_ring->dx_ring_pa; u32 dma_desc_msw_addr = (u32)(((u64)aq_ring->dx_ring_pa) >> 32); + u32 dma_desc_lsw_addr = (u32)aq_ring->dx_ring_pa; hw_atl_reg_tx_dma_desc_base_addresslswset(self, dma_desc_lsw_addr, aq_ring->idx); @@ -599,8 +603,8 @@ static int hw_atl_a0_hw_ring_rx_fill(struct aq_hw_s *self, static int hw_atl_a0_hw_ring_tx_head_update(struct aq_hw_s *self, struct aq_ring_s *ring) { - int err = 0; unsigned int hw_head = hw_atl_tdm_tx_desc_head_ptr_get(self, ring->idx); + int err = 0; if (aq_utils_obj_test(&self->flags, AQ_HW_FLAG_ERR_UNPLUG)) { err = -ENXIO; @@ -720,6 +724,7 @@ static int hw_atl_a0_hw_irq_enable(struct aq_hw_s *self, u64 mask) { hw_atl_itr_irq_msk_setlsw_set(self, LODWORD(mask) | (1U << HW_ATL_A0_ERR_INT)); + return aq_hw_err_from_flags(self); } @@ -737,6 +742,7 @@ static int hw_atl_a0_hw_irq_disable(struct aq_hw_s *self, u64 mask) static int hw_atl_a0_hw_irq_read(struct aq_hw_s *self, u64 *mask) { *mask = hw_atl_itr_irq_statuslsw_get(self); + return aq_hw_err_from_flags(self); } @@ -859,6 +865,7 @@ static int hw_atl_a0_hw_interrupt_moderation_set(struct aq_hw_s *self) static int hw_atl_a0_hw_stop(struct aq_hw_s *self) { hw_atl_a0_hw_irq_disable(self, HW_ATL_A0_INT_MASK); + return aq_hw_err_from_flags(self); } @@ -866,6 +873,7 @@ static int hw_atl_a0_hw_ring_tx_stop(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_tdm_tx_desc_en_set(self, 0U, ring->idx); + return aq_hw_err_from_flags(self); } @@ -873,6 +881,7 @@ static int hw_atl_a0_hw_ring_rx_stop(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_rdm_rx_desc_en_set(self, 0U, ring->idx); + return aq_hw_err_from_flags(self); } diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c index 2ad3fa6316ce..58e891af6e09 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_b0.c: Definition of Atlantic hardware specific functions. */ @@ -10,6 +10,7 @@ #include "../aq_hw_utils.h" #include "../aq_ring.h" #include "../aq_nic.h" +#include "../aq_phy.h" #include "hw_atl_b0.h" #include "hw_atl_utils.h" #include "hw_atl_llh.h" @@ -42,13 +43,17 @@ NETIF_F_NTUPLE | \ NETIF_F_HW_VLAN_CTAG_FILTER | \ NETIF_F_HW_VLAN_CTAG_RX | \ - NETIF_F_HW_VLAN_CTAG_TX, \ + NETIF_F_HW_VLAN_CTAG_TX | \ + NETIF_F_GSO_UDP_L4 | \ + NETIF_F_GSO_PARTIAL, \ .hw_priv_flags = IFF_UNICAST_FLT, \ .flow_control = true, \ .mtu = HW_ATL_B0_MTU_JUMBO, \ .mac_regs_count = 88, \ .hw_alive_check_addr = 0x10U +#define FRAC_PER_NS 0x100000000LL + const struct aq_hw_caps_s hw_atl_b0_caps_aqc100 = { DEFAULT_B0_BOARD_BASIC_CAPABILITIES, .media_type = AQ_HW_MEDIA_TYPE_FIBRE, @@ -104,14 +109,15 @@ static int hw_atl_b0_hw_reset(struct aq_hw_s *self) static int hw_atl_b0_set_fc(struct aq_hw_s *self, u32 fc, u32 tc) { hw_atl_rpb_rx_xoff_en_per_tc_set(self, !!(fc & AQ_NIC_FC_RX), tc); + return 0; } static int hw_atl_b0_hw_qos_set(struct aq_hw_s *self) { - u32 tc = 0U; - u32 buff_size = 0U; unsigned int i_priority = 0U; + u32 buff_size = 0U; + u32 tc = 0U; /* TPS Descriptor rate init */ hw_atl_tps_tx_pkt_shed_desc_rate_curr_time_res_set(self, 0x0U); @@ -124,13 +130,16 @@ static int hw_atl_b0_hw_qos_set(struct aq_hw_s *self) hw_atl_tps_tx_pkt_shed_desc_tc_arb_mode_set(self, 0U); hw_atl_tps_tx_pkt_shed_data_arb_mode_set(self, 0U); - hw_atl_tps_tx_pkt_shed_tc_data_max_credit_set(self, 0xFFF, 0U); - hw_atl_tps_tx_pkt_shed_tc_data_weight_set(self, 0x64, 0U); - hw_atl_tps_tx_pkt_shed_desc_tc_max_credit_set(self, 0x50, 0U); - hw_atl_tps_tx_pkt_shed_desc_tc_weight_set(self, 0x1E, 0U); + tc = 0; + + /* TX Packet Scheduler Data TC0 */ + hw_atl_tps_tx_pkt_shed_tc_data_max_credit_set(self, 0xFFF, tc); + hw_atl_tps_tx_pkt_shed_tc_data_weight_set(self, 0x64, tc); + hw_atl_tps_tx_pkt_shed_desc_tc_max_credit_set(self, 0x50, tc); + hw_atl_tps_tx_pkt_shed_desc_tc_weight_set(self, 0x1E, tc); - /* Tx buf size */ - buff_size = HW_ATL_B0_TXBUF_MAX; + /* Tx buf size TC0 */ + buff_size = HW_ATL_B0_TXBUF_MAX - HW_ATL_B0_PTP_TXBUF_SIZE; hw_atl_tpb_tx_pkt_buff_size_per_tc_set(self, buff_size, tc); hw_atl_tpb_tx_buff_hi_threshold_per_tc_set(self, @@ -141,10 +150,15 @@ static int hw_atl_b0_hw_qos_set(struct aq_hw_s *self) (buff_size * (1024 / 32U) * 50U) / 100U, tc); + /* Init TC2 for PTP_TX */ + tc = 2; + + hw_atl_tpb_tx_pkt_buff_size_per_tc_set(self, HW_ATL_B0_PTP_TXBUF_SIZE, + tc); /* QoS Rx buf size per TC */ tc = 0; - buff_size = HW_ATL_B0_RXBUF_MAX; + buff_size = HW_ATL_B0_RXBUF_MAX - HW_ATL_B0_PTP_RXBUF_SIZE; hw_atl_rpb_rx_pkt_buff_size_per_tc_set(self, buff_size, tc); hw_atl_rpb_rx_buff_hi_threshold_per_tc_set(self, @@ -156,7 +170,15 @@ static int hw_atl_b0_hw_qos_set(struct aq_hw_s *self) (1024U / 32U) * 50U) / 100U, tc); - hw_atl_b0_set_fc(self, self->aq_nic_cfg->flow_control, tc); + hw_atl_b0_set_fc(self, self->aq_nic_cfg->fc.req, tc); + + /* Init TC2 for PTP_RX */ + tc = 2; + + hw_atl_rpb_rx_pkt_buff_size_per_tc_set(self, HW_ATL_B0_PTP_RXBUF_SIZE, + tc); + /* No flow control for PTP */ + hw_atl_rpb_rx_xoff_en_per_tc_set(self, 0U, tc); /* QoS 802.1p priority -> TC mapping */ for (i_priority = 8U; i_priority--;) @@ -169,9 +191,9 @@ static int hw_atl_b0_hw_rss_hash_set(struct aq_hw_s *self, struct aq_rss_parameters *rss_params) { struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; - int err = 0; - unsigned int i = 0U; unsigned int addr = 0U; + unsigned int i = 0U; + int err = 0; u32 val; for (i = 10, addr = 0U; i--; ++addr) { @@ -196,12 +218,12 @@ err_exit: static int hw_atl_b0_hw_rss_set(struct aq_hw_s *self, struct aq_rss_parameters *rss_params) { - u8 *indirection_table = rss_params->indirection_table; - u32 i = 0U; u32 num_rss_queues = max(1U, self->aq_nic_cfg->num_rss_queues); - int err = 0; + u8 *indirection_table = rss_params->indirection_table; u16 bitary[1 + (HW_ATL_B0_RSS_REDIRECTION_MAX * HW_ATL_B0_RSS_REDIRECTION_BITS / 16U)]; + int err = 0; + u32 i = 0U; u32 val; memset(bitary, 0, sizeof(bitary)); @@ -285,6 +307,7 @@ static int hw_atl_b0_hw_offload_set(struct aq_hw_s *self, hw_atl_itr_rsc_delay_set(self, 1U); } + return aq_hw_err_from_flags(self); } @@ -363,9 +386,9 @@ static int hw_atl_b0_hw_init_rx_path(struct aq_hw_s *self) static int hw_atl_b0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr) { - int err = 0; unsigned int h = 0U; unsigned int l = 0U; + int err = 0; if (!mac_addr) { err = -EINVAL; @@ -394,11 +417,10 @@ static int hw_atl_b0_hw_init(struct aq_hw_s *self, u8 *mac_addr) [AQ_HW_IRQ_MSI] = { 0x20000021U, 0x20000025U }, [AQ_HW_IRQ_MSIX] = { 0x20000022U, 0x20000026U }, }; - + struct aq_nic_cfg_s *aq_nic_cfg = self->aq_nic_cfg; int err = 0; u32 val; - struct aq_nic_cfg_s *aq_nic_cfg = self->aq_nic_cfg; hw_atl_b0_hw_init_tx_path(self); hw_atl_b0_hw_init_rx_path(self); @@ -441,8 +463,10 @@ static int hw_atl_b0_hw_init(struct aq_hw_s *self, u8 *mac_addr) /* Interrupts */ hw_atl_reg_gen_irq_map_set(self, - ((HW_ATL_B0_ERR_INT << 0x18) | (1U << 0x1F)) | - ((HW_ATL_B0_ERR_INT << 0x10) | (1U << 0x17)), 0U); + ((HW_ATL_B0_ERR_INT << 0x18) | + (1U << 0x1F)) | + ((HW_ATL_B0_ERR_INT << 0x10) | + (1U << 0x17)), 0U); /* Enable link interrupt */ if (aq_nic_cfg->link_irq_vec) @@ -459,6 +483,7 @@ static int hw_atl_b0_hw_ring_tx_start(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_tdm_tx_desc_en_set(self, 1, ring->idx); + return aq_hw_err_from_flags(self); } @@ -466,6 +491,7 @@ static int hw_atl_b0_hw_ring_rx_start(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_rdm_rx_desc_en_set(self, 1, ring->idx); + return aq_hw_err_from_flags(self); } @@ -473,6 +499,7 @@ static int hw_atl_b0_hw_start(struct aq_hw_s *self) { hw_atl_tpb_tx_buff_en_set(self, 1); hw_atl_rpb_rx_buff_en_set(self, 1); + return aq_hw_err_from_flags(self); } @@ -480,6 +507,7 @@ static int hw_atl_b0_hw_tx_ring_tail_update(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_reg_tx_dma_desc_tail_ptr_set(self, ring->sw_tail, ring->idx); + return 0; } @@ -490,8 +518,8 @@ static int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self, struct aq_ring_buff_s *buff = NULL; struct hw_atl_txd_s *txd = NULL; unsigned int buff_pa_len = 0U; - unsigned int pkt_len = 0U; unsigned int frag_count = 0U; + unsigned int pkt_len = 0U; bool is_vlan = false; bool is_gso = false; @@ -507,8 +535,9 @@ static int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self, buff = &ring->buff_ring[ring->sw_tail]; - if (buff->is_gso) { - txd->ctl |= HW_ATL_B0_TXD_CTL_CMD_TCP; + if (buff->is_gso_tcp || buff->is_gso_udp) { + if (buff->is_gso_tcp) + txd->ctl |= HW_ATL_B0_TXD_CTL_CMD_TCP; txd->ctl |= HW_ATL_B0_TXD_CTL_DESC_TYPE_TXC; txd->ctl |= (buff->len_l3 << 31) | (buff->len_l2 << 24); @@ -528,7 +557,7 @@ static int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self, txd->ctl |= buff->vlan_tx_tag << 4; is_vlan = true; } - if (!buff->is_gso && !buff->is_vlan) { + if (!buff->is_gso_tcp && !buff->is_gso_udp && !buff->is_vlan) { buff_pa_len = buff->len; txd->buf_addr = buff->pa; @@ -567,6 +596,7 @@ static int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self, } hw_atl_b0_hw_tx_ring_tail_update(self, ring); + return aq_hw_err_from_flags(self); } @@ -574,9 +604,9 @@ static int hw_atl_b0_hw_ring_rx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring, struct aq_ring_param_s *aq_ring_param) { - u32 dma_desc_addr_lsw = (u32)aq_ring->dx_ring_pa; u32 dma_desc_addr_msw = (u32)(((u64)aq_ring->dx_ring_pa) >> 32); u32 vlan_rx_stripping = self->aq_nic_cfg->is_vlan_rx_strip; + u32 dma_desc_addr_lsw = (u32)aq_ring->dx_ring_pa; hw_atl_rdm_rx_desc_en_set(self, false, aq_ring->idx); @@ -617,8 +647,8 @@ static int hw_atl_b0_hw_ring_tx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring, struct aq_ring_param_s *aq_ring_param) { - u32 dma_desc_lsw_addr = (u32)aq_ring->dx_ring_pa; u32 dma_desc_msw_addr = (u32)(((u64)aq_ring->dx_ring_pa) >> 32); + u32 dma_desc_lsw_addr = (u32)aq_ring->dx_ring_pa; hw_atl_reg_tx_dma_desc_base_addresslswset(self, dma_desc_lsw_addr, aq_ring->idx); @@ -664,11 +694,53 @@ static int hw_atl_b0_hw_ring_rx_fill(struct aq_hw_s *self, return aq_hw_err_from_flags(self); } +static int hw_atl_b0_hw_ring_hwts_rx_fill(struct aq_hw_s *self, + struct aq_ring_s *ring) +{ + unsigned int i; + + for (i = aq_ring_avail_dx(ring); i--; + ring->sw_tail = aq_ring_next_dx(ring, ring->sw_tail)) { + struct hw_atl_rxd_s *rxd = + (struct hw_atl_rxd_s *) + &ring->dx_ring[ring->sw_tail * HW_ATL_B0_RXD_SIZE]; + + rxd->buf_addr = ring->dx_ring_pa + ring->size * ring->dx_size; + rxd->hdr_addr = 0U; + } + /* Make sure descriptors are updated before bump tail*/ + wmb(); + + hw_atl_reg_rx_dma_desc_tail_ptr_set(self, ring->sw_tail, ring->idx); + + return aq_hw_err_from_flags(self); +} + +static int hw_atl_b0_hw_ring_hwts_rx_receive(struct aq_hw_s *self, + struct aq_ring_s *ring) +{ + while (ring->hw_head != ring->sw_tail) { + struct hw_atl_rxd_hwts_wb_s *hwts_wb = + (struct hw_atl_rxd_hwts_wb_s *) + (ring->dx_ring + (ring->hw_head * HW_ATL_B0_RXD_SIZE)); + + /* RxD is not done */ + if (!(hwts_wb->sec_lw0 & 0x1U)) + break; + + ring->hw_head = aq_ring_next_dx(ring, ring->hw_head); + } + + return aq_hw_err_from_flags(self); +} + static int hw_atl_b0_hw_ring_tx_head_update(struct aq_hw_s *self, struct aq_ring_s *ring) { + unsigned int hw_head_; int err = 0; - unsigned int hw_head_ = hw_atl_tdm_tx_desc_head_ptr_get(self, ring->idx); + + hw_head_ = hw_atl_tdm_tx_desc_head_ptr_get(self, ring->idx); if (aq_utils_obj_test(&self->flags, AQ_HW_FLAG_ERR_UNPLUG)) { err = -ENXIO; @@ -784,6 +856,7 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, static int hw_atl_b0_hw_irq_enable(struct aq_hw_s *self, u64 mask) { hw_atl_itr_irq_msk_setlsw_set(self, LODWORD(mask)); + return aq_hw_err_from_flags(self); } @@ -793,12 +866,14 @@ static int hw_atl_b0_hw_irq_disable(struct aq_hw_s *self, u64 mask) hw_atl_itr_irq_status_clearlsw_set(self, LODWORD(mask)); atomic_inc(&self->dpc); + return aq_hw_err_from_flags(self); } static int hw_atl_b0_hw_irq_read(struct aq_hw_s *self, u64 *mask) { *mask = hw_atl_itr_irq_statuslsw_get(self); + return aq_hw_err_from_flags(self); } @@ -807,8 +882,8 @@ static int hw_atl_b0_hw_irq_read(struct aq_hw_s *self, u64 *mask) static int hw_atl_b0_hw_packet_filter_set(struct aq_hw_s *self, unsigned int packet_filter) { - unsigned int i = 0U; struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; + unsigned int i = 0U; hw_atl_rpfl2promiscuous_mode_en_set(self, IS_FILTER_ENABLED(IFF_PROMISC)); @@ -846,29 +921,30 @@ static int hw_atl_b0_hw_multicast_list_set(struct aq_hw_s *self, u32 count) { int err = 0; + struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; if (count > (HW_ATL_B0_MAC_MAX - HW_ATL_B0_MAC_MIN)) { err = -EBADRQC; goto err_exit; } - for (self->aq_nic_cfg->mc_list_count = 0U; - self->aq_nic_cfg->mc_list_count < count; - ++self->aq_nic_cfg->mc_list_count) { - u32 i = self->aq_nic_cfg->mc_list_count; + for (cfg->mc_list_count = 0U; + cfg->mc_list_count < count; + ++cfg->mc_list_count) { + u32 i = cfg->mc_list_count; u32 h = (ar_mac[i][0] << 8) | (ar_mac[i][1]); u32 l = (ar_mac[i][2] << 24) | (ar_mac[i][3] << 16) | (ar_mac[i][4] << 8) | ar_mac[i][5]; hw_atl_rpfl2_uc_flr_en_set(self, 0U, HW_ATL_B0_MAC_MIN + i); - hw_atl_rpfl2unicast_dest_addresslsw_set(self, - l, HW_ATL_B0_MAC_MIN + i); + hw_atl_rpfl2unicast_dest_addresslsw_set(self, l, + HW_ATL_B0_MAC_MIN + i); - hw_atl_rpfl2unicast_dest_addressmsw_set(self, - h, HW_ATL_B0_MAC_MIN + i); + hw_atl_rpfl2unicast_dest_addressmsw_set(self, h, + HW_ATL_B0_MAC_MIN + i); hw_atl_rpfl2_uc_flr_en_set(self, - (self->aq_nic_cfg->is_mc_list_enabled), + (cfg->is_mc_list_enabled), HW_ATL_B0_MAC_MIN + i); } @@ -995,6 +1071,7 @@ static int hw_atl_b0_hw_ring_tx_stop(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_tdm_tx_desc_en_set(self, 0U, ring->idx); + return aq_hw_err_from_flags(self); } @@ -1002,9 +1079,231 @@ static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self, struct aq_ring_s *ring) { hw_atl_rdm_rx_desc_en_set(self, 0U, ring->idx); + return aq_hw_err_from_flags(self); } +static int hw_atl_b0_tx_tc_mode_get(struct aq_hw_s *self, u32 *tc_mode) +{ + *tc_mode = hw_atl_rpb_tps_tx_tc_mode_get(self); + return aq_hw_err_from_flags(self); +} + +static int hw_atl_b0_rx_tc_mode_get(struct aq_hw_s *self, u32 *tc_mode) +{ + *tc_mode = hw_atl_rpb_rpf_rx_traf_class_mode_get(self); + return aq_hw_err_from_flags(self); +} + +#define get_ptp_ts_val_u64(self, indx) \ + ((u64)(hw_atl_pcs_ptp_clock_get(self, indx) & 0xffff)) + +static void hw_atl_b0_get_ptp_ts(struct aq_hw_s *self, u64 *stamp) +{ + u64 ns; + + hw_atl_pcs_ptp_clock_read_enable(self, 1); + hw_atl_pcs_ptp_clock_read_enable(self, 0); + ns = (get_ptp_ts_val_u64(self, 0) + + (get_ptp_ts_val_u64(self, 1) << 16)) * NSEC_PER_SEC + + (get_ptp_ts_val_u64(self, 3) + + (get_ptp_ts_val_u64(self, 4) << 16)); + + *stamp = ns + self->ptp_clk_offset; +} + +static void hw_atl_b0_adj_params_get(u64 freq, s64 adj, u32 *ns, u32 *fns) +{ + /* For accuracy, the digit is extended */ + s64 base_ns = ((adj + NSEC_PER_SEC) * NSEC_PER_SEC); + u64 nsi_frac = 0; + u64 nsi; + + base_ns = div64_s64(base_ns, freq); + nsi = div64_u64(base_ns, NSEC_PER_SEC); + + if (base_ns != nsi * NSEC_PER_SEC) { + s64 divisor = div64_s64((s64)NSEC_PER_SEC * NSEC_PER_SEC, + base_ns - nsi * NSEC_PER_SEC); + nsi_frac = div64_s64(FRAC_PER_NS * NSEC_PER_SEC, divisor); + } + + *ns = (u32)nsi; + *fns = (u32)nsi_frac; +} + +static void +hw_atl_b0_mac_adj_param_calc(struct hw_fw_request_ptp_adj_freq *ptp_adj_freq, + u64 phyfreq, u64 macfreq) +{ + s64 adj_fns_val; + s64 fns_in_sec_phy = phyfreq * (ptp_adj_freq->fns_phy + + FRAC_PER_NS * ptp_adj_freq->ns_phy); + s64 fns_in_sec_mac = macfreq * (ptp_adj_freq->fns_mac + + FRAC_PER_NS * ptp_adj_freq->ns_mac); + s64 fault_in_sec_phy = FRAC_PER_NS * NSEC_PER_SEC - fns_in_sec_phy; + s64 fault_in_sec_mac = FRAC_PER_NS * NSEC_PER_SEC - fns_in_sec_mac; + /* MAC MCP counter freq is macfreq / 4 */ + s64 diff_in_mcp_overflow = (fault_in_sec_mac - fault_in_sec_phy) * + 4 * FRAC_PER_NS; + + diff_in_mcp_overflow = div64_s64(diff_in_mcp_overflow, + AQ_HW_MAC_COUNTER_HZ); + adj_fns_val = (ptp_adj_freq->fns_mac + FRAC_PER_NS * + ptp_adj_freq->ns_mac) + diff_in_mcp_overflow; + + ptp_adj_freq->mac_ns_adj = div64_s64(adj_fns_val, FRAC_PER_NS); + ptp_adj_freq->mac_fns_adj = adj_fns_val - ptp_adj_freq->mac_ns_adj * + FRAC_PER_NS; +} + +static int hw_atl_b0_adj_sys_clock(struct aq_hw_s *self, s64 delta) +{ + self->ptp_clk_offset += delta; + + return 0; +} + +static int hw_atl_b0_set_sys_clock(struct aq_hw_s *self, u64 time, u64 ts) +{ + s64 delta = time - (self->ptp_clk_offset + ts); + + return hw_atl_b0_adj_sys_clock(self, delta); +} + +static int hw_atl_b0_ts_to_sys_clock(struct aq_hw_s *self, u64 ts, u64 *time) +{ + *time = self->ptp_clk_offset + ts; + return 0; +} + +static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb) +{ + struct hw_fw_request_iface fwreq; + size_t size; + + memset(&fwreq, 0, sizeof(fwreq)); + + fwreq.msg_id = HW_AQ_FW_REQUEST_PTP_ADJ_FREQ; + hw_atl_b0_adj_params_get(AQ_HW_MAC_COUNTER_HZ, ppb, + &fwreq.ptp_adj_freq.ns_mac, + &fwreq.ptp_adj_freq.fns_mac); + hw_atl_b0_adj_params_get(AQ_HW_PHY_COUNTER_HZ, ppb, + &fwreq.ptp_adj_freq.ns_phy, + &fwreq.ptp_adj_freq.fns_phy); + hw_atl_b0_mac_adj_param_calc(&fwreq.ptp_adj_freq, + AQ_HW_PHY_COUNTER_HZ, + AQ_HW_MAC_COUNTER_HZ); + + size = sizeof(fwreq.msg_id) + sizeof(fwreq.ptp_adj_freq); + return self->aq_fw_ops->send_fw_request(self, &fwreq, size); +} + +static int hw_atl_b0_gpio_pulse(struct aq_hw_s *self, u32 index, + u64 start, u32 period) +{ + struct hw_fw_request_iface fwreq; + size_t size; + + memset(&fwreq, 0, sizeof(fwreq)); + + fwreq.msg_id = HW_AQ_FW_REQUEST_PTP_GPIO_CTRL; + fwreq.ptp_gpio_ctrl.index = index; + fwreq.ptp_gpio_ctrl.period = period; + /* Apply time offset */ + fwreq.ptp_gpio_ctrl.start = start - self->ptp_clk_offset; + + size = sizeof(fwreq.msg_id) + sizeof(fwreq.ptp_gpio_ctrl); + return self->aq_fw_ops->send_fw_request(self, &fwreq, size); +} + +static int hw_atl_b0_extts_gpio_enable(struct aq_hw_s *self, u32 index, + u32 enable) +{ + /* Enable/disable Sync1588 GPIO Timestamping */ + aq_phy_write_reg(self, MDIO_MMD_PCS, 0xc611, enable ? 0x71 : 0); + + return 0; +} + +static int hw_atl_b0_get_sync_ts(struct aq_hw_s *self, u64 *ts) +{ + u64 sec_l; + u64 sec_h; + u64 nsec_l; + u64 nsec_h; + + if (!ts) + return -1; + + /* PTP external GPIO clock seconds count 15:0 */ + sec_l = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc914); + /* PTP external GPIO clock seconds count 31:16 */ + sec_h = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc915); + /* PTP external GPIO clock nanoseconds count 15:0 */ + nsec_l = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc916); + /* PTP external GPIO clock nanoseconds count 31:16 */ + nsec_h = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc917); + + *ts = (nsec_h << 16) + nsec_l + ((sec_h << 16) + sec_l) * NSEC_PER_SEC; + + return 0; +} + +static u16 hw_atl_b0_rx_extract_ts(struct aq_hw_s *self, u8 *p, + unsigned int len, u64 *timestamp) +{ + unsigned int offset = 14; + struct ethhdr *eth; + __be64 sec; + __be32 ns; + u8 *ptr; + + if (len <= offset || !timestamp) + return 0; + + /* The TIMESTAMP in the end of package has following format: + * (big-endian) + * struct { + * uint64_t sec; + * uint32_t ns; + * uint16_t stream_id; + * }; + */ + ptr = p + (len - offset); + memcpy(&sec, ptr, sizeof(sec)); + ptr += sizeof(sec); + memcpy(&ns, ptr, sizeof(ns)); + + *timestamp = (be64_to_cpu(sec) & 0xffffffffffffllu) * NSEC_PER_SEC + + be32_to_cpu(ns) + self->ptp_clk_offset; + + eth = (struct ethhdr *)p; + + return (eth->h_proto == htons(ETH_P_1588)) ? 12 : 14; +} + +static int hw_atl_b0_extract_hwts(struct aq_hw_s *self, u8 *p, unsigned int len, + u64 *timestamp) +{ + struct hw_atl_rxd_hwts_wb_s *hwts_wb = (struct hw_atl_rxd_hwts_wb_s *)p; + u64 tmp, sec, ns; + + sec = 0; + tmp = (hwts_wb->sec_lw0 >> 2) & 0x3ff; + sec += tmp; + tmp = (u64)((hwts_wb->sec_lw1 >> 16) & 0xffff) << 10; + sec += tmp; + tmp = (u64)(hwts_wb->sec_hw & 0xfff) << 26; + sec += tmp; + tmp = (u64)((hwts_wb->sec_hw >> 22) & 0x3ff) << 38; + sec += tmp; + ns = sec * NSEC_PER_SEC + hwts_wb->ns; + if (timestamp) + *timestamp = ns + self->ptp_clk_offset; + return 0; +} + static int hw_atl_b0_hw_fl3l4_clear(struct aq_hw_s *self, struct aq_rx_filter_l3l4 *data) { @@ -1038,7 +1337,8 @@ static int hw_atl_b0_hw_fl3l4_set(struct aq_hw_s *self, hw_atl_b0_hw_fl3l4_clear(self, data); - if (data->cmd) { + if (data->cmd & (HW_ATL_RX_ENABLE_CMP_DEST_ADDR_L3 | + HW_ATL_RX_ENABLE_CMP_SRC_ADDR_L3)) { if (!data->is_ipv6) { hw_atl_rpfl3l4_ipv4_dest_addr_set(self, location, @@ -1055,8 +1355,13 @@ static int hw_atl_b0_hw_fl3l4_set(struct aq_hw_s *self, data->ip_src); } } - hw_atl_rpf_l4_dpd_set(self, data->p_dst, location); - hw_atl_rpf_l4_spd_set(self, data->p_src, location); + + if (data->cmd & (HW_ATL_RX_ENABLE_CMP_DEST_PORT_L4 | + HW_ATL_RX_ENABLE_CMP_SRC_PORT_L4)) { + hw_atl_rpf_l4_dpd_set(self, data->p_dst, location); + hw_atl_rpf_l4_spd_set(self, data->p_src, location); + } + hw_atl_rpfl3l4_cmd_set(self, location, data->cmd); return aq_hw_err_from_flags(self); @@ -1141,6 +1446,31 @@ static int hw_atl_b0_hw_vlan_ctrl(struct aq_hw_s *self, bool enable) return aq_hw_err_from_flags(self); } +static int hw_atl_b0_set_loopback(struct aq_hw_s *self, u32 mode, bool enable) +{ + switch (mode) { + case AQ_HW_LOOPBACK_DMA_SYS: + hw_atl_tpb_tx_dma_sys_lbk_en_set(self, enable); + hw_atl_rpb_dma_sys_lbk_set(self, enable); + break; + case AQ_HW_LOOPBACK_PKT_SYS: + hw_atl_tpo_tx_pkt_sys_lbk_en_set(self, enable); + hw_atl_rpf_tpo_to_rpf_sys_lbk_set(self, enable); + break; + case AQ_HW_LOOPBACK_DMA_NET: + hw_atl_rpf_vlan_prom_mode_en_set(self, enable); + hw_atl_rpfl2promiscuous_mode_en_set(self, enable); + hw_atl_tpb_tx_tx_clk_gate_en_set(self, !enable); + hw_atl_tpb_tx_dma_net_lbk_en_set(self, enable); + hw_atl_rpb_dma_net_lbk_set(self, enable); + break; + default: + return -EINVAL; + } + + return 0; +} + const struct aq_hw_ops hw_atl_ops_b0 = { .hw_set_mac_address = hw_atl_b0_hw_mac_addr_set, .hw_init = hw_atl_b0_hw_init, @@ -1177,6 +1507,27 @@ const struct aq_hw_ops hw_atl_ops_b0 = { .hw_get_regs = hw_atl_utils_hw_get_regs, .hw_get_hw_stats = hw_atl_utils_get_hw_stats, .hw_get_fw_version = hw_atl_utils_get_fw_version, - .hw_set_offload = hw_atl_b0_hw_offload_set, - .hw_set_fc = hw_atl_b0_set_fc, + + .hw_tx_tc_mode_get = hw_atl_b0_tx_tc_mode_get, + .hw_rx_tc_mode_get = hw_atl_b0_rx_tc_mode_get, + + .hw_ring_hwts_rx_fill = hw_atl_b0_hw_ring_hwts_rx_fill, + .hw_ring_hwts_rx_receive = hw_atl_b0_hw_ring_hwts_rx_receive, + + .hw_get_ptp_ts = hw_atl_b0_get_ptp_ts, + .hw_adj_sys_clock = hw_atl_b0_adj_sys_clock, + .hw_set_sys_clock = hw_atl_b0_set_sys_clock, + .hw_ts_to_sys_clock = hw_atl_b0_ts_to_sys_clock, + .hw_adj_clock_freq = hw_atl_b0_adj_clock_freq, + .hw_gpio_pulse = hw_atl_b0_gpio_pulse, + .hw_extts_gpio_enable = hw_atl_b0_extts_gpio_enable, + .hw_get_sync_ts = hw_atl_b0_get_sync_ts, + .rx_extract_ts = hw_atl_b0_rx_extract_ts, + .extract_hwts = hw_atl_b0_extract_hwts, + .hw_set_offload = hw_atl_b0_hw_offload_set, + .hw_get_hw_stats = hw_atl_utils_get_hw_stats, + .hw_get_fw_version = hw_atl_utils_get_fw_version, + .hw_set_offload = hw_atl_b0_hw_offload_set, + .hw_set_loopback = hw_atl_b0_set_loopback, + .hw_set_fc = hw_atl_b0_set_fc, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0_internal.h index 808d8cd4252a..7ab23a1751d3 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0_internal.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0_internal.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_b0_internal.h: Definition of Atlantic B0 chip specific @@ -64,8 +64,11 @@ #define HW_ATL_B0_MPI_SPEED_MSK 0xFFFFU #define HW_ATL_B0_MPI_SPEED_SHIFT 16U -#define HW_ATL_B0_TXBUF_MAX 160U -#define HW_ATL_B0_RXBUF_MAX 320U +#define HW_ATL_B0_TXBUF_MAX 160U +#define HW_ATL_B0_PTP_TXBUF_SIZE 8U + +#define HW_ATL_B0_RXBUF_MAX 320U +#define HW_ATL_B0_PTP_RXBUF_SIZE 16U #define HW_ATL_B0_RSS_REDIRECTION_MAX 64U #define HW_ATL_B0_RSS_REDIRECTION_BITS 3U diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c index 6f340695e6bd..d1f68fc16291 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_llh.c: Definitions of bitfield and register access functions for @@ -563,6 +563,13 @@ void hw_atl_rpb_dma_sys_lbk_set(struct aq_hw_s *aq_hw, u32 dma_sys_lbk) HW_ATL_RPB_DMA_SYS_LBK_SHIFT, dma_sys_lbk); } +void hw_atl_rpb_dma_net_lbk_set(struct aq_hw_s *aq_hw, u32 dma_net_lbk) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL_RPB_DMA_NET_LBK_ADR, + HW_ATL_RPB_DMA_NET_LBK_MSK, + HW_ATL_RPB_DMA_NET_LBK_SHIFT, dma_net_lbk); +} + void hw_atl_rpb_rpf_rx_traf_class_mode_set(struct aq_hw_s *aq_hw, u32 rx_traf_class_mode) { @@ -572,6 +579,13 @@ void hw_atl_rpb_rpf_rx_traf_class_mode_set(struct aq_hw_s *aq_hw, rx_traf_class_mode); } +u32 hw_atl_rpb_rpf_rx_traf_class_mode_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg_bit(aq_hw, HW_ATL_RPB_RPF_RX_TC_MODE_ADR, + HW_ATL_RPB_RPF_RX_TC_MODE_MSK, + HW_ATL_RPB_RPF_RX_TC_MODE_SHIFT); +} + void hw_atl_rpb_rx_buff_en_set(struct aq_hw_s *aq_hw, u32 rx_buff_en) { aq_hw_write_reg_bit(aq_hw, HW_ATL_RPB_RX_BUF_EN_ADR, @@ -636,8 +650,8 @@ void hw_atl_rpb_rx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw, rx_pkt_buff_size_per_tc); } -void hw_atl_rpb_rx_xoff_en_per_tc_set(struct aq_hw_s *aq_hw, u32 rx_xoff_en_per_tc, - u32 buffer) +void hw_atl_rpb_rx_xoff_en_per_tc_set(struct aq_hw_s *aq_hw, + u32 rx_xoff_en_per_tc, u32 buffer) { aq_hw_write_reg_bit(aq_hw, HW_ATL_RPB_RXBXOFF_EN_ADR(buffer), HW_ATL_RPB_RXBXOFF_EN_MSK, @@ -1290,6 +1304,13 @@ void hw_atl_tpb_tx_buff_en_set(struct aq_hw_s *aq_hw, u32 tx_buff_en) HW_ATL_TPB_TX_BUF_EN_SHIFT, tx_buff_en); } +u32 hw_atl_rpb_tps_tx_tc_mode_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg_bit(aq_hw, HW_ATL_TPB_TX_TC_MODE_ADDR, + HW_ATL_TPB_TX_TC_MODE_MSK, + HW_ATL_TPB_TX_TC_MODE_SHIFT); +} + void hw_atl_rpb_tps_tx_tc_mode_set(struct aq_hw_s *aq_hw, u32 tx_traf_class_mode) { @@ -1327,7 +1348,26 @@ void hw_atl_tpb_tx_dma_sys_lbk_en_set(struct aq_hw_s *aq_hw, u32 tx_dma_sys_lbk_ tx_dma_sys_lbk_en); } +void hw_atl_tpb_tx_dma_net_lbk_en_set(struct aq_hw_s *aq_hw, + u32 tx_dma_net_lbk_en) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL_TPB_DMA_NET_LBK_ADR, + HW_ATL_TPB_DMA_NET_LBK_MSK, + HW_ATL_TPB_DMA_NET_LBK_SHIFT, + tx_dma_net_lbk_en); +} + +void hw_atl_tpb_tx_tx_clk_gate_en_set(struct aq_hw_s *aq_hw, + u32 tx_clk_gate_en) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL_TPB_TX_CLK_GATE_EN_ADR, + HW_ATL_TPB_TX_CLK_GATE_EN_MSK, + HW_ATL_TPB_TX_CLK_GATE_EN_SHIFT, + tx_clk_gate_en); +} + void hw_atl_tpb_tx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw, + u32 tx_pkt_buff_size_per_tc, u32 buffer) { aq_hw_write_reg_bit(aq_hw, HW_ATL_TPB_TXBBUF_SIZE_ADR(buffer), @@ -1526,6 +1566,20 @@ void hw_atl_reg_glb_cpu_scratch_scp_set(struct aq_hw_s *aq_hw, glb_cpu_scratch_scp); } +void hw_atl_pcs_ptp_clock_read_enable(struct aq_hw_s *aq_hw, + u32 ptp_clock_read_enable) +{ + aq_hw_write_reg_bit(aq_hw, HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_ADR, + HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_MSK, + HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_SHIFT, + ptp_clock_read_enable); +} + +u32 hw_atl_pcs_ptp_clock_get(struct aq_hw_s *aq_hw, u32 index) +{ + return aq_hw_read_reg(aq_hw, HW_ATL_PCS_PTP_TS_VAL_ADDR(index)); +} + void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr) { aq_hw_write_reg_bit(aq_hw, HW_ATL_MCP_UP_FORCE_INTERRUPT_ADR, @@ -1616,6 +1670,11 @@ u32 hw_atl_sem_ram_get(struct aq_hw_s *self) return hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM); } +u32 hw_atl_sem_mdio_get(struct aq_hw_s *self) +{ + return hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_MDIO); +} + u32 hw_atl_scrpad_get(struct aq_hw_s *aq_hw, u32 scratch_scp) { return aq_hw_read_reg(aq_hw, @@ -1631,3 +1690,60 @@ u32 hw_atl_scrpad25_get(struct aq_hw_s *self) { return hw_atl_scrpad_get(self, 0x18); } + +void hw_atl_glb_mdio_iface1_set(struct aq_hw_s *aq_hw, u32 value) +{ + aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(1), value); +} + +u32 hw_atl_glb_mdio_iface1_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(1)); +} + +void hw_atl_glb_mdio_iface2_set(struct aq_hw_s *aq_hw, u32 value) +{ + aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(2), value); +} + +u32 hw_atl_glb_mdio_iface2_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(2)); +} + +void hw_atl_glb_mdio_iface3_set(struct aq_hw_s *aq_hw, u32 value) +{ + aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(3), value); +} + +u32 hw_atl_glb_mdio_iface3_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(3)); +} + +void hw_atl_glb_mdio_iface4_set(struct aq_hw_s *aq_hw, u32 value) +{ + aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(4), value); +} + +u32 hw_atl_glb_mdio_iface4_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(4)); +} + +void hw_atl_glb_mdio_iface5_set(struct aq_hw_s *aq_hw, u32 value) +{ + aq_hw_write_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(5), value); +} + +u32 hw_atl_glb_mdio_iface5_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg(aq_hw, HW_ATL_GLB_MDIO_IFACE_N_ADR(5)); +} + +u32 hw_atl_mdio_busy_get(struct aq_hw_s *aq_hw) +{ + return aq_hw_read_reg_bit(aq_hw, HW_ATL_MDIO_BUSY_ADR, + HW_ATL_MDIO_BUSY_MSK, + HW_ATL_MDIO_BUSY_SHIFT); +} diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h index c3ee278c3747..62992b23c0e8 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_llh.h: Declarations of bitfield and register access functions for @@ -288,10 +288,16 @@ void hw_atl_reg_glb_cpu_scratch_scp_set(struct aq_hw_s *aq_hw, /* set dma system loopback */ void hw_atl_rpb_dma_sys_lbk_set(struct aq_hw_s *aq_hw, u32 dma_sys_lbk); +/* set dma network loopback */ +void hw_atl_rpb_dma_net_lbk_set(struct aq_hw_s *aq_hw, u32 dma_net_lbk); + /* set rx traffic class mode */ void hw_atl_rpb_rpf_rx_traf_class_mode_set(struct aq_hw_s *aq_hw, u32 rx_traf_class_mode); +/* get rx traffic class mode */ +u32 hw_atl_rpb_rpf_rx_traf_class_mode_get(struct aq_hw_s *aq_hw); + /* set rx buffer enable */ void hw_atl_rpb_rx_buff_en_set(struct aq_hw_s *aq_hw, u32 rx_buff_en); @@ -306,7 +312,8 @@ void hw_atl_rpb_rx_buff_lo_threshold_per_tc_set(struct aq_hw_s *aq_hw, u32 buffer); /* set rx flow control mode */ -void hw_atl_rpb_rx_flow_ctl_mode_set(struct aq_hw_s *aq_hw, u32 rx_flow_ctl_mode); +void hw_atl_rpb_rx_flow_ctl_mode_set(struct aq_hw_s *aq_hw, + u32 rx_flow_ctl_mode); /* set rx packet buffer size (per tc) */ void hw_atl_rpb_rx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw, @@ -320,7 +327,8 @@ void hw_atl_rdm_rx_dma_desc_cache_init_tgl(struct aq_hw_s *aq_hw); u32 hw_atl_rdm_rx_dma_desc_cache_init_done_get(struct aq_hw_s *aq_hw); /* set rx xoff enable (per tc) */ -void hw_atl_rpb_rx_xoff_en_per_tc_set(struct aq_hw_s *aq_hw, u32 rx_xoff_en_per_tc, +void hw_atl_rpb_rx_xoff_en_per_tc_set(struct aq_hw_s *aq_hw, + u32 rx_xoff_en_per_tc, u32 buffer); /* rpf */ @@ -605,6 +613,9 @@ void hw_atl_thm_lso_tcp_flag_of_middle_pkt_set(struct aq_hw_s *aq_hw, void hw_atl_rpb_tps_tx_tc_mode_set(struct aq_hw_s *aq_hw, u32 tx_traf_class_mode); +/* get TX Traffic Class Mode */ +u32 hw_atl_rpb_tps_tx_tc_mode_get(struct aq_hw_s *aq_hw); + /* set tx buffer enable */ void hw_atl_tpb_tx_buff_en_set(struct aq_hw_s *aq_hw, u32 tx_buff_en); @@ -621,9 +632,18 @@ void hw_atl_tpb_tx_buff_lo_threshold_per_tc_set(struct aq_hw_s *aq_hw, /* set tx dma system loopback enable */ void hw_atl_tpb_tx_dma_sys_lbk_en_set(struct aq_hw_s *aq_hw, u32 tx_dma_sys_lbk_en); +/* set tx dma network loopback enable */ +void hw_atl_tpb_tx_dma_net_lbk_en_set(struct aq_hw_s *aq_hw, + u32 tx_dma_net_lbk_en); + +/* set tx clock gating enable */ +void hw_atl_tpb_tx_tx_clk_gate_en_set(struct aq_hw_s *aq_hw, + u32 tx_clk_gate_en); + /* set tx packet buffer size (per tc) */ void hw_atl_tpb_tx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw, - u32 tx_pkt_buff_size_per_tc, u32 buffer); + u32 tx_pkt_buff_size_per_tc, + u32 buffer); /* set tx path pad insert enable */ void hw_atl_tpb_tx_path_scp_ins_en_set(struct aq_hw_s *aq_hw, u32 tx_path_scp_ins_en); @@ -715,6 +735,12 @@ void hw_atl_msm_reg_wr_strobe_set(struct aq_hw_s *aq_hw, u32 reg_wr_strobe); /* set pci register reset disable */ void hw_atl_pci_pci_reg_res_dis_set(struct aq_hw_s *aq_hw, u32 pci_reg_res_dis); +/* pcs */ +void hw_atl_pcs_ptp_clock_read_enable(struct aq_hw_s *aq_hw, + u32 ptp_clock_read_enable); + +u32 hw_atl_pcs_ptp_clock_get(struct aq_hw_s *aq_hw, u32 index); + /* set uP Force Interrupt */ void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr); @@ -752,9 +778,44 @@ void hw_atl_rpfl3l4_ipv6_src_addr_set(struct aq_hw_s *aq_hw, u8 location, void hw_atl_rpfl3l4_ipv6_dest_addr_set(struct aq_hw_s *aq_hw, u8 location, u32 *ipv6_dest); +/* set Global MDIO Interface 1 */ +void hw_atl_glb_mdio_iface1_set(struct aq_hw_s *hw, u32 value); + +/* get Global MDIO Interface 1 */ +u32 hw_atl_glb_mdio_iface1_get(struct aq_hw_s *hw); + +/* set Global MDIO Interface 2 */ +void hw_atl_glb_mdio_iface2_set(struct aq_hw_s *hw, u32 value); + +/* get Global MDIO Interface 2 */ +u32 hw_atl_glb_mdio_iface2_get(struct aq_hw_s *hw); + +/* set Global MDIO Interface 3 */ +void hw_atl_glb_mdio_iface3_set(struct aq_hw_s *hw, u32 value); + +/* get Global MDIO Interface 3 */ +u32 hw_atl_glb_mdio_iface3_get(struct aq_hw_s *hw); + +/* set Global MDIO Interface 4 */ +void hw_atl_glb_mdio_iface4_set(struct aq_hw_s *hw, u32 value); + +/* get Global MDIO Interface 4 */ +u32 hw_atl_glb_mdio_iface4_get(struct aq_hw_s *hw); + +/* set Global MDIO Interface 5 */ +void hw_atl_glb_mdio_iface5_set(struct aq_hw_s *hw, u32 value); + +/* get Global MDIO Interface 5 */ +u32 hw_atl_glb_mdio_iface5_get(struct aq_hw_s *hw); + +u32 hw_atl_mdio_busy_get(struct aq_hw_s *aq_hw); + /* get global microprocessor ram semaphore */ u32 hw_atl_sem_ram_get(struct aq_hw_s *self); +/* get global microprocessor mdio semaphore */ +u32 hw_atl_sem_mdio_get(struct aq_hw_s *self); + /* get global microprocessor scratch pad register */ u32 hw_atl_scrpad_get(struct aq_hw_s *aq_hw, u32 scratch_scp); diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h index 35887ad89025..18de2f7b8959 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_llh_internal.h: Preprocessor definitions @@ -554,6 +554,24 @@ /* default value of bitfield dma_sys_loopback */ #define HW_ATL_RPB_DMA_SYS_LBK_DEFAULT 0x0 +/* rx dma_net_loopback bitfield definitions + * preprocessor definitions for the bitfield "dma_net_loopback". + * port="pif_rpb_dma_net_lbk_i" + */ + +/* register address for bitfield dma_net_loopback */ +#define HW_ATL_RPB_DMA_NET_LBK_ADR 0x00005000 +/* bitmask for bitfield dma_net_loopback */ +#define HW_ATL_RPB_DMA_NET_LBK_MSK 0x00000010 +/* inverted bitmask for bitfield dma_net_loopback */ +#define HW_ATL_RPB_DMA_NET_LBK_MSKN 0xffffffef +/* lower bit position of bitfield dma_net_loopback */ +#define HW_ATL_RPB_DMA_NET_LBK_SHIFT 4 +/* width of bitfield dma_net_loopback */ +#define HW_ATL_RPB_DMA_NET_LBK_WIDTH 1 +/* default value of bitfield dma_net_loopback */ +#define HW_ATL_RPB_DMA_NET_LBK_DEFAULT 0x0 + /* rx rx_tc_mode bitfield definitions * preprocessor definitions for the bitfield "rx_tc_mode". * port="pif_rpb_rx_tc_mode_i,pif_rpf_rx_tc_mode_i" @@ -1308,6 +1326,52 @@ /* default value of bitfield et_val{f}[f:0] */ #define HW_ATL_RPF_ET_VALF_DEFAULT 0x0 +/* RX l3_l4_en{F} Bitfield Definitions + * Preprocessor definitions for the bitfield "l3_l4_en{F}". + * Parameter: filter {F} | stride size 0x4 | range [0, 7] + * PORT="pif_rpf_l3_l4_en_i[0]" + */ + +#define HW_ATL_RPF_L3_REG_CTRL_ADR(filter) (0x00005380 + (filter) * 0x4) + +/* RX rpf_l3_sa{D}[1F:0] Bitfield Definitions + * Preprocessor definitions for the bitfield "l3_sa{D}[1F:0]". + * Parameter: location {D} | stride size 0x4 | range [0, 7] + * PORT="pif_rpf_l3_sa0_i[31:0]" + */ + +/* Register address for bitfield pif_rpf_l3_sa0_i[31:0] */ +#define HW_ATL_RPF_L3_SRCA_ADR(filter) (0x000053B0 + (filter) * 0x4) +/* Bitmask for bitfield l3_sa0[1F:0] */ +#define HW_ATL_RPF_L3_SRCA_MSK 0xFFFFFFFFu +/* Inverted bitmask for bitfield l3_sa0[1F:0] */ +#define HW_ATL_RPF_L3_SRCA_MSKN 0xFFFFFFFFu +/* Lower bit position of bitfield l3_sa0[1F:0] */ +#define HW_ATL_RPF_L3_SRCA_SHIFT 0 +/* Width of bitfield l3_sa0[1F:0] */ +#define HW_ATL_RPF_L3_SRCA_WIDTH 32 +/* Default value of bitfield l3_sa0[1F:0] */ +#define HW_ATL_RPF_L3_SRCA_DEFAULT 0x0 + +/* RX rpf_l3_da{D}[1F:0] Bitfield Definitions + * Preprocessor definitions for the bitfield "l3_da{D}[1F:0]". + * Parameter: location {D} | stride size 0x4 | range [0, 7] + * PORT="pif_rpf_l3_da0_i[31:0]" + */ + + /* Register address for bitfield pif_rpf_l3_da0_i[31:0] */ +#define HW_ATL_RPF_L3_DSTA_ADR(filter) (0x000053B0 + (filter) * 0x4) +/* Bitmask for bitfield l3_da0[1F:0] */ +#define HW_ATL_RPF_L3_DSTA_MSK 0xFFFFFFFFu +/* Inverted bitmask for bitfield l3_da0[1F:0] */ +#define HW_ATL_RPF_L3_DSTA_MSKN 0xFFFFFFFFu +/* Lower bit position of bitfield l3_da0[1F:0] */ +#define HW_ATL_RPF_L3_DSTA_SHIFT 0 +/* Width of bitfield l3_da0[1F:0] */ +#define HW_ATL_RPF_L3_DSTA_WIDTH 32 +/* Default value of bitfield l3_da0[1F:0] */ +#define HW_ATL_RPF_L3_DSTA_DEFAULT 0x0 + /* RX l4_sp{D}[F:0] Bitfield Definitions * Preprocessor definitions for the bitfield "l4_sp{D}[F:0]". * Parameter: srcport {D} | stride size 0x4 | range [0, 7] @@ -2061,6 +2125,24 @@ /* default value of bitfield dma_sys_loopback */ #define HW_ATL_TPB_DMA_SYS_LBK_DEFAULT 0x0 +/* tx dma_net_loopback bitfield definitions + * preprocessor definitions for the bitfield "dma_net_loopback". + * port="pif_tpb_dma_net_lbk_i" + */ + +/* register address for bitfield dma_net_loopback */ +#define HW_ATL_TPB_DMA_NET_LBK_ADR 0x00007000 +/* bitmask for bitfield dma_net_loopback */ +#define HW_ATL_TPB_DMA_NET_LBK_MSK 0x00000010 +/* inverted bitmask for bitfield dma_net_loopback */ +#define HW_ATL_TPB_DMA_NET_LBK_MSKN 0xffffffef +/* lower bit position of bitfield dma_net_loopback */ +#define HW_ATL_TPB_DMA_NET_LBK_SHIFT 4 +/* width of bitfield dma_net_loopback */ +#define HW_ATL_TPB_DMA_NET_LBK_WIDTH 1 +/* default value of bitfield dma_net_loopback */ +#define HW_ATL_TPB_DMA_NET_LBK_DEFAULT 0x0 + /* tx tx{b}_buf_size[7:0] bitfield definitions * preprocessor definitions for the bitfield "tx{b}_buf_size[7:0]". * parameter: buffer {b} | stride size 0x10 | range [0, 7] @@ -2098,6 +2180,24 @@ /* default value of bitfield tx_scp_ins_en */ #define HW_ATL_TPB_TX_SCP_INS_EN_DEFAULT 0x0 +/* tx tx_clk_gate_en bitfield definitions + * preprocessor definitions for the bitfield "tx_clk_gate_en". + * port="pif_tpb_clk_gate_en_i" + */ + +/* register address for bitfield tx_clk_gate_en */ +#define HW_ATL_TPB_TX_CLK_GATE_EN_ADR 0x00007900 +/* bitmask for bitfield tx_clk_gate_en */ +#define HW_ATL_TPB_TX_CLK_GATE_EN_MSK 0x00000010 +/* inverted bitmask for bitfield tx_clk_gate_en */ +#define HW_ATL_TPB_TX_CLK_GATE_EN_MSKN 0xffffffef +/* lower bit position of bitfield tx_clk_gate_en */ +#define HW_ATL_TPB_TX_CLK_GATE_EN_SHIFT 4 +/* width of bitfield tx_clk_gate_en */ +#define HW_ATL_TPB_TX_CLK_GATE_EN_WIDTH 1 +/* default value of bitfield tx_clk_gate_en */ +#define HW_ATL_TPB_TX_CLK_GATE_EN_DEFAULT 0x1 + /* tx ipv4_chk_en bitfield definitions * preprocessor definitions for the bitfield "ipv4_chk_en". * port="pif_tpo_ipv4_chk_en_i" @@ -2440,6 +2540,22 @@ /* default value of bitfield register write strobe */ #define HW_ATL_MSM_REG_WR_STROBE_DEFAULT 0x0 +/* register address for bitfield PTP Digital Clock Read Enable */ +#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_ADR 0x00004628 +/* bitmask for bitfield PTP Digital Clock Read Enable */ +#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_MSK 0x00000010 +/* inverted bitmask for bitfield PTP Digital Clock Read Enable */ +#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_MSKN 0xFFFFFFEF +/* lower bit position of bitfield PTP Digital Clock Read Enable */ +#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_SHIFT 4 +/* width of bitfield PTP Digital Clock Read Enable */ +#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_WIDTH 1 +/* default value of bitfield PTP Digital Clock Read Enable */ +#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_DEFAULT 0x0 + +/* register address for ptp counter reading */ +#define HW_ATL_PCS_PTP_TS_VAL_ADDR(index) (0x00004900 + (index) * 0x4) + /* mif soft reset bitfield definitions * preprocessor definitions for the bitfield "soft reset". * port="pif_glb_res_i" @@ -2532,50 +2648,121 @@ /* default value of bitfield uP Force Interrupt */ #define HW_ATL_MCP_UP_FORCE_INTERRUPT_DEFAULT 0x0 -#define HW_ATL_RX_CTRL_ADDR_BEGIN_FL3L4 0x00005380 -#define HW_ATL_RX_SRCA_ADDR_BEGIN_FL3L4 0x000053B0 -#define HW_ATL_RX_DESTA_ADDR_BEGIN_FL3L4 0x000053D0 - -#define HW_ATL_RPF_L3_REG_CTRL_ADR(location) (0x00005380 + (location) * 0x4) - -/* RX rpf_l3_sa{D}[1F:0] Bitfield Definitions - * Preprocessor definitions for the bitfield "l3_sa{D}[1F:0]". - * Parameter: location {D} | stride size 0x4 | range [0, 7] - * PORT="pif_rpf_l3_sa0_i[31:0]" - */ - -/* Register address for bitfield pif_rpf_l3_sa0_i[31:0] */ -#define HW_ATL_RPF_L3_SRCA_ADR(location) (0x000053B0 + (location) * 0x4) -/* Bitmask for bitfield l3_sa0[1F:0] */ -#define HW_ATL_RPF_L3_SRCA_MSK 0xFFFFFFFFu -/* Inverted bitmask for bitfield l3_sa0[1F:0] */ -#define HW_ATL_RPF_L3_SRCA_MSKN 0xFFFFFFFFu -/* Lower bit position of bitfield l3_sa0[1F:0] */ -#define HW_ATL_RPF_L3_SRCA_SHIFT 0 -/* Width of bitfield l3_sa0[1F:0] */ -#define HW_ATL_RPF_L3_SRCA_WIDTH 32 -/* Default value of bitfield l3_sa0[1F:0] */ -#define HW_ATL_RPF_L3_SRCA_DEFAULT 0x0 - -/* RX rpf_l3_da{D}[1F:0] Bitfield Definitions - * Preprocessor definitions for the bitfield "l3_da{D}[1F:0]". - * Parameter: location {D} | stride size 0x4 | range [0, 7] - * PORT="pif_rpf_l3_da0_i[31:0]" - */ - - /* Register address for bitfield pif_rpf_l3_da0_i[31:0] */ -#define HW_ATL_RPF_L3_DSTA_ADR(location) (0x000053B0 + (location) * 0x4) -/* Bitmask for bitfield l3_da0[1F:0] */ -#define HW_ATL_RPF_L3_DSTA_MSK 0xFFFFFFFFu -/* Inverted bitmask for bitfield l3_da0[1F:0] */ -#define HW_ATL_RPF_L3_DSTA_MSKN 0xFFFFFFFFu -/* Lower bit position of bitfield l3_da0[1F:0] */ -#define HW_ATL_RPF_L3_DSTA_SHIFT 0 -/* Width of bitfield l3_da0[1F:0] */ -#define HW_ATL_RPF_L3_DSTA_WIDTH 32 -/* Default value of bitfield l3_da0[1F:0] */ -#define HW_ATL_RPF_L3_DSTA_DEFAULT 0x0 - +/* Preprocessor definitions for Global MDIO Interfaces + * Address: 0x00000280 + 0x4 * Number of interface + */ +#define HW_ATL_GLB_MDIO_IFACE_ADDR_BEGIN 0x00000280u + +#define HW_ATL_GLB_MDIO_IFACE_N_ADR(number) \ + (HW_ATL_GLB_MDIO_IFACE_ADDR_BEGIN + (((number) - 1) * 0x4)) + +/* MIF MDIO Busy Bitfield Definitions + * Preprocessor definitions for the bitfield "MDIO Busy". + * PORT="mdio_pif_busy_o" + */ + +/* Register address for bitfield MDIO Busy */ +#define HW_ATL_MDIO_BUSY_ADR 0x00000284 +/* Bitmask for bitfield MDIO Busy */ +#define HW_ATL_MDIO_BUSY_MSK 0x80000000 +/* Inverted bitmask for bitfield MDIO Busy */ +#define HW_ATL_MDIO_BUSY_MSKN 0x7FFFFFFF +/* Lower bit position of bitfield MDIO Busy */ +#define HW_ATL_MDIO_BUSY_SHIFT 31 +/* Width of bitfield MDIO Busy */ +#define HW_ATL_MDIO_BUSY_WIDTH 1 + +/* MIF MDIO Execute Operation Bitfield Definitions + * Preprocessor definitions for the bitfield "MDIO Execute Operation". + * PORT="pif_mdio_op_start_i" + */ + +/* Register address for bitfield MDIO Execute Operation */ +#define HW_ATL_MDIO_EXECUTE_OPERATION_ADR 0x00000284 +/* Bitmask for bitfield MDIO Execute Operation */ +#define HW_ATL_MDIO_EXECUTE_OPERATION_MSK 0x00008000 +/* Inverted bitmask for bitfield MDIO Execute Operation */ +#define HW_ATL_MDIO_EXECUTE_OPERATION_MSKN 0xFFFF7FFF +/* Lower bit position of bitfield MDIO Execute Operation */ +#define HW_ATL_MDIO_EXECUTE_OPERATION_SHIFT 15 +/* Width of bitfield MDIO Execute Operation */ +#define HW_ATL_MDIO_EXECUTE_OPERATION_WIDTH 1 +/* Default value of bitfield MDIO Execute Operation */ +#define HW_ATL_MDIO_EXECUTE_OPERATION_DEFAULT 0x0 + +/* MIF Op Mode [1:0] Bitfield Definitions + * Preprocessor definitions for the bitfield "Op Mode [1:0]". + * PORT="pif_mdio_mode_i[1:0]" + */ + +/* Register address for bitfield Op Mode [1:0] */ +#define HW_ATL_MDIO_OP_MODE_ADR 0x00000284 +/* Bitmask for bitfield Op Mode [1:0] */ +#define HW_ATL_MDIO_OP_MODE_MSK 0x00003000 +/* Inverted bitmask for bitfield Op Mode [1:0] */ +#define HW_ATL_MDIO_OP_MODE_MSKN 0xFFFFCFFF +/* Lower bit position of bitfield Op Mode [1:0] */ +#define HW_ATL_MDIO_OP_MODE_SHIFT 12 +/* Width of bitfield Op Mode [1:0] */ +#define HW_ATL_MDIO_OP_MODE_WIDTH 2 +/* Default value of bitfield Op Mode [1:0] */ +#define HW_ATL_MDIO_OP_MODE_DEFAULT 0x0 + +/* MIF PHY address Bitfield Definitions + * Preprocessor definitions for the bitfield "PHY address". + * PORT="pif_mdio_phy_addr_i[9:0]" + */ + +/* Register address for bitfield PHY address */ +#define HW_ATL_MDIO_PHY_ADDRESS_ADR 0x00000284 +/* Bitmask for bitfield PHY address */ +#define HW_ATL_MDIO_PHY_ADDRESS_MSK 0x000003FF +/* Inverted bitmask for bitfield PHY address */ +#define HW_ATL_MDIO_PHY_ADDRESS_MSKN 0xFFFFFC00 +/* Lower bit position of bitfield PHY address */ +#define HW_ATL_MDIO_PHY_ADDRESS_SHIFT 0 +/* Width of bitfield PHY address */ +#define HW_ATL_MDIO_PHY_ADDRESS_WIDTH 10 +/* Default value of bitfield PHY address */ +#define HW_ATL_MDIO_PHY_ADDRESS_DEFAULT 0x0 + +/* MIF MDIO WriteData [F:0] Bitfield Definitions + * Preprocessor definitions for the bitfield "MDIO WriteData [F:0]". + * PORT="pif_mdio_wdata_i[15:0]" + */ + +/* Register address for bitfield MDIO WriteData [F:0] */ +#define HW_ATL_MDIO_WRITE_DATA_ADR 0x00000288 +/* Bitmask for bitfield MDIO WriteData [F:0] */ +#define HW_ATL_MDIO_WRITE_DATA_MSK 0x0000FFFF +/* Inverted bitmask for bitfield MDIO WriteData [F:0] */ +#define HW_ATL_MDIO_WRITE_DATA_MSKN 0xFFFF0000 +/* Lower bit position of bitfield MDIO WriteData [F:0] */ +#define HW_ATL_MDIO_WRITE_DATA_SHIFT 0 +/* Width of bitfield MDIO WriteData [F:0] */ +#define HW_ATL_MDIO_WRITE_DATA_WIDTH 16 +/* Default value of bitfield MDIO WriteData [F:0] */ +#define HW_ATL_MDIO_WRITE_DATA_DEFAULT 0x0 + +/* MIF MDIO Address [F:0] Bitfield Definitions + * Preprocessor definitions for the bitfield "MDIO Address [F:0]". + * PORT="pif_mdio_addr_i[15:0]" + */ + +/* Register address for bitfield MDIO Address [F:0] */ +#define HW_ATL_MDIO_ADDRESS_ADR 0x0000028C +/* Bitmask for bitfield MDIO Address [F:0] */ +#define HW_ATL_MDIO_ADDRESS_MSK 0x0000FFFF +/* Inverted bitmask for bitfield MDIO Address [F:0] */ +#define HW_ATL_MDIO_ADDRESS_MSKN 0xFFFF0000 +/* Lower bit position of bitfield MDIO Address [F:0] */ +#define HW_ATL_MDIO_ADDRESS_SHIFT 0 +/* Width of bitfield MDIO Address [F:0] */ +#define HW_ATL_MDIO_ADDRESS_WIDTH 16 +/* Default value of bitfield MDIO Address [F:0] */ +#define HW_ATL_MDIO_ADDRESS_DEFAULT 0x0 + +#define HW_ATL_FW_SM_MDIO 0x0U #define HW_ATL_FW_SM_RAM 0x2U #endif /* HW_ATL_LLH_INTERNAL_H */ 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 52646855495e..8910b62e67ed 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 @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_utils.c: Definition of common functions for Atlantic hardware @@ -47,6 +47,11 @@ #define FORCE_FLASHLESS 0 +enum mcp_area { + MCP_AREA_CONFIG = 0x80000000, + MCP_AREA_SETTINGS = 0x20000000, +}; + static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual); static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, @@ -87,6 +92,7 @@ int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops) } self->aq_fw_ops = *fw_ops; err = self->aq_fw_ops->init(self); + return err; } @@ -237,9 +243,9 @@ static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self) int hw_atl_utils_soft_reset(struct aq_hw_s *self) { - int k; u32 boot_exit_code = 0; u32 val; + int k; for (k = 0; k < 1000; ++k) { u32 flb_status = aq_hw_read_reg(self, @@ -327,11 +333,75 @@ err_exit: return err; } -static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, - u32 cnt) +static int hw_atl_utils_write_b1_mbox(struct aq_hw_s *self, u32 addr, + u32 *p, u32 cnt, enum mcp_area area) { + u32 data_offset = 0; + u32 offset = addr; + int err = 0; u32 val; + + switch (area) { + case MCP_AREA_CONFIG: + offset -= self->rpc_addr; + break; + + case MCP_AREA_SETTINGS: + offset -= self->settings_addr; + break; + } + + offset = offset / sizeof(u32); + + for (; data_offset < cnt; ++data_offset, ++offset) { + aq_hw_write_reg(self, 0x328, p[data_offset]); + aq_hw_write_reg(self, 0x32C, + (area | (0xFFFF & (offset * 4)))); + hw_atl_mcp_up_force_intr_set(self, 1); + /* 1000 times by 10us = 10ms */ + err = readx_poll_timeout_atomic(hw_atl_scrpad12_get, + self, val, + (val & 0xF0000000) != + area, + 10U, 10000U); + + if (err < 0) + break; + } + + return err; +} + +static int hw_atl_utils_write_b0_mbox(struct aq_hw_s *self, u32 addr, + u32 *p, u32 cnt) +{ + u32 offset = 0; int err = 0; + u32 val; + + aq_hw_write_reg(self, 0x208, addr); + + for (; offset < cnt; ++offset) { + aq_hw_write_reg(self, 0x20C, p[offset]); + aq_hw_write_reg(self, 0x200, 0xC000); + + err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get, + self, val, + (val & 0x100) == 0U, + 10U, 10000U); + + if (err < 0) + break; + } + + return err; +} + +static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 addr, u32 *p, + u32 cnt, enum mcp_area area) +{ + int err = 0; + u32 val; err = readx_poll_timeout_atomic(hw_atl_sem_ram_get, self, val, val == 1U, @@ -339,54 +409,47 @@ static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, if (err < 0) goto err_exit; - if (IS_CHIP_FEATURE(REVISION_B1)) { - u32 offset = 0; - - for (; offset < cnt; ++offset) { - aq_hw_write_reg(self, 0x328, p[offset]); - aq_hw_write_reg(self, 0x32C, - (0x80000000 | (0xFFFF & (offset * 4)))); - hw_atl_mcp_up_force_intr_set(self, 1); - /* 1000 times by 10us = 10ms */ - err = readx_poll_timeout_atomic(hw_atl_scrpad12_get, - self, val, - (val & 0xF0000000) != - 0x80000000, - 10U, 10000U); - } - } else { - u32 offset = 0; - - aq_hw_write_reg(self, 0x208, a); + if (IS_CHIP_FEATURE(REVISION_B1)) + err = hw_atl_utils_write_b1_mbox(self, addr, p, cnt, area); + else + err = hw_atl_utils_write_b0_mbox(self, addr, p, cnt); - for (; offset < cnt; ++offset) { - aq_hw_write_reg(self, 0x20C, p[offset]); - aq_hw_write_reg(self, 0x200, 0xC000); + hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); - err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get, - self, val, - (val & 0x100) == 0, - 1000U, 10000U); - } - } + if (err < 0) + goto err_exit; - hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM); + err = aq_hw_err_from_flags(self); err_exit: return err; } +int hw_atl_write_fwcfg_dwords(struct aq_hw_s *self, u32 *p, u32 cnt) +{ + return hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, p, + cnt, MCP_AREA_CONFIG); +} + +int hw_atl_write_fwsettings_dwords(struct aq_hw_s *self, u32 offset, u32 *p, + u32 cnt) +{ + return hw_atl_utils_fw_upload_dwords(self, self->settings_addr + offset, + p, cnt, MCP_AREA_SETTINGS); +} + static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual) { - int err = 0; const u32 dw_major_mask = 0xff000000U; const u32 dw_minor_mask = 0x00ffffffU; + int err = 0; err = (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0; if (err < 0) goto err_exit; err = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ? -EOPNOTSUPP : 0; + err_exit: return err; } @@ -431,17 +494,16 @@ struct aq_hw_atl_utils_fw_rpc_tid_s { int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size) { - int err = 0; struct aq_hw_atl_utils_fw_rpc_tid_s sw; + int err = 0; if (!IS_CHIP_FEATURE(MIPS)) { err = -1; goto err_exit; } - err = hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, - (u32 *)(void *)&self->rpc, - (rpc_size + sizeof(u32) - - sizeof(u8)) / sizeof(u32)); + err = hw_atl_write_fwcfg_dwords(self, (u32 *)(void *)&self->rpc, + (rpc_size + sizeof(u32) - + sizeof(u8)) / sizeof(u32)); if (err < 0) goto err_exit; @@ -456,9 +518,9 @@ err_exit: int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self, struct hw_atl_utils_fw_rpc **rpc) { - int err = 0; struct aq_hw_atl_utils_fw_rpc_tid_s sw; struct aq_hw_atl_utils_fw_rpc_tid_s fw; + int err = 0; do { sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR); @@ -562,10 +624,10 @@ static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed) static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, enum hal_atl_utils_fw_state_e state) { - int err = 0; - u32 transaction_id = 0; - struct hw_atl_utils_mbox_header mbox; u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR); + struct hw_atl_utils_mbox_header mbox; + u32 transaction_id = 0; + int err = 0; if (state == MPI_RESET) { hw_atl_utils_mpi_read_mbox(self, &mbox); @@ -593,20 +655,26 @@ static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self, val |= state & HW_ATL_MPI_STATE_MSK; aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val); + err_exit: return err; } int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self) { - u32 cp0x036C = hw_atl_utils_mpi_get_state(self); - u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT; struct aq_hw_link_status_s *link_status = &self->aq_link_status; + u32 mpi_state; + u32 speed; - if (!link_speed_mask) { + mpi_state = hw_atl_utils_mpi_get_state(self); + speed = mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G | + FW2X_RATE_2G5 | FW2X_RATE_5G | + FW2X_RATE_10G); + + if (!speed) { link_status->mbps = 0U; } else { - switch (link_speed_mask) { + switch (speed) { case HAL_ATLANTIC_RATE_10G: link_status->mbps = 10000U; break; @@ -639,14 +707,15 @@ int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self) int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, u8 *mac) { + u32 mac_addr[2]; + u32 efuse_addr; int err = 0; u32 h = 0U; u32 l = 0U; - u32 mac_addr[2]; if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) { - unsigned int rnd = 0; unsigned int ucp_0x370 = 0; + unsigned int rnd = 0; get_random_bytes(&rnd, sizeof(unsigned int)); @@ -654,11 +723,10 @@ int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self, aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370); } - err = hw_atl_utils_fw_downld_dwords(self, - aq_hw_read_reg(self, 0x00000374U) + - (40U * 4U), - mac_addr, - ARRAY_SIZE(mac_addr)); + efuse_addr = aq_hw_read_reg(self, 0x00000374U); + + err = hw_atl_utils_fw_downld_dwords(self, efuse_addr + (40U * 4U), + mac_addr, ARRAY_SIZE(mac_addr)); if (err < 0) { mac_addr[0] = 0U; mac_addr[1] = 0U; @@ -720,14 +788,15 @@ unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps) default: break; } + return ret; } void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p) { - u32 chip_features = 0U; u32 val = hw_atl_reg_glb_mif_id_get(self); u32 mif_rev = val & 0xFFU; + u32 chip_features = 0U; if ((0xFU & mif_rev) == 1U) { chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 | @@ -754,13 +823,14 @@ static int hw_atl_fw1x_deinit(struct aq_hw_s *self) { hw_atl_utils_mpi_set_speed(self, 0); hw_atl_utils_mpi_set_state(self, MPI_DEINIT); + return 0; } int hw_atl_utils_update_stats(struct aq_hw_s *self) { - struct hw_atl_utils_mbox mbox; struct aq_stats_s *cs = &self->curr_stats; + struct hw_atl_utils_mbox mbox; hw_atl_utils_mpi_read_stats(self, &mbox); @@ -837,16 +907,19 @@ int hw_atl_utils_hw_get_regs(struct aq_hw_s *self, for (i = 0; i < aq_hw_caps->mac_regs_count; i++) regs_buff[i] = aq_hw_read_reg(self, hw_atl_utils_hw_mac_regs[i]); + return 0; } int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version) { *fw_version = aq_hw_read_reg(self, 0x18U); + return 0; } -static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac) +static int aq_fw1x_set_wake_magic(struct aq_hw_s *self, bool wol_enabled, + u8 *mac) { struct hw_atl_utils_fw_rpc *prpc = NULL; unsigned int rpc_size = 0U; @@ -859,22 +932,26 @@ static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac) memset(prpc, 0, sizeof(*prpc)); if (wol_enabled) { - rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_wol); + rpc_size = offsetof(struct hw_atl_utils_fw_rpc, msg_wol_add) + + sizeof(prpc->msg_wol_add); + prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD; - prpc->msg_wol.priority = + prpc->msg_wol_add.priority = HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR; - prpc->msg_wol.pattern_id = + prpc->msg_wol_add.pattern_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; - prpc->msg_wol.wol_packet_type = + prpc->msg_wol_add.packet_type = HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT; - ether_addr_copy((u8 *)&prpc->msg_wol.wol_pattern, mac); + ether_addr_copy((u8 *)&prpc->msg_wol_add.magic_packet_pattern, + mac); } else { - rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_del_id); + rpc_size = sizeof(prpc->msg_wol_remove) + + offsetof(struct hw_atl_utils_fw_rpc, msg_wol_remove); prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL; - prpc->msg_wol.pattern_id = + prpc->msg_wol_add.pattern_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN; } @@ -891,8 +968,8 @@ static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state, unsigned int rpc_size = 0U; int err = 0; - if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { - err = aq_fw1x_set_wol(self, 1, mac); + if (self->aq_nic_cfg->wol & WAKE_MAGIC) { + err = aq_fw1x_set_wake_magic(self, 1, mac); if (err < 0) goto err_exit; @@ -964,4 +1041,7 @@ const struct aq_fw_ops aq_fw_1x_ops = { .set_eee_rate = NULL, .get_eee_rate = NULL, .set_flow_control = NULL, + .send_fw_request = NULL, + .enable_ptp = NULL, + .led_control = NULL, }; diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h index 692bed70e104..42f0c5c6ec2d 100644 --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_utils.h: Declaration of common functions for Atlantic hardware @@ -41,7 +41,15 @@ struct __packed hw_atl_rxd_wb_s { u16 status; u16 pkt_len; u16 next_desc_ptr; - u16 vlan; + __le16 vlan; +}; + +/* Hardware rx HW TIMESTAMP writeback */ +struct __packed hw_atl_rxd_hwts_wb_s { + u32 sec_hw; + u32 ns; + u32 sec_lw0; + u32 sec_lw1; }; struct __packed hw_atl_stats_s { @@ -62,104 +70,41 @@ struct __packed hw_atl_stats_s { u32 dpc; }; -union __packed ip_addr { - struct { - u8 addr[16]; - } v6; - struct { - u8 padding[12]; - u8 addr[4]; - } v4; -}; - -struct __packed hw_atl_utils_fw_rpc { - u32 msg_id; - +struct __packed drv_msg_enable_wakeup { union { - struct { - u32 pong; - } msg_ping; + u32 pattern_mask; struct { - u8 mac_addr[6]; - u32 ip_addr_cnt; + u32 reason_arp_v4_pkt : 1; + u32 reason_ipv4_ping_pkt : 1; + u32 reason_ipv6_ns_pkt : 1; + u32 reason_ipv6_ping_pkt : 1; + u32 reason_link_up : 1; + u32 reason_link_down : 1; + u32 reason_maximum : 1; + }; + }; - struct { - union ip_addr addr; - union ip_addr mask; - } ip[1]; - } msg_arp; + union { + u32 offload_mask; + }; +}; - struct { - u32 len; - u8 packet[1514U]; - } msg_inject; +struct __packed magic_packet_pattern_s { + u8 mac_addr[ETH_ALEN]; +}; - struct { - u32 priority; - u32 wol_packet_type; - u32 pattern_id; - u32 next_wol_pattern_offset; - - union { - struct { - u32 flags; - u8 ipv4_source_address[4]; - u8 ipv4_dest_address[4]; - u16 tcp_source_port_number; - u16 tcp_dest_port_number; - } ipv4_tcp_syn_parameters; - - struct { - u32 flags; - u8 ipv6_source_address[16]; - u8 ipv6_dest_address[16]; - u16 tcp_source_port_number; - u16 tcp_dest_port_number; - } ipv6_tcp_syn_parameters; - - struct { - u32 flags; - } eapol_request_id_message_parameters; - - struct { - u32 flags; - u32 mask_offset; - u32 mask_size; - u32 pattern_offset; - u32 pattern_size; - } wol_bit_map_pattern; - - struct { - u8 mac_addr[ETH_ALEN]; - } wol_magic_packet_patter; - } wol_pattern; - } msg_wol; +struct __packed drv_msg_wol_add { + u32 priority; + u32 packet_type; + u32 pattern_id; + u32 next_pattern_offset; - struct { - union { - u32 pattern_mask; - - struct { - u32 reason_arp_v4_pkt : 1; - u32 reason_ipv4_ping_pkt : 1; - u32 reason_ipv6_ns_pkt : 1; - u32 reason_ipv6_ping_pkt : 1; - u32 reason_link_up : 1; - u32 reason_link_down : 1; - u32 reason_maximum : 1; - }; - }; - - union { - u32 offload_mask; - }; - } msg_enable_wakeup; + struct magic_packet_pattern_s magic_packet_pattern; +}; - struct { - u32 id; - } msg_del_id; - }; +struct __packed drv_msg_wol_remove { + u32 id; }; struct __packed hw_atl_utils_mbox_header { @@ -168,43 +113,89 @@ struct __packed hw_atl_utils_mbox_header { u32 error; }; -struct __packed hw_aq_info { +struct __packed hw_atl_ptp_offset { + u16 ingress_100; + u16 egress_100; + u16 ingress_1000; + u16 egress_1000; + u16 ingress_2500; + u16 egress_2500; + u16 ingress_5000; + u16 egress_5000; + u16 ingress_10000; + u16 egress_10000; +}; + +struct __packed hw_atl_cable_diag { + u8 fault; + u8 distance; + u8 far_distance; + u8 reserved; +}; + +enum gpio_pin_function { + GPIO_PIN_FUNCTION_NC, + GPIO_PIN_FUNCTION_VAUX_ENABLE, + GPIO_PIN_FUNCTION_EFUSE_BURN_ENABLE, + GPIO_PIN_FUNCTION_SFP_PLUS_DETECT, + GPIO_PIN_FUNCTION_TX_DISABLE, + GPIO_PIN_FUNCTION_RATE_SEL_0, + GPIO_PIN_FUNCTION_RATE_SEL_1, + GPIO_PIN_FUNCTION_TX_FAULT, + GPIO_PIN_FUNCTION_PTP0, + GPIO_PIN_FUNCTION_PTP1, + GPIO_PIN_FUNCTION_PTP2, + GPIO_PIN_FUNCTION_SIZE +}; + +struct __packed hw_atl_info { u8 reserved[6]; u16 phy_fault_code; u16 phy_temperature; u8 cable_len; u8 reserved1; - u32 cable_diag_data[4]; - u8 reserved2[32]; + struct hw_atl_cable_diag cable_diag_data[4]; + struct hw_atl_ptp_offset ptp_offset; + u8 reserved2[12]; u32 caps_lo; u32 caps_hi; + u32 reserved_datapath; + u32 reserved3[7]; + u32 reserved_simpleresp[3]; + u32 reserved_linkstat[7]; + u32 reserved_wakes_count; + u32 reserved_eee_stat[12]; + u32 tx_stuck_cnt; + u32 setting_address; + u32 setting_length; + u32 caps_ex; + enum gpio_pin_function gpio_pin[3]; + u32 pcie_aer_dump[18]; + u16 snr_margin[4]; }; struct __packed hw_atl_utils_mbox { struct hw_atl_utils_mbox_header header; struct hw_atl_stats_s stats; - struct hw_aq_info info; + struct hw_atl_info info; }; -/* fw2x */ -typedef u32 fw_offset_t; - struct __packed offload_ip_info { u8 v4_local_addr_count; u8 v4_addr_count; u8 v6_local_addr_count; u8 v6_addr_count; - fw_offset_t v4_addr; - fw_offset_t v4_prefix; - fw_offset_t v6_addr; - fw_offset_t v6_prefix; + u32 v4_addr; + u32 v4_prefix; + u32 v6_addr; + u32 v6_prefix; }; struct __packed offload_port_info { u16 udp_port_count; u16 tcp_port_count; - fw_offset_t udp_port; - fw_offset_t tcp_port; + u32 udp_port; + u32 tcp_port; }; struct __packed offload_ka_info { @@ -212,15 +203,15 @@ struct __packed offload_ka_info { u16 v6_ka_count; u32 retry_count; u32 retry_interval; - fw_offset_t v4_ka; - fw_offset_t v6_ka; + u32 v4_ka; + u32 v6_ka; }; struct __packed offload_rr_info { u32 rr_count; u32 rr_buf_len; - fw_offset_t rr_id_x; - fw_offset_t rr_buf; + u32 rr_id_x; + u32 rr_buf; }; struct __packed offload_info { @@ -237,9 +228,103 @@ struct __packed offload_info { u8 buf[0]; }; +struct __packed hw_atl_utils_fw_rpc { + u32 msg_id; + + union { + /* fw1x structures */ + struct drv_msg_wol_add msg_wol_add; + struct drv_msg_wol_remove msg_wol_remove; + struct drv_msg_enable_wakeup msg_enable_wakeup; + /* fw2x structures */ + struct offload_info fw2x_offloads; + }; +}; + +/* Mailbox FW Request interface */ +struct __packed hw_fw_request_ptp_gpio_ctrl { + u32 index; + u32 period; + u64 start; +}; + +struct __packed hw_fw_request_ptp_adj_freq { + u32 ns_mac; + u32 fns_mac; + u32 ns_phy; + u32 fns_phy; + u32 mac_ns_adj; + u32 mac_fns_adj; +}; + +struct __packed hw_fw_request_ptp_adj_clock { + u32 ns; + u32 sec; + int sign; +}; + +#define HW_AQ_FW_REQUEST_PTP_GPIO_CTRL 0x11 +#define HW_AQ_FW_REQUEST_PTP_ADJ_FREQ 0x12 +#define HW_AQ_FW_REQUEST_PTP_ADJ_CLOCK 0x13 + +struct __packed hw_fw_request_iface { + u32 msg_id; + union { + /* PTP FW Request */ + struct hw_fw_request_ptp_gpio_ctrl ptp_gpio_ctrl; + struct hw_fw_request_ptp_adj_freq ptp_adj_freq; + struct hw_fw_request_ptp_adj_clock ptp_adj_clock; + }; +}; + +struct __packed hw_atl_utils_settings { + u32 mtu; + u32 downshift_retry_count; + u32 link_pause_frame_quanta_100m; + u32 link_pause_frame_threshold_100m; + u32 link_pause_frame_quanta_1g; + u32 link_pause_frame_threshold_1g; + u32 link_pause_frame_quanta_2p5g; + u32 link_pause_frame_threshold_2p5g; + u32 link_pause_frame_quanta_5g; + u32 link_pause_frame_threshold_5g; + u32 link_pause_frame_quanta_10g; + u32 link_pause_frame_threshold_10g; + u32 pfc_quanta_class_0; + u32 pfc_threshold_class_0; + u32 pfc_quanta_class_1; + u32 pfc_threshold_class_1; + u32 pfc_quanta_class_2; + u32 pfc_threshold_class_2; + u32 pfc_quanta_class_3; + u32 pfc_threshold_class_3; + u32 pfc_quanta_class_4; + u32 pfc_threshold_class_4; + u32 pfc_quanta_class_5; + u32 pfc_threshold_class_5; + u32 pfc_quanta_class_6; + u32 pfc_threshold_class_6; + u32 pfc_quanta_class_7; + u32 pfc_threshold_class_7; + u32 eee_link_down_timeout; + u32 eee_link_up_timeout; + u32 eee_max_link_drops; + u32 eee_rates_mask; + u32 wake_timer; + u32 thermal_shutdown_off_temp; + u32 thermal_shutdown_warning_temp; + u32 thermal_shutdown_cold_temp; + u32 msm_options; + u32 dac_cable_serdes_modes; + u32 media_detect; +}; + enum hw_atl_rx_action_with_traffic { HW_ATL_RX_DISCARD, HW_ATL_RX_HOST, + HW_ATL_RX_MNGMNT, + HW_ATL_RX_HOST_AND_MNGMNT, + HW_ATL_RX_WOL }; struct aq_rx_filter_vlan { @@ -321,20 +406,12 @@ enum hal_atl_utils_fw_state_e { #define HAL_ATLANTIC_RATE_100M BIT(5) #define HAL_ATLANTIC_RATE_INVALID BIT(6) -#define HAL_ATLANTIC_UTILS_FW_MSG_PING 0x1U -#define HAL_ATLANTIC_UTILS_FW_MSG_ARP 0x2U -#define HAL_ATLANTIC_UTILS_FW_MSG_INJECT 0x3U #define HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD 0x4U #define HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR 0x10000000U #define HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN 0x1U #define HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT 0x2U #define HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL 0x5U #define HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP 0x6U -#define HAL_ATLANTIC_UTILS_FW_MSG_MSM_PFC 0x7U -#define HAL_ATLANTIC_UTILS_FW_MSG_PROVISIONING 0x8U -#define HAL_ATLANTIC_UTILS_FW_MSG_OFFLOAD_ADD 0x9U -#define HAL_ATLANTIC_UTILS_FW_MSG_OFFLOAD_DEL 0xAU -#define HAL_ATLANTIC_UTILS_FW_MSG_CABLE_DIAG 0xDU enum hw_atl_fw2x_rate { FW2X_RATE_100M = 0x20, @@ -344,91 +421,135 @@ enum hw_atl_fw2x_rate { FW2X_RATE_10G = 0x800, }; +/* 0x370 + * Link capabilities resolution register + */ enum hw_atl_fw2x_caps_lo { - CAPS_LO_10BASET_HD = 0x00, + CAPS_LO_10BASET_HD = 0, CAPS_LO_10BASET_FD, CAPS_LO_100BASETX_HD, CAPS_LO_100BASET4_HD, CAPS_LO_100BASET2_HD, - CAPS_LO_100BASETX_FD, + CAPS_LO_100BASETX_FD = 5, CAPS_LO_100BASET2_FD, CAPS_LO_1000BASET_HD, CAPS_LO_1000BASET_FD, CAPS_LO_2P5GBASET_FD, - CAPS_LO_5GBASET_FD, + CAPS_LO_5GBASET_FD = 10, CAPS_LO_10GBASET_FD, }; +/* 0x374 + * Status register + */ enum hw_atl_fw2x_caps_hi { - CAPS_HI_RESERVED1 = 0x00, + CAPS_HI_RESERVED1 = 0, CAPS_HI_10BASET_EEE, CAPS_HI_RESERVED2, CAPS_HI_PAUSE, CAPS_HI_ASYMMETRIC_PAUSE, - CAPS_HI_100BASETX_EEE, + CAPS_HI_100BASETX_EEE = 5, CAPS_HI_RESERVED3, CAPS_HI_RESERVED4, CAPS_HI_1000BASET_FD_EEE, CAPS_HI_2P5GBASET_FD_EEE, - CAPS_HI_5GBASET_FD_EEE, + CAPS_HI_5GBASET_FD_EEE = 10, CAPS_HI_10GBASET_FD_EEE, - CAPS_HI_RESERVED5, + CAPS_HI_FW_REQUEST, CAPS_HI_RESERVED6, CAPS_HI_RESERVED7, - CAPS_HI_RESERVED8, + CAPS_HI_RESERVED8 = 15, CAPS_HI_RESERVED9, CAPS_HI_CABLE_DIAG, CAPS_HI_TEMPERATURE, CAPS_HI_DOWNSHIFT, - CAPS_HI_PTP_AVB_EN, + CAPS_HI_PTP_AVB_EN_FW2X = 20, CAPS_HI_MEDIA_DETECT, CAPS_HI_LINK_DROP, CAPS_HI_SLEEP_PROXY, CAPS_HI_WOL, - CAPS_HI_MAC_STOP, + CAPS_HI_MAC_STOP = 25, CAPS_HI_EXT_LOOPBACK, CAPS_HI_INT_LOOPBACK, CAPS_HI_EFUSE_AGENT, CAPS_HI_WOL_TIMER, - CAPS_HI_STATISTICS, + CAPS_HI_STATISTICS = 30, CAPS_HI_TRANSACTION_ID, }; +/* 0x36C + * Control register + */ enum hw_atl_fw2x_ctrl { - CTRL_RESERVED1 = 0x00, + CTRL_RESERVED1 = 0, CTRL_RESERVED2, CTRL_RESERVED3, CTRL_PAUSE, CTRL_ASYMMETRIC_PAUSE, - CTRL_RESERVED4, + CTRL_RESERVED4 = 5, CTRL_RESERVED5, CTRL_RESERVED6, CTRL_1GBASET_FD_EEE, CTRL_2P5GBASET_FD_EEE, - CTRL_5GBASET_FD_EEE, + CTRL_5GBASET_FD_EEE = 10, CTRL_10GBASET_FD_EEE, CTRL_THERMAL_SHUTDOWN, CTRL_PHY_LOGS, CTRL_EEE_AUTO_DISABLE, - CTRL_PFC, + CTRL_PFC = 15, CTRL_WAKE_ON_LINK, CTRL_CABLE_DIAG, CTRL_TEMPERATURE, CTRL_DOWNSHIFT, - CTRL_PTP_AVB, + CTRL_PTP_AVB = 20, CTRL_RESERVED7, CTRL_LINK_DROP, CTRL_SLEEP_PROXY, CTRL_WOL, - CTRL_MAC_STOP, + CTRL_MAC_STOP = 25, CTRL_EXT_LOOPBACK, CTRL_INT_LOOPBACK, CTRL_RESERVED8, CTRL_WOL_TIMER, - CTRL_STATISTICS, + CTRL_STATISTICS = 30, CTRL_FORCE_RECONNECT, }; +enum hw_atl_caps_ex { + CAPS_EX_LED_CONTROL = 0, + CAPS_EX_LED0_MODE_LO, + CAPS_EX_LED0_MODE_HI, + CAPS_EX_LED1_MODE_LO, + CAPS_EX_LED1_MODE_HI, + CAPS_EX_LED2_MODE_LO = 5, + CAPS_EX_LED2_MODE_HI, + CAPS_EX_RESERVED07, + CAPS_EX_RESERVED08, + CAPS_EX_RESERVED09, + CAPS_EX_RESERVED10 = 10, + CAPS_EX_RESERVED11, + CAPS_EX_RESERVED12, + CAPS_EX_RESERVED13, + CAPS_EX_RESERVED14, + CAPS_EX_RESERVED15 = 15, + CAPS_EX_PHY_PTP_EN, + CAPS_EX_MAC_PTP_EN, + CAPS_EX_EXT_CLK_EN, + CAPS_EX_SCHED_DMA_EN, + CAPS_EX_PTP_GPIO_EN = 20, + CAPS_EX_UPDATE_SETTINGS, + CAPS_EX_PHY_CTRL_TS_PIN, + CAPS_EX_SNR_OPERATING_MARGIN, + CAPS_EX_RESERVED24, + CAPS_EX_RESERVED25 = 25, + CAPS_EX_RESERVED26, + CAPS_EX_RESERVED27, + CAPS_EX_RESERVED28, + CAPS_EX_RESERVED29, + CAPS_EX_RESERVED30 = 30, + CAPS_EX_RESERVED31 +}; + struct aq_hw_s; struct aq_fw_ops; struct aq_hw_caps_s; @@ -475,6 +596,11 @@ struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self); int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a, u32 *p, u32 cnt); +int hw_atl_write_fwcfg_dwords(struct aq_hw_s *self, u32 *p, u32 cnt); + +int hw_atl_write_fwsettings_dwords(struct aq_hw_s *self, u32 offset, u32 *p, + u32 cnt); + int hw_atl_utils_fw_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac); int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size); 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 7bc51f8d6f2f..97ebf849695f 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 @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * aQuantia Corporation Network Driver - * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved + * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved */ /* File hw_atl_utils_fw2x.c: Definition of firmware 2.x functions for @@ -17,26 +17,34 @@ #include "hw_atl_utils.h" #include "hw_atl_llh.h" -#define HW_ATL_FW2X_MPI_RPC_ADDR 0x334 +#define HW_ATL_FW2X_MPI_LED_ADDR 0x31c +#define HW_ATL_FW2X_MPI_RPC_ADDR 0x334 -#define HW_ATL_FW2X_MPI_MBOX_ADDR 0x360 -#define HW_ATL_FW2X_MPI_EFUSE_ADDR 0x364 -#define HW_ATL_FW2X_MPI_CONTROL_ADDR 0x368 -#define HW_ATL_FW2X_MPI_CONTROL2_ADDR 0x36C -#define HW_ATL_FW2X_MPI_STATE_ADDR 0x370 -#define HW_ATL_FW2X_MPI_STATE2_ADDR 0x374 +#define HW_ATL_FW2X_MPI_MBOX_ADDR 0x360 +#define HW_ATL_FW2X_MPI_EFUSE_ADDR 0x364 +#define HW_ATL_FW2X_MPI_CONTROL_ADDR 0x368 +#define HW_ATL_FW2X_MPI_CONTROL2_ADDR 0x36C +#define HW_ATL_FW2X_MPI_STATE_ADDR 0x370 +#define HW_ATL_FW2X_MPI_STATE2_ADDR 0x374 + +#define HW_ATL_FW3X_EXT_CONTROL_ADDR 0x378 +#define HW_ATL_FW3X_EXT_STATE_ADDR 0x37c #define HW_ATL_FW2X_CAP_PAUSE BIT(CAPS_HI_PAUSE) #define HW_ATL_FW2X_CAP_ASYM_PAUSE BIT(CAPS_HI_ASYMMETRIC_PAUSE) #define HW_ATL_FW2X_CAP_SLEEP_PROXY BIT(CAPS_HI_SLEEP_PROXY) #define HW_ATL_FW2X_CAP_WOL BIT(CAPS_HI_WOL) +#define HW_ATL_FW2X_CTRL_WAKE_ON_LINK BIT(CTRL_WAKE_ON_LINK) #define HW_ATL_FW2X_CTRL_SLEEP_PROXY BIT(CTRL_SLEEP_PROXY) #define HW_ATL_FW2X_CTRL_WOL BIT(CTRL_WOL) #define HW_ATL_FW2X_CTRL_LINK_DROP BIT(CTRL_LINK_DROP) #define HW_ATL_FW2X_CTRL_PAUSE BIT(CTRL_PAUSE) #define HW_ATL_FW2X_CTRL_TEMPERATURE BIT(CTRL_TEMPERATURE) #define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE) +#define HW_ATL_FW2X_CTRL_INT_LOOPBACK BIT(CTRL_INT_LOOPBACK) +#define HW_ATL_FW2X_CTRL_EXT_LOOPBACK BIT(CTRL_EXT_LOOPBACK) +#define HW_ATL_FW2X_CTRL_DOWNSHIFT BIT(CTRL_DOWNSHIFT) #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) @@ -47,6 +55,9 @@ #define HAL_ATLANTIC_WOL_FILTERS_COUNT 8 #define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL 0x0E +#define HW_ATL_FW_VER_LED 0x03010026U +#define HW_ATL_FW_VER_MEDIA_CONTROL 0x0301005aU + struct __packed fw2x_msg_wol_pattern { u8 mask[16]; u32 crc; @@ -71,6 +82,7 @@ static int aq_fw2x_set_state(struct aq_hw_s *self, static u32 aq_fw2x_mbox_get(struct aq_hw_s *self); static u32 aq_fw2x_rpc_get(struct aq_hw_s *self); +static int aq_fw2x_settings_get(struct aq_hw_s *self, u32 *addr); static u32 aq_fw2x_state2_get(struct aq_hw_s *self); static int aq_fw2x_init(struct aq_hw_s *self) @@ -88,6 +100,8 @@ static int aq_fw2x_init(struct aq_hw_s *self) self->rpc_addr != 0U, 1000U, 100000U); + err = aq_fw2x_settings_get(self, &self->settings_addr); + return err; } @@ -167,17 +181,26 @@ 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) +static void aq_fw2x_upd_flow_control_bits(struct aq_hw_s *self, + u32 *mpi_state, u32 fc) { - if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_RX) - *mpi_state |= BIT(CAPS_HI_PAUSE); - else - *mpi_state &= ~BIT(CAPS_HI_PAUSE); + *mpi_state &= ~(HW_ATL_FW2X_CTRL_PAUSE | + HW_ATL_FW2X_CTRL_ASYMMETRIC_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); + switch (fc) { + /* There is not explicit mode of RX only pause frames, + * thus, we join this mode with FC full. + * FC full is either Rx, either Tx, or both. + */ + case AQ_NIC_FC_FULL: + case AQ_NIC_FC_RX: + *mpi_state |= HW_ATL_FW2X_CTRL_PAUSE | + HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE; + break; + case AQ_NIC_FC_TX: + *mpi_state |= HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE; + break; + } } static void aq_fw2x_upd_eee_rate_bits(struct aq_hw_s *self, u32 *mpi_opts, @@ -201,7 +224,8 @@ static int aq_fw2x_set_state(struct aq_hw_s *self, 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); + aq_fw2x_upd_flow_control_bits(self, &mpi_state, + self->aq_nic_cfg->fc.req); break; case MPI_DEINIT: mpi_state |= BIT(CAPS_HI_LINK_DROP); @@ -212,15 +236,20 @@ static int aq_fw2x_set_state(struct aq_hw_s *self, break; } aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state); + return 0; } static int aq_fw2x_update_link_status(struct aq_hw_s *self) { - u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR); - u32 speed = mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G | - FW2X_RATE_2G5 | FW2X_RATE_5G | FW2X_RATE_10G); struct aq_hw_link_status_s *link_status = &self->aq_link_status; + u32 mpi_state; + u32 speed; + + mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR); + speed = mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G | + FW2X_RATE_2G5 | FW2X_RATE_5G | + FW2X_RATE_10G); if (speed) { if (speed & FW2X_RATE_10G) @@ -244,11 +273,11 @@ static int aq_fw2x_update_link_status(struct aq_hw_s *self) static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac) { + u32 efuse_addr = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_EFUSE_ADDR); + u32 mac_addr[2] = { 0 }; int err = 0; u32 h = 0U; u32 l = 0U; - u32 mac_addr[2] = { 0 }; - u32 efuse_addr = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_EFUSE_ADDR); if (efuse_addr != 0) { err = hw_atl_utils_fw_downld_dwords(self, @@ -282,15 +311,16 @@ static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac) h >>= 8; mac[0] = (u8)(0xFFU & h); } + return err; } static int aq_fw2x_update_stats(struct aq_hw_s *self) { - int err = 0; u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); u32 orig_stats_val = mpi_opts & BIT(CAPS_HI_STATISTICS); u32 stats_val; + int err = 0; /* Toggle statistics bit for FW to update */ mpi_opts = mpi_opts ^ BIT(CAPS_HI_STATISTICS); @@ -317,9 +347,9 @@ static int aq_fw2x_get_phy_temp(struct aq_hw_s *self, int *temp) int err = 0; u32 val; - phy_temp_offset = self->mbox_addr + - offsetof(struct hw_atl_utils_mbox, info) + - offsetof(struct hw_aq_info, phy_temperature); + phy_temp_offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, + info.phy_temperature); + /* Toggle statistics bit for FW to 0x36C.18 (CTRL_TEMPERATURE) */ mpi_opts = mpi_opts ^ HW_ATL_FW2X_CTRL_TEMPERATURE; aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); @@ -342,106 +372,117 @@ static int aq_fw2x_get_phy_temp(struct aq_hw_s *self, int *temp) return 0; } -static int aq_fw2x_set_sleep_proxy(struct aq_hw_s *self, u8 *mac) +static int aq_fw2x_set_wol(struct aq_hw_s *self, u8 *mac) { struct hw_atl_utils_fw_rpc *rpc = NULL; - struct offload_info *cfg = NULL; - unsigned int rpc_size = 0U; - u32 mpi_opts; + struct offload_info *info = NULL; + u32 wol_bits = 0; + u32 rpc_size; int err = 0; u32 val; - rpc_size = sizeof(rpc->msg_id) + sizeof(*cfg); - - err = hw_atl_utils_fw_rpc_wait(self, &rpc); - if (err < 0) - goto err_exit; - - memset(rpc, 0, rpc_size); - cfg = (struct offload_info *)(&rpc->msg_id + 1); + if (self->aq_nic_cfg->wol & WAKE_PHY) { + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, + HW_ATL_FW2X_CTRL_LINK_DROP); + readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val, + (val & + HW_ATL_FW2X_CTRL_LINK_DROP) != 0, + 1000, 100000); + wol_bits |= HW_ATL_FW2X_CTRL_WAKE_ON_LINK; + } - memcpy(cfg->mac_addr, mac, ETH_ALEN); - cfg->len = sizeof(*cfg); + if (self->aq_nic_cfg->wol & WAKE_MAGIC) { + wol_bits |= HW_ATL_FW2X_CTRL_SLEEP_PROXY | + HW_ATL_FW2X_CTRL_WOL; - /* Clear bit 0x36C.23 and 0x36C.22 */ - mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); - mpi_opts &= ~HW_ATL_FW2X_CTRL_SLEEP_PROXY; - mpi_opts &= ~HW_ATL_FW2X_CTRL_LINK_DROP; + err = hw_atl_utils_fw_rpc_wait(self, &rpc); + if (err < 0) + goto err_exit; - aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + rpc_size = sizeof(*info) + + offsetof(struct hw_atl_utils_fw_rpc, fw2x_offloads); + memset(rpc, 0, rpc_size); + info = &rpc->fw2x_offloads; + memcpy(info->mac_addr, mac, ETH_ALEN); + info->len = sizeof(*info); - err = hw_atl_utils_fw_rpc_call(self, rpc_size); - if (err < 0) - goto err_exit; + err = hw_atl_utils_fw_rpc_call(self, rpc_size); + if (err < 0) + goto err_exit; + } - /* Set bit 0x36C.23 */ - mpi_opts |= HW_ATL_FW2X_CTRL_SLEEP_PROXY; - aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); - - err = readx_poll_timeout_atomic(aq_fw2x_state2_get, - self, val, - val & HW_ATL_FW2X_CTRL_SLEEP_PROXY, - 1U, 100000U); + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, wol_bits); err_exit: return err; } -static int aq_fw2x_set_wol_params(struct aq_hw_s *self, u8 *mac) +static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state, + u8 *mac) { - struct hw_atl_utils_fw_rpc *rpc = NULL; - struct fw2x_msg_wol *msg = NULL; - u32 mpi_opts; int err = 0; - u32 val; - - err = hw_atl_utils_fw_rpc_wait(self, &rpc); - if (err < 0) - goto err_exit; - - msg = (struct fw2x_msg_wol *)rpc; - - memset(msg, 0, sizeof(*msg)); - msg->msg_id = HAL_ATLANTIC_UTILS_FW2X_MSG_WOL; - msg->magic_packet_enabled = true; - memcpy(msg->hw_addr, mac, ETH_ALEN); + if (self->aq_nic_cfg->wol) + err = aq_fw2x_set_wol(self, mac); - mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); - mpi_opts &= ~(HW_ATL_FW2X_CTRL_SLEEP_PROXY | HW_ATL_FW2X_CTRL_WOL); + return err; +} - aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); +static int aq_fw2x_send_fw_request(struct aq_hw_s *self, + const struct hw_fw_request_iface *fw_req, + size_t size) +{ + u32 ctrl2, orig_ctrl2; + u32 dword_cnt; + int err = 0; + u32 val; - err = hw_atl_utils_fw_rpc_call(self, sizeof(*msg)); + /* Write data to drvIface Mailbox */ + dword_cnt = size / sizeof(u32); + if (size % sizeof(u32)) + dword_cnt++; + err = hw_atl_write_fwcfg_dwords(self, (void *)fw_req, dword_cnt); if (err < 0) goto err_exit; - /* Set bit 0x36C.24 */ - mpi_opts |= HW_ATL_FW2X_CTRL_WOL; - aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + /* Toggle statistics bit for FW to update */ + ctrl2 = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + orig_ctrl2 = ctrl2 & BIT(CAPS_HI_FW_REQUEST); + ctrl2 = ctrl2 ^ BIT(CAPS_HI_FW_REQUEST); + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, ctrl2); - err = readx_poll_timeout_atomic(aq_fw2x_state2_get, - self, val, val & HW_ATL_FW2X_CTRL_WOL, + /* Wait FW to report back */ + err = readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val, + orig_ctrl2 != (val & + BIT(CAPS_HI_FW_REQUEST)), 1U, 10000U); err_exit: return err; } -static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state, - u8 *mac) +static void aq_fw3x_enable_ptp(struct aq_hw_s *self, int enable) { - int err = 0; + u32 ptp_opts = aq_hw_read_reg(self, HW_ATL_FW3X_EXT_STATE_ADDR); + u32 all_ptp_features = BIT(CAPS_EX_PHY_PTP_EN) | + BIT(CAPS_EX_PTP_GPIO_EN); - if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { - err = aq_fw2x_set_sleep_proxy(self, mac); - if (err < 0) - goto err_exit; - err = aq_fw2x_set_wol_params(self, mac); - } + if (enable) + ptp_opts |= all_ptp_features; + else + ptp_opts &= ~all_ptp_features; -err_exit: - return err; + aq_hw_write_reg(self, HW_ATL_FW3X_EXT_CONTROL_ADDR, ptp_opts); +} + +static int aq_fw2x_led_control(struct aq_hw_s *self, u32 mode) +{ + if (self->fw_ver_actual < HW_ATL_FW_VER_LED) + return -EOPNOTSUPP; + + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_LED_ADDR, mode); + + return 0; } static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed) @@ -461,11 +502,12 @@ static int aq_fw2x_get_eee_rate(struct aq_hw_s *self, u32 *rate, u32 mpi_state; u32 caps_hi; int err = 0; - u32 addr = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, info) + - offsetof(struct hw_aq_info, caps_hi); + u32 offset; + + offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, + info.caps_hi); - err = hw_atl_utils_fw_downld_dwords(self, addr, &caps_hi, - sizeof(caps_hi) / sizeof(u32)); + err = hw_atl_utils_fw_downld_dwords(self, offset, &caps_hi, 1); if (err) return err; @@ -493,7 +535,8 @@ 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_fw2x_upd_flow_control_bits(self, &mpi_state, + self->aq_nic_cfg->fc.req); aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state); @@ -503,17 +546,41 @@ static int aq_fw2x_set_flow_control(struct aq_hw_s *self) static u32 aq_fw2x_get_flow_control(struct aq_hw_s *self, u32 *fcmode) { u32 mpi_state = aq_fw2x_state2_get(self); + *fcmode = 0; if (mpi_state & HW_ATL_FW2X_CAP_PAUSE) - if (mpi_state & HW_ATL_FW2X_CAP_ASYM_PAUSE) - *fcmode = AQ_NIC_FC_RX; + *fcmode |= AQ_NIC_FC_RX; + + if (mpi_state & HW_ATL_FW2X_CAP_ASYM_PAUSE) + *fcmode |= AQ_NIC_FC_TX; + + return 0; +} + +static int aq_fw2x_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable) +{ + u32 mpi_opts; + + switch (mode) { + case AQ_HW_LOOPBACK_PHYINT_SYS: + mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + if (enable) + mpi_opts |= HW_ATL_FW2X_CTRL_INT_LOOPBACK; else - *fcmode = AQ_NIC_FC_RX | AQ_NIC_FC_TX; - else - if (mpi_state & HW_ATL_FW2X_CAP_ASYM_PAUSE) - *fcmode = AQ_NIC_FC_TX; + mpi_opts &= ~HW_ATL_FW2X_CTRL_INT_LOOPBACK; + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + break; + case AQ_HW_LOOPBACK_PHYEXT_SYS: + mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); + if (enable) + mpi_opts |= HW_ATL_FW2X_CTRL_EXT_LOOPBACK; else - *fcmode = 0; + mpi_opts &= ~HW_ATL_FW2X_CTRL_EXT_LOOPBACK; + aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); + break; + default: + return -EINVAL; + } return 0; } @@ -528,25 +595,42 @@ static u32 aq_fw2x_rpc_get(struct aq_hw_s *self) return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_RPC_ADDR); } +static int aq_fw2x_settings_get(struct aq_hw_s *self, u32 *addr) +{ + int err = 0; + u32 offset; + + offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, + info.setting_address); + + err = hw_atl_utils_fw_downld_dwords(self, offset, addr, 1); + + return err; +} + static u32 aq_fw2x_state2_get(struct aq_hw_s *self) { return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR); } const struct aq_fw_ops aq_fw_2x_ops = { - .init = aq_fw2x_init, - .deinit = aq_fw2x_deinit, - .reset = NULL, - .renegotiate = aq_fw2x_renegotiate, - .get_mac_permanent = aq_fw2x_get_mac_permanent, - .set_link_speed = aq_fw2x_set_link_speed, - .set_state = aq_fw2x_set_state, + .init = aq_fw2x_init, + .deinit = aq_fw2x_deinit, + .reset = NULL, + .renegotiate = aq_fw2x_renegotiate, + .get_mac_permanent = aq_fw2x_get_mac_permanent, + .set_link_speed = aq_fw2x_set_link_speed, + .set_state = aq_fw2x_set_state, .update_link_status = aq_fw2x_update_link_status, - .update_stats = aq_fw2x_update_stats, - .get_phy_temp = aq_fw2x_get_phy_temp, - .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, - .get_flow_control = aq_fw2x_get_flow_control + .update_stats = aq_fw2x_update_stats, + .get_phy_temp = aq_fw2x_get_phy_temp, + .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, + .get_flow_control = aq_fw2x_get_flow_control, + .send_fw_request = aq_fw2x_send_fw_request, + .enable_ptp = aq_fw3x_enable_ptp, + .led_control = aq_fw2x_led_control, + .set_phyloopback = aq_fw2x_set_phyloopback, }; |