diff options
Diffstat (limited to 'drivers/net/ethernet')
226 files changed, 28791 insertions, 3720 deletions
diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index edf72258ab1d..29c3075bfb05 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -64,7 +64,7 @@ config ARM_ETHERH should say Y to this option if you wish to use it with Linux. config MAC8390 - bool "Macintosh NS 8390 based ethernet cards" + tristate "Macintosh NS 8390 based ethernet cards" depends on MAC select CRC32 ---help--- diff --git a/drivers/net/ethernet/8390/mac8390.c b/drivers/net/ethernet/8390/mac8390.c index 65cf60f6718c..b9283901136e 100644 --- a/drivers/net/ethernet/8390/mac8390.c +++ b/drivers/net/ethernet/8390/mac8390.c @@ -454,34 +454,22 @@ MODULE_AUTHOR("David Huggins-Daines <dhd@debian.org> and others"); MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver"); MODULE_LICENSE("GPL"); -/* overkill, of course */ -static struct net_device *dev_mac8390[15]; -int init_module(void) +static struct net_device *dev_mac8390; + +int __init init_module(void) { - int i; - for (i = 0; i < 15; i++) { - struct net_device *dev = mac8390_probe(-1); - if (IS_ERR(dev)) - break; - dev_mac890[i] = dev; - } - if (!i) { - pr_notice("No useable cards found, driver NOT installed.\n"); - return -ENODEV; + dev_mac8390 = mac8390_probe(-1); + if (IS_ERR(dev_mac8390)) { + pr_warn("mac8390: No card found\n"); + return PTR_ERR(dev_mac8390); } return 0; } -void cleanup_module(void) +void __exit cleanup_module(void) { - int i; - for (i = 0; i < 15; i++) { - struct net_device *dev = dev_mac890[i]; - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } + unregister_netdev(dev_mac8390); + free_netdev(dev_mac8390); } #endif /* MODULE */ diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index ae89de7deb13..20bf55dbd76f 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1141,8 +1141,6 @@ static void greth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *in strlcpy(info->version, "revision: 1.0", sizeof(info->version)); strlcpy(info->bus_info, greth->dev->bus->name, sizeof(info->bus_info)); strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); - info->eedump_len = 0; - info->regdump_len = sizeof(struct greth_regs); } static void greth_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) diff --git a/drivers/net/ethernet/amd/7990.c b/drivers/net/ethernet/amd/7990.c index 98a10d555b79..66d0b73c39c0 100644 --- a/drivers/net/ethernet/amd/7990.c +++ b/drivers/net/ethernet/amd/7990.c @@ -661,6 +661,7 @@ void lance_poll(struct net_device *dev) spin_unlock(&lp->devlock); lance_interrupt(dev->irq, dev); } +EXPORT_SYMBOL_GPL(lance_poll); #endif MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index afc62ea804fc..0038709fd317 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -100,7 +100,7 @@ config DECLANCE DEPCA series. (This chipset is better known via the NE2100 cards.) config HPLANCE - bool "HP on-board LANCE support" + tristate "HP on-board LANCE support" depends on DIO select CRC32 ---help--- diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index cb367cc59e0b..5330bcb8a944 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -714,7 +714,6 @@ au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) strlcpy(info->version, DRV_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME, aup->mac_id); - info->regdump_len = 0; } static void au1000_set_msglevel(struct net_device *dev, u32 value) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index a4473d8ff4fa..45512242baea 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -1940,84 +1940,31 @@ static void xgbe_config_mtl_mode(struct xgbe_prv_data *pdata) static unsigned int xgbe_calculate_per_queue_fifo(unsigned int fifo_size, unsigned int queue_count) { - unsigned int q_fifo_size = 0; - enum xgbe_mtl_fifo_size p_fifo = XGMAC_MTL_FIFO_SIZE_256; + unsigned int q_fifo_size; + unsigned int p_fifo; - /* Calculate Tx/Rx fifo share per queue */ - switch (fifo_size) { - case 0: - q_fifo_size = XGBE_FIFO_SIZE_B(128); - break; - case 1: - q_fifo_size = XGBE_FIFO_SIZE_B(256); - break; - case 2: - q_fifo_size = XGBE_FIFO_SIZE_B(512); - break; - case 3: - q_fifo_size = XGBE_FIFO_SIZE_KB(1); - break; - case 4: - q_fifo_size = XGBE_FIFO_SIZE_KB(2); - break; - case 5: - q_fifo_size = XGBE_FIFO_SIZE_KB(4); - break; - case 6: - q_fifo_size = XGBE_FIFO_SIZE_KB(8); - break; - case 7: - q_fifo_size = XGBE_FIFO_SIZE_KB(16); - break; - case 8: - q_fifo_size = XGBE_FIFO_SIZE_KB(32); - break; - case 9: - q_fifo_size = XGBE_FIFO_SIZE_KB(64); - break; - case 10: - q_fifo_size = XGBE_FIFO_SIZE_KB(128); - break; - case 11: - q_fifo_size = XGBE_FIFO_SIZE_KB(256); - break; - } + /* Calculate the configured fifo size */ + q_fifo_size = 1 << (fifo_size + 7); - /* The configured value is not the actual amount of fifo RAM */ + /* The configured value may not be the actual amount of fifo RAM */ q_fifo_size = min_t(unsigned int, XGBE_FIFO_MAX, q_fifo_size); q_fifo_size = q_fifo_size / queue_count; - /* Set the queue fifo size programmable value */ - if (q_fifo_size >= XGBE_FIFO_SIZE_KB(256)) - p_fifo = XGMAC_MTL_FIFO_SIZE_256K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(128)) - p_fifo = XGMAC_MTL_FIFO_SIZE_128K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(64)) - p_fifo = XGMAC_MTL_FIFO_SIZE_64K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(32)) - p_fifo = XGMAC_MTL_FIFO_SIZE_32K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(16)) - p_fifo = XGMAC_MTL_FIFO_SIZE_16K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(8)) - p_fifo = XGMAC_MTL_FIFO_SIZE_8K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(4)) - p_fifo = XGMAC_MTL_FIFO_SIZE_4K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(2)) - p_fifo = XGMAC_MTL_FIFO_SIZE_2K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_KB(1)) - p_fifo = XGMAC_MTL_FIFO_SIZE_1K; - else if (q_fifo_size >= XGBE_FIFO_SIZE_B(512)) - p_fifo = XGMAC_MTL_FIFO_SIZE_512; - else if (q_fifo_size >= XGBE_FIFO_SIZE_B(256)) - p_fifo = XGMAC_MTL_FIFO_SIZE_256; + /* Each increment in the queue fifo size represents 256 bytes of + * fifo, with 0 representing 256 bytes. Distribute the fifo equally + * between the queues. + */ + p_fifo = q_fifo_size / 256; + if (p_fifo) + p_fifo--; return p_fifo; } static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) { - enum xgbe_mtl_fifo_size fifo_size; + unsigned int fifo_size; unsigned int i; fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size, @@ -2033,7 +1980,7 @@ static void xgbe_config_tx_fifo_size(struct xgbe_prv_data *pdata) static void xgbe_config_rx_fifo_size(struct xgbe_prv_data *pdata) { - enum xgbe_mtl_fifo_size fifo_size; + unsigned int fifo_size; unsigned int i; fifo_size = xgbe_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size, @@ -2224,7 +2171,7 @@ static u64 xgbe_mmc_read(struct xgbe_prv_data *pdata, unsigned int reg_lo) default: read_hi = false; - }; + } val = XGMAC_IOREAD(pdata, reg_lo); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index aae9d5ecd182..49f796aaad4f 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -360,6 +360,9 @@ static irqreturn_t xgbe_isr(int irq, void *data) } } + if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU)) + pdata->ext_stats.rx_buffer_unavailable++; + /* Restart the device on a Fatal Bus Error */ if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE)) schedule_work(&pdata->restart_work); @@ -384,7 +387,8 @@ static irqreturn_t xgbe_isr(int irq, void *data) /* Read Tx Timestamp to clear interrupt */ pdata->tx_tstamp = hw_if->get_tx_tstamp(pdata); - schedule_work(&pdata->tx_tstamp_work); + queue_work(pdata->dev_workqueue, + &pdata->tx_tstamp_work); } } } @@ -450,7 +454,7 @@ static void xgbe_service_timer(unsigned long data) { struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; - schedule_work(&pdata->service_work); + queue_work(pdata->dev_workqueue, &pdata->service_work); mod_timer(&pdata->service_timer, jiffies + HZ); } @@ -891,7 +895,7 @@ static int xgbe_start(struct xgbe_prv_data *pdata) netif_tx_start_all_queues(netdev); xgbe_start_timers(pdata); - schedule_work(&pdata->service_work); + queue_work(pdata->dev_workqueue, &pdata->service_work); DBGPR("<--xgbe_start\n"); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 59e090e95c0e..6040293db9c1 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -179,6 +179,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror), XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes), XGMAC_EXT_STAT("rx_split_header_packets", rx_split_header_packets), + XGMAC_EXT_STAT("rx_buffer_unavailable", rx_buffer_unavailable), }; #define XGBE_STATS_COUNT ARRAY_SIZE(xgbe_gstring_stats) @@ -187,8 +188,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { int i; - DBGPR("-->%s\n", __func__); - switch (stringset) { case ETH_SS_STATS: for (i = 0; i < XGBE_STATS_COUNT; i++) { @@ -198,8 +197,6 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } break; } - - DBGPR("<--%s\n", __func__); } static void xgbe_get_ethtool_stats(struct net_device *netdev, @@ -209,23 +206,17 @@ static void xgbe_get_ethtool_stats(struct net_device *netdev, u8 *stat; int i; - DBGPR("-->%s\n", __func__); - pdata->hw_if.read_mmc_stats(pdata); for (i = 0; i < XGBE_STATS_COUNT; i++) { stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset; *data++ = *(u64 *)stat; } - - DBGPR("<--%s\n", __func__); } static int xgbe_get_sset_count(struct net_device *netdev, int stringset) { int ret; - DBGPR("-->%s\n", __func__); - switch (stringset) { case ETH_SS_STATS: ret = XGBE_STATS_COUNT; @@ -235,8 +226,6 @@ static int xgbe_get_sset_count(struct net_device *netdev, int stringset) ret = -EOPNOTSUPP; } - DBGPR("<--%s\n", __func__); - return ret; } @@ -245,13 +234,9 @@ static void xgbe_get_pauseparam(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_pauseparam\n"); - pause->autoneg = pdata->phy.pause_autoneg; pause->tx_pause = pdata->phy.tx_pause; pause->rx_pause = pdata->phy.rx_pause; - - DBGPR("<--xgbe_get_pauseparam\n"); } static int xgbe_set_pauseparam(struct net_device *netdev, @@ -260,13 +245,11 @@ static int xgbe_set_pauseparam(struct net_device *netdev, struct xgbe_prv_data *pdata = netdev_priv(netdev); int ret = 0; - DBGPR("-->xgbe_set_pauseparam\n"); - - DBGPR(" autoneg = %d, tx_pause = %d, rx_pause = %d\n", - pause->autoneg, pause->tx_pause, pause->rx_pause); - - if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) + if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) { + netdev_err(netdev, + "autoneg disabled, pause autoneg not avialable\n"); return -EINVAL; + } pdata->phy.pause_autoneg = pause->autoneg; pdata->phy.tx_pause = pause->tx_pause; @@ -286,8 +269,6 @@ static int xgbe_set_pauseparam(struct net_device *netdev, if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); - DBGPR("<--xgbe_set_pauseparam\n"); - return ret; } @@ -296,8 +277,6 @@ static int xgbe_get_settings(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_settings\n"); - cmd->phy_address = pdata->phy.address; cmd->supported = pdata->phy.supported; @@ -311,8 +290,6 @@ static int xgbe_get_settings(struct net_device *netdev, cmd->port = PORT_NONE; cmd->transceiver = XCVR_INTERNAL; - DBGPR("<--xgbe_get_settings\n"); - return 0; } @@ -323,16 +300,20 @@ static int xgbe_set_settings(struct net_device *netdev, u32 speed; int ret; - DBGPR("-->xgbe_set_settings\n"); - speed = ethtool_cmd_speed(cmd); - if (cmd->phy_address != pdata->phy.address) + if (cmd->phy_address != pdata->phy.address) { + netdev_err(netdev, "invalid phy address %hhu\n", + cmd->phy_address); return -EINVAL; + } if ((cmd->autoneg != AUTONEG_ENABLE) && - (cmd->autoneg != AUTONEG_DISABLE)) + (cmd->autoneg != AUTONEG_DISABLE)) { + netdev_err(netdev, "unsupported autoneg %hhu\n", + cmd->autoneg); return -EINVAL; + } if (cmd->autoneg == AUTONEG_DISABLE) { switch (speed) { @@ -341,16 +322,27 @@ static int xgbe_set_settings(struct net_device *netdev, case SPEED_1000: break; default: + netdev_err(netdev, "unsupported speed %u\n", speed); return -EINVAL; } - if (cmd->duplex != DUPLEX_FULL) + if (cmd->duplex != DUPLEX_FULL) { + netdev_err(netdev, "unsupported duplex %hhu\n", + cmd->duplex); return -EINVAL; + } } + netif_dbg(pdata, link, netdev, + "requested advertisement %#x, phy supported %#x\n", + cmd->advertising, pdata->phy.supported); + cmd->advertising &= pdata->phy.supported; - if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) + if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising) { + netdev_err(netdev, + "unsupported requested advertisement\n"); return -EINVAL; + } ret = 0; pdata->phy.autoneg = cmd->autoneg; @@ -366,8 +358,6 @@ static int xgbe_set_settings(struct net_device *netdev, if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); - DBGPR("<--xgbe_set_settings\n"); - return ret; } @@ -385,7 +375,20 @@ static void xgbe_get_drvinfo(struct net_device *netdev, XGMAC_GET_BITS(hw_feat->version, MAC_VR, USERVER), XGMAC_GET_BITS(hw_feat->version, MAC_VR, DEVID), XGMAC_GET_BITS(hw_feat->version, MAC_VR, SNPSVER)); - drvinfo->n_stats = XGBE_STATS_COUNT; +} + +static u32 xgbe_get_msglevel(struct net_device *netdev) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + return pdata->msg_enable; +} + +static void xgbe_set_msglevel(struct net_device *netdev, u32 msglevel) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + + pdata->msg_enable = msglevel; } static int xgbe_get_coalesce(struct net_device *netdev, @@ -393,8 +396,6 @@ static int xgbe_get_coalesce(struct net_device *netdev, { struct xgbe_prv_data *pdata = netdev_priv(netdev); - DBGPR("-->xgbe_get_coalesce\n"); - memset(ec, 0, sizeof(struct ethtool_coalesce)); ec->rx_coalesce_usecs = pdata->rx_usecs; @@ -402,8 +403,6 @@ static int xgbe_get_coalesce(struct net_device *netdev, ec->tx_max_coalesced_frames = pdata->tx_frames; - DBGPR("<--xgbe_get_coalesce\n"); - return 0; } @@ -415,8 +414,6 @@ static int xgbe_set_coalesce(struct net_device *netdev, unsigned int rx_frames, rx_riwt, rx_usecs; unsigned int tx_frames; - DBGPR("-->xgbe_set_coalesce\n"); - /* Check for not supported parameters */ if ((ec->rx_coalesce_usecs_irq) || (ec->rx_max_coalesced_frames_irq) || @@ -436,8 +433,10 @@ static int xgbe_set_coalesce(struct net_device *netdev, (ec->rx_max_coalesced_frames_high) || (ec->tx_coalesce_usecs_high) || (ec->tx_max_coalesced_frames_high) || - (ec->rate_sample_interval)) + (ec->rate_sample_interval)) { + netdev_err(netdev, "unsupported coalescing parameter\n"); return -EOPNOTSUPP; + } rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs); rx_usecs = ec->rx_coalesce_usecs; @@ -449,13 +448,13 @@ static int xgbe_set_coalesce(struct net_device *netdev, /* Check the bounds of values for Rx */ if (rx_riwt > XGMAC_MAX_DMA_RIWT) { - netdev_alert(netdev, "rx-usec is limited to %d usecs\n", - hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT)); + netdev_err(netdev, "rx-usec is limited to %d usecs\n", + hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT)); return -EINVAL; } if (rx_frames > pdata->rx_desc_count) { - netdev_alert(netdev, "rx-frames is limited to %d frames\n", - pdata->rx_desc_count); + netdev_err(netdev, "rx-frames is limited to %d frames\n", + pdata->rx_desc_count); return -EINVAL; } @@ -463,8 +462,8 @@ static int xgbe_set_coalesce(struct net_device *netdev, /* Check the bounds of values for Tx */ if (tx_frames > pdata->tx_desc_count) { - netdev_alert(netdev, "tx-frames is limited to %d frames\n", - pdata->tx_desc_count); + netdev_err(netdev, "tx-frames is limited to %d frames\n", + pdata->tx_desc_count); return -EINVAL; } @@ -476,8 +475,6 @@ static int xgbe_set_coalesce(struct net_device *netdev, pdata->tx_frames = tx_frames; hw_if->config_tx_coalesce(pdata); - DBGPR("<--xgbe_set_coalesce\n"); - return 0; } @@ -539,8 +536,10 @@ static int xgbe_set_rxfh(struct net_device *netdev, const u32 *indir, struct xgbe_hw_if *hw_if = &pdata->hw_if; unsigned int ret; - if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) { + netdev_err(netdev, "unsupported hash function\n"); return -EOPNOTSUPP; + } if (indir) { ret = hw_if->set_rss_lookup_table(pdata, indir); @@ -594,6 +593,8 @@ static const struct ethtool_ops xgbe_ethtool_ops = { .get_settings = xgbe_get_settings, .set_settings = xgbe_set_settings, .get_drvinfo = xgbe_get_drvinfo, + .get_msglevel = xgbe_get_msglevel, + .set_msglevel = xgbe_set_msglevel, .get_link = ethtool_op_get_link, .get_coalesce = xgbe_get_coalesce, .set_coalesce = xgbe_set_coalesce, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index e83bd76abce6..7dd893331785 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -371,7 +371,7 @@ static int xgbe_probe(struct platform_device *pdev) set_bit(XGBE_DOWN, &pdata->dev_state); /* Check if we should use ACPI or DT */ - pdata->use_acpi = (!pdata->adev || acpi_disabled) ? 0 : 1; + pdata->use_acpi = dev->of_node ? 0 : 1; phy_pdev = xgbe_get_phy_pdev(pdata); if (!phy_pdev) { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 9088c3a35a20..446058081866 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -1115,8 +1115,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) unsigned int reg, link_aneg; if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) { - if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state)) - netif_carrier_off(pdata->netdev); + netif_carrier_off(pdata->netdev); pdata->phy.link = 0; goto adjust_link; @@ -1142,10 +1141,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) clear_bit(XGBE_LINK_INIT, &pdata->dev_state); - if (!test_bit(XGBE_LINK, &pdata->dev_state)) { - set_bit(XGBE_LINK, &pdata->dev_state); - netif_carrier_on(pdata->netdev); - } + netif_carrier_on(pdata->netdev); } else { if (test_bit(XGBE_LINK_INIT, &pdata->dev_state)) { xgbe_check_link_timeout(pdata); @@ -1156,10 +1152,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata) xgbe_phy_status_aneg(pdata); - if (test_bit(XGBE_LINK, &pdata->dev_state)) { - clear_bit(XGBE_LINK, &pdata->dev_state); - netif_carrier_off(pdata->netdev); - } + netif_carrier_off(pdata->netdev); } adjust_link: @@ -1179,8 +1172,7 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata) devm_free_irq(pdata->dev, pdata->an_irq, pdata); pdata->phy.link = 0; - if (test_and_clear_bit(XGBE_LINK, &pdata->dev_state)) - netif_carrier_off(pdata->netdev); + netif_carrier_off(pdata->netdev); xgbe_phy_adjust_link(pdata); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 8c9d01ef730d..e234b9970318 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -209,8 +209,6 @@ #define XGMAC_IOCTL_CONTEXT 2 #define XGBE_FIFO_MAX 81920 -#define XGBE_FIFO_SIZE_B(x) (x) -#define XGBE_FIFO_SIZE_KB(x) (x * 1024) #define XGBE_TC_MIN_QUANTUM 10 @@ -461,7 +459,6 @@ struct xgbe_channel { enum xgbe_state { XGBE_DOWN, - XGBE_LINK, XGBE_LINK_INIT, XGBE_LINK_ERR, }; @@ -483,20 +480,6 @@ enum xgbe_int_state { XGMAC_INT_STATE_RESTORE, }; -enum xgbe_mtl_fifo_size { - XGMAC_MTL_FIFO_SIZE_256 = 0x00, - XGMAC_MTL_FIFO_SIZE_512 = 0x01, - XGMAC_MTL_FIFO_SIZE_1K = 0x03, - XGMAC_MTL_FIFO_SIZE_2K = 0x07, - XGMAC_MTL_FIFO_SIZE_4K = 0x0f, - XGMAC_MTL_FIFO_SIZE_8K = 0x1f, - XGMAC_MTL_FIFO_SIZE_16K = 0x3f, - XGMAC_MTL_FIFO_SIZE_32K = 0x7f, - XGMAC_MTL_FIFO_SIZE_64K = 0xff, - XGMAC_MTL_FIFO_SIZE_128K = 0x1ff, - XGMAC_MTL_FIFO_SIZE_256K = 0x3ff, -}; - enum xgbe_speed { XGBE_SPEED_1000 = 0, XGBE_SPEED_2500, @@ -598,6 +581,7 @@ struct xgbe_mmc_stats { struct xgbe_ext_stats { u64 tx_tso_packets; u64 rx_split_header_packets; + u64 rx_buffer_unavailable; }; struct xgbe_hw_if { diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index c4bb8027b3fb..652f21889a48 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -107,7 +107,8 @@ static void xgene_enet_set_ring_state(struct xgene_enet_desc_ring *ring) { xgene_enet_ring_set_type(ring); - if (xgene_enet_ring_owner(ring->id) == RING_OWNER_ETH0) + if (xgene_enet_ring_owner(ring->id) == RING_OWNER_ETH0 || + xgene_enet_ring_owner(ring->id) == RING_OWNER_ETH1) xgene_enet_ring_set_recombbuf(ring); xgene_enet_ring_init(ring); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index e47298faf78d..6b1846df7b0c 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1305,10 +1305,17 @@ static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata) pdata->ring_num = START_RING_NUM_0; break; case 1: - pdata->cpu_bufnum = START_CPU_BUFNUM_1; - pdata->eth_bufnum = START_ETH_BUFNUM_1; - pdata->bp_bufnum = START_BP_BUFNUM_1; - pdata->ring_num = START_RING_NUM_1; + if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { + pdata->cpu_bufnum = XG_START_CPU_BUFNUM_1; + pdata->eth_bufnum = XG_START_ETH_BUFNUM_1; + pdata->bp_bufnum = XG_START_BP_BUFNUM_1; + pdata->ring_num = XG_START_RING_NUM_1; + } else { + pdata->cpu_bufnum = START_CPU_BUFNUM_1; + pdata->eth_bufnum = START_ETH_BUFNUM_1; + pdata->bp_bufnum = START_BP_BUFNUM_1; + pdata->ring_num = START_RING_NUM_1; + } break; default: break; @@ -1478,6 +1485,7 @@ static const struct acpi_device_id xgene_enet_acpi_match[] = { { "APMC0D05", XGENE_ENET1}, { "APMC0D30", XGENE_ENET1}, { "APMC0D31", XGENE_ENET1}, + { "APMC0D3F", XGENE_ENET1}, { "APMC0D26", XGENE_ENET2}, { "APMC0D25", XGENE_ENET2}, { } diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 50f92c39ed2a..ff89a5d98d01 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -56,6 +56,11 @@ #define START_BP_BUFNUM_1 0x2A #define START_RING_NUM_1 264 +#define XG_START_CPU_BUFNUM_1 12 +#define XG_START_ETH_BUFNUM_1 2 +#define XG_START_BP_BUFNUM_1 0x22 +#define XG_START_RING_NUM_1 264 + #define X2_START_CPU_BUFNUM_0 0 #define X2_START_ETH_BUFNUM_0 0 #define X2_START_BP_BUFNUM_0 0x20 diff --git a/drivers/net/ethernet/apple/Kconfig b/drivers/net/ethernet/apple/Kconfig index d19a41b0c6d2..31071297896c 100644 --- a/drivers/net/ethernet/apple/Kconfig +++ b/drivers/net/ethernet/apple/Kconfig @@ -51,7 +51,7 @@ config BMAC will be called bmac. config MACMACE - bool "Macintosh (AV) onboard MACE ethernet" + tristate "Macintosh (AV) onboard MACE ethernet" depends on MAC select CRC32 ---help--- diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c index 48694c239d5c..872b7abb0196 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_ethtool.c @@ -233,10 +233,6 @@ static void atl1c_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = atl1c_get_regs_len(netdev); - drvinfo->eedump_len = atl1c_get_eeprom_len(netdev); } static void atl1c_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c index 1be072f4afc2..8e3dbd4d9f79 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c @@ -316,10 +316,6 @@ static void atl1e_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->fw_version, "L1e", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = atl1e_get_regs_len(netdev); - drvinfo->eedump_len = atl1e_get_eeprom_len(netdev); } static void atl1e_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index eca1d113fee1..529bca718334 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -3388,7 +3388,6 @@ static void atl1_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->eedump_len = ATL1_EEDUMP_LEN; } static void atl1_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 46a535318c7a..8f76f4558a88 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -2030,10 +2030,6 @@ static void atl2_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->fw_version, "L2", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = atl2_get_regs_len(netdev); - drvinfo->eedump_len = atl2_get_eeprom_len(netdev); } static void atl2_get_wol(struct net_device *netdev, diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index a7f2cc3e485e..95af75d35bc5 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1333,7 +1333,6 @@ static void bcm_enet_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "bcm63xx", sizeof(drvinfo->bus_info)); - drvinfo->n_stats = BCM_ENET_STATS_LEN; } static int bcm_enet_get_sset_count(struct net_device *netdev, @@ -2597,7 +2596,6 @@ static void bcm_enetsw_get_drvinfo(struct net_device *netdev, strncpy(drvinfo->version, bcm_enet_driver_version, 32); strncpy(drvinfo->fw_version, "N/A", 32); strncpy(drvinfo->bus_info, "bcm63xx", 32); - drvinfo->n_stats = BCM_ENETSW_STATS_LEN; } static void bcm_enetsw_get_ethtool_stats(struct net_device *netdev, diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index f1b5364f3521..858106352ce9 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -287,7 +287,6 @@ static void bcm_sysport_get_drvinfo(struct net_device *dev, strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); strlcpy(info->version, "0.1", sizeof(info->version)); strlcpy(info->bus_info, "platform", sizeof(info->bus_info)); - info->n_stats = BCM_SYSPORT_STATS_LEN; } static u32 bcm_sysport_get_msglvl(struct net_device *dev) diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 2b66ef3d8217..8fc3f3c137f8 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -813,6 +813,46 @@ bnx2_alloc_rx_mem(struct bnx2 *bp) } static void +bnx2_free_stats_blk(struct net_device *dev) +{ + struct bnx2 *bp = netdev_priv(dev); + + if (bp->status_blk) { + dma_free_coherent(&bp->pdev->dev, bp->status_stats_size, + bp->status_blk, + bp->status_blk_mapping); + bp->status_blk = NULL; + bp->stats_blk = NULL; + } +} + +static int +bnx2_alloc_stats_blk(struct net_device *dev) +{ + int status_blk_size; + void *status_blk; + struct bnx2 *bp = netdev_priv(dev); + + /* Combine status and statistics blocks into one allocation. */ + status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); + if (bp->flags & BNX2_FLAG_MSIX_CAP) + status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * + BNX2_SBLK_MSIX_ALIGN_SIZE); + bp->status_stats_size = status_blk_size + + sizeof(struct statistics_block); + status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size, + &bp->status_blk_mapping, GFP_KERNEL); + if (status_blk == NULL) + return -ENOMEM; + + bp->status_blk = status_blk; + bp->stats_blk = status_blk + status_blk_size; + bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; + + return 0; +} + +static void bnx2_free_mem(struct bnx2 *bp) { int i; @@ -829,37 +869,19 @@ bnx2_free_mem(struct bnx2 *bp) bp->ctx_blk[i] = NULL; } } - if (bnapi->status_blk.msi) { - dma_free_coherent(&bp->pdev->dev, bp->status_stats_size, - bnapi->status_blk.msi, - bp->status_blk_mapping); + + if (bnapi->status_blk.msi) bnapi->status_blk.msi = NULL; - bp->stats_blk = NULL; - } } static int bnx2_alloc_mem(struct bnx2 *bp) { - int i, status_blk_size, err; + int i, err; struct bnx2_napi *bnapi; - void *status_blk; - - /* Combine status and statistics blocks into one allocation. */ - status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); - if (bp->flags & BNX2_FLAG_MSIX_CAP) - status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * - BNX2_SBLK_MSIX_ALIGN_SIZE); - bp->status_stats_size = status_blk_size + - sizeof(struct statistics_block); - - status_blk = dma_zalloc_coherent(&bp->pdev->dev, bp->status_stats_size, - &bp->status_blk_mapping, GFP_KERNEL); - if (status_blk == NULL) - goto alloc_mem_err; bnapi = &bp->bnx2_napi[0]; - bnapi->status_blk.msi = status_blk; + bnapi->status_blk.msi = bp->status_blk; bnapi->hw_tx_cons_ptr = &bnapi->status_blk.msi->status_tx_quick_consumer_index0; bnapi->hw_rx_cons_ptr = @@ -870,7 +892,7 @@ bnx2_alloc_mem(struct bnx2 *bp) bnapi = &bp->bnx2_napi[i]; - sblk = (status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); + sblk = (bp->status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); bnapi->status_blk.msix = sblk; bnapi->hw_tx_cons_ptr = &sblk->status_tx_quick_consumer_index; @@ -880,10 +902,6 @@ bnx2_alloc_mem(struct bnx2 *bp) } } - bp->stats_blk = status_blk + status_blk_size; - - bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; - if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { bp->ctx_pages = 0x2000 / BNX2_PAGE_SIZE; if (bp->ctx_pages == 0) @@ -8330,6 +8348,11 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->phy_addr = 1; + /* allocate stats_blk */ + rc = bnx2_alloc_stats_blk(dev); + if (rc) + goto err_out_unmap; + /* Disable WOL support if we are running on a SERDES chip. */ if (BNX2_CHIP(bp) == BNX2_CHIP_5709) bnx2_get_5709_media(bp); @@ -8453,6 +8476,8 @@ err_out_disable: pci_disable_device(pdev); err_out: + kfree(bp->temp_stats_blk); + return rc; } @@ -8586,6 +8611,7 @@ error: pci_release_regions(pdev); pci_disable_device(pdev); err_free: + bnx2_free_stats_blk(dev); free_netdev(dev); return rc; } @@ -8603,6 +8629,7 @@ bnx2_remove_one(struct pci_dev *pdev) pci_iounmap(bp->pdev, bp->regview); + bnx2_free_stats_blk(dev); kfree(bp->temp_stats_blk); if (bp->flags & BNX2_FLAG_AER_ENABLED) { diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index f92f76c44756..380234d72b95 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -6928,6 +6928,7 @@ struct bnx2 { dma_addr_t status_blk_mapping; + void *status_blk; struct statistics_block *stats_blk; struct statistics_block *temp_stats_blk; dma_addr_t stats_blk_mapping; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index be628bd9fb18..d84efcd34fac 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1090,10 +1090,6 @@ static void bnx2x_get_drvinfo(struct net_device *dev, bnx2x_fill_fw_str(bp, info->fw_version, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); - info->n_stats = BNX2X_NUM_STATS; - info->testinfo_len = BNX2X_NUM_TESTS(bp); - info->eedump_len = bp->common.flash_size; - info->regdump_len = bnx2x_get_regs_len(dev); } static void bnx2x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 1805541b4240..50f63b7f3c3e 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -205,6 +205,23 @@ enum dma_reg { DMA_INDEX2RING_5, DMA_INDEX2RING_6, DMA_INDEX2RING_7, + DMA_RING0_TIMEOUT, + DMA_RING1_TIMEOUT, + DMA_RING2_TIMEOUT, + DMA_RING3_TIMEOUT, + DMA_RING4_TIMEOUT, + DMA_RING5_TIMEOUT, + DMA_RING6_TIMEOUT, + DMA_RING7_TIMEOUT, + DMA_RING8_TIMEOUT, + DMA_RING9_TIMEOUT, + DMA_RING10_TIMEOUT, + DMA_RING11_TIMEOUT, + DMA_RING12_TIMEOUT, + DMA_RING13_TIMEOUT, + DMA_RING14_TIMEOUT, + DMA_RING15_TIMEOUT, + DMA_RING16_TIMEOUT, }; static const u8 bcmgenet_dma_regs_v3plus[] = { @@ -216,6 +233,23 @@ static const u8 bcmgenet_dma_regs_v3plus[] = { [DMA_PRIORITY_0] = 0x30, [DMA_PRIORITY_1] = 0x34, [DMA_PRIORITY_2] = 0x38, + [DMA_RING0_TIMEOUT] = 0x2C, + [DMA_RING1_TIMEOUT] = 0x30, + [DMA_RING2_TIMEOUT] = 0x34, + [DMA_RING3_TIMEOUT] = 0x38, + [DMA_RING4_TIMEOUT] = 0x3c, + [DMA_RING5_TIMEOUT] = 0x40, + [DMA_RING6_TIMEOUT] = 0x44, + [DMA_RING7_TIMEOUT] = 0x48, + [DMA_RING8_TIMEOUT] = 0x4c, + [DMA_RING9_TIMEOUT] = 0x50, + [DMA_RING10_TIMEOUT] = 0x54, + [DMA_RING11_TIMEOUT] = 0x58, + [DMA_RING12_TIMEOUT] = 0x5c, + [DMA_RING13_TIMEOUT] = 0x60, + [DMA_RING14_TIMEOUT] = 0x64, + [DMA_RING15_TIMEOUT] = 0x68, + [DMA_RING16_TIMEOUT] = 0x6C, [DMA_INDEX2RING_0] = 0x70, [DMA_INDEX2RING_1] = 0x74, [DMA_INDEX2RING_2] = 0x78, @@ -235,6 +269,23 @@ static const u8 bcmgenet_dma_regs_v2[] = { [DMA_PRIORITY_0] = 0x34, [DMA_PRIORITY_1] = 0x38, [DMA_PRIORITY_2] = 0x3C, + [DMA_RING0_TIMEOUT] = 0x2C, + [DMA_RING1_TIMEOUT] = 0x30, + [DMA_RING2_TIMEOUT] = 0x34, + [DMA_RING3_TIMEOUT] = 0x38, + [DMA_RING4_TIMEOUT] = 0x3c, + [DMA_RING5_TIMEOUT] = 0x40, + [DMA_RING6_TIMEOUT] = 0x44, + [DMA_RING7_TIMEOUT] = 0x48, + [DMA_RING8_TIMEOUT] = 0x4c, + [DMA_RING9_TIMEOUT] = 0x50, + [DMA_RING10_TIMEOUT] = 0x54, + [DMA_RING11_TIMEOUT] = 0x58, + [DMA_RING12_TIMEOUT] = 0x5c, + [DMA_RING13_TIMEOUT] = 0x60, + [DMA_RING14_TIMEOUT] = 0x64, + [DMA_RING15_TIMEOUT] = 0x68, + [DMA_RING16_TIMEOUT] = 0x6C, }; static const u8 bcmgenet_dma_regs_v1[] = { @@ -245,6 +296,23 @@ static const u8 bcmgenet_dma_regs_v1[] = { [DMA_PRIORITY_0] = 0x34, [DMA_PRIORITY_1] = 0x38, [DMA_PRIORITY_2] = 0x3C, + [DMA_RING0_TIMEOUT] = 0x2C, + [DMA_RING1_TIMEOUT] = 0x30, + [DMA_RING2_TIMEOUT] = 0x34, + [DMA_RING3_TIMEOUT] = 0x38, + [DMA_RING4_TIMEOUT] = 0x3c, + [DMA_RING5_TIMEOUT] = 0x40, + [DMA_RING6_TIMEOUT] = 0x44, + [DMA_RING7_TIMEOUT] = 0x48, + [DMA_RING8_TIMEOUT] = 0x4c, + [DMA_RING9_TIMEOUT] = 0x50, + [DMA_RING10_TIMEOUT] = 0x54, + [DMA_RING11_TIMEOUT] = 0x58, + [DMA_RING12_TIMEOUT] = 0x5c, + [DMA_RING13_TIMEOUT] = 0x60, + [DMA_RING14_TIMEOUT] = 0x64, + [DMA_RING15_TIMEOUT] = 0x68, + [DMA_RING16_TIMEOUT] = 0x6C, }; /* Set at runtime once bcmgenet version is known */ @@ -498,6 +566,85 @@ static void bcmgenet_set_msglevel(struct net_device *dev, u32 level) priv->msg_enable = level; } +static int bcmgenet_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + + ec->tx_max_coalesced_frames = + bcmgenet_tdma_ring_readl(priv, DESC_INDEX, + DMA_MBUF_DONE_THRESH); + ec->rx_max_coalesced_frames = + bcmgenet_rdma_ring_readl(priv, DESC_INDEX, + DMA_MBUF_DONE_THRESH); + ec->rx_coalesce_usecs = + bcmgenet_rdma_readl(priv, DMA_RING16_TIMEOUT) * 8192 / 1000; + + return 0; +} + +static int bcmgenet_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + unsigned int i; + u32 reg; + + /* Base system clock is 125Mhz, DMA timeout is this reference clock + * divided by 1024, which yields roughly 8.192us, our maximum value + * has to fit in the DMA_TIMEOUT_MASK (16 bits) + */ + if (ec->tx_max_coalesced_frames > DMA_INTR_THRESHOLD_MASK || + ec->tx_max_coalesced_frames == 0 || + ec->rx_max_coalesced_frames > DMA_INTR_THRESHOLD_MASK || + ec->rx_coalesce_usecs > (DMA_TIMEOUT_MASK * 8) + 1) + return -EINVAL; + + if (ec->rx_coalesce_usecs == 0 && ec->rx_max_coalesced_frames == 0) + return -EINVAL; + + /* GENET TDMA hardware does not support a configurable timeout, but will + * always generate an interrupt either after MBDONE packets have been + * transmitted, or when the ring is emtpy. + */ + if (ec->tx_coalesce_usecs || ec->tx_coalesce_usecs_high || + ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_low) + return -EOPNOTSUPP; + + /* Program all TX queues with the same values, as there is no + * ethtool knob to do coalescing on a per-queue basis + */ + for (i = 0; i < priv->hw_params->tx_queues; i++) + bcmgenet_tdma_ring_writel(priv, i, + ec->tx_max_coalesced_frames, + DMA_MBUF_DONE_THRESH); + bcmgenet_tdma_ring_writel(priv, DESC_INDEX, + ec->tx_max_coalesced_frames, + DMA_MBUF_DONE_THRESH); + + for (i = 0; i < priv->hw_params->rx_queues; i++) { + bcmgenet_rdma_ring_writel(priv, i, + ec->rx_max_coalesced_frames, + DMA_MBUF_DONE_THRESH); + + reg = bcmgenet_rdma_readl(priv, DMA_RING0_TIMEOUT + i); + reg &= ~DMA_TIMEOUT_MASK; + reg |= DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000, 8192); + bcmgenet_rdma_writel(priv, reg, DMA_RING0_TIMEOUT + i); + } + + bcmgenet_rdma_ring_writel(priv, DESC_INDEX, + ec->rx_max_coalesced_frames, + DMA_MBUF_DONE_THRESH); + + reg = bcmgenet_rdma_readl(priv, DMA_RING16_TIMEOUT); + reg &= ~DMA_TIMEOUT_MASK; + reg |= DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000, 8192); + bcmgenet_rdma_writel(priv, reg, DMA_RING16_TIMEOUT); + + return 0; +} + /* standard ethtool support functions. */ enum bcmgenet_stat_type { BCMGENET_STAT_NETDEV = -1, @@ -646,7 +793,6 @@ static void bcmgenet_get_drvinfo(struct net_device *dev, { strlcpy(info->driver, "bcmgenet", sizeof(info->driver)); strlcpy(info->version, "v2.0", sizeof(info->version)); - info->n_stats = BCMGENET_STATS_LEN; } static int bcmgenet_get_sset_count(struct net_device *dev, int string_set) @@ -844,6 +990,8 @@ static struct ethtool_ops bcmgenet_ethtool_ops = { .get_eee = bcmgenet_get_eee, .set_eee = bcmgenet_set_eee, .nway_reset = bcmgenet_nway_reset, + .get_coalesce = bcmgenet_get_coalesce, + .set_coalesce = bcmgenet_set_coalesce, }; /* Power down the unimac, based on mode. */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 7299d1075422..29dc2f1bbb19 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -304,13 +304,12 @@ struct bcmgenet_mib_counters { #define UMAC_IRQ_RXDMA_MBDONE (1 << 13) #define UMAC_IRQ_RXDMA_PDONE (1 << 14) #define UMAC_IRQ_RXDMA_BDONE (1 << 15) -#define UMAC_IRQ_RXDMA_DONE (UMAC_IRQ_RXDMA_PDONE | \ - UMAC_IRQ_RXDMA_BDONE) +#define UMAC_IRQ_RXDMA_DONE UMAC_IRQ_RXDMA_MBDONE #define UMAC_IRQ_TXDMA_MBDONE (1 << 16) #define UMAC_IRQ_TXDMA_PDONE (1 << 17) #define UMAC_IRQ_TXDMA_BDONE (1 << 18) -#define UMAC_IRQ_TXDMA_DONE (UMAC_IRQ_TXDMA_PDONE | \ - UMAC_IRQ_TXDMA_BDONE) +#define UMAC_IRQ_TXDMA_DONE UMAC_IRQ_TXDMA_MBDONE + /* Only valid for GENETv3+ */ #define UMAC_IRQ_MDIO_DONE (1 << 23) #define UMAC_IRQ_MDIO_ERROR (1 << 24) @@ -386,7 +385,7 @@ struct bcmgenet_mib_counters { #define DMA_RING_BUFFER_SIZE_MASK 0xFFFF /* DMA interrupt threshold register */ -#define DMA_INTR_THRESHOLD_MASK 0x00FF +#define DMA_INTR_THRESHOLD_MASK 0x01FF /* DMA XON/XOFF register */ #define DMA_XON_THREHOLD_MASK 0xFFFF diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 29f330831784..245c063ed4db 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -153,7 +153,6 @@ lio_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strncpy(drvinfo->fw_version, oct->fw_info.liquidio_firmware_version, ETHTOOL_FWVERS_LEN); strncpy(drvinfo->bus_info, pci_name(oct->pci_dev), 32); - drvinfo->regdump_len = OCT_ETHTOOL_REGDUMP_LEN; } static void diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index fa0c7b54ec7a..634e50c8c5ef 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -47,6 +47,7 @@ #include <linux/timer.h> #include <linux/vmalloc.h> #include <linux/etherdevice.h> +#include <linux/net_tstamp.h> #include <asm/io.h> #include "cxgb4_uld.h" @@ -478,6 +479,8 @@ struct port_info { #ifdef CONFIG_CHELSIO_T4_FCOE struct cxgb_fcoe fcoe; #endif /* CONFIG_CHELSIO_T4_FCOE */ + bool rxtstamp; /* Enable TS */ + struct hwtstamp_config tstamp_config; }; struct dentry; @@ -517,6 +520,7 @@ struct sge_fl { /* SGE free-buffer queue state */ /* A packet gather list */ struct pkt_gl { + u64 sgetstamp; /* SGE Time Stamp for Ingress Packet */ struct page_frag frags[MAX_SKB_FRAGS]; void *va; /* virtual address of first byte */ unsigned int nfrags; /* # of fragments */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 0a87a3247464..4269944c5db5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -940,6 +940,7 @@ static const char * const devlog_level_strings[] = { static const char * const devlog_facility_strings[] = { [FW_DEVLOG_FACILITY_CORE] = "CORE", + [FW_DEVLOG_FACILITY_CF] = "CF", [FW_DEVLOG_FACILITY_SCHED] = "SCHED", [FW_DEVLOG_FACILITY_TIMER] = "TIMER", [FW_DEVLOG_FACILITY_RES] = "RES", @@ -1128,18 +1129,26 @@ static const struct file_operations devlog_fops = { static int mbox_show(struct seq_file *seq, void *v) { static const char * const owner[] = { "none", "FW", "driver", - "unknown" }; + "unknown", "<unread>" }; int i; unsigned int mbox = (uintptr_t)seq->private & 7; struct adapter *adap = seq->private - mbox; void __iomem *addr = adap->regs + PF_REG(mbox, CIM_PF_MAILBOX_DATA_A); - unsigned int ctrl_reg = (is_t4(adap->params.chip) - ? CIM_PF_MAILBOX_CTRL_A - : CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A); - void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg); - i = MBOWNER_G(readl(ctrl)); + /* For T4 we don't have a shadow copy of the Mailbox Control register. + * And since reading that real register causes a side effect of + * granting ownership, we're best of simply not reading it at all. + */ + if (is_t4(adap->params.chip)) { + i = 4; /* index of "<unread>" */ + } else { + unsigned int ctrl_reg = CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A; + void __iomem *ctrl = adap->regs + PF_REG(mbox, ctrl_reg); + + i = MBOWNER_G(readl(ctrl)); + } + seq_printf(seq, "mailbox owned by %s\n\n", owner[i]); for (i = 0; i < MBOX_LEN; i += 8) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 5eedb98ff581..02e4e028a647 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -961,6 +961,20 @@ static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) return ret; } +static int get_ts_info(struct net_device *dev, struct ethtool_ts_info *ts_info) +{ + ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + + ts_info->so_timestamping |= SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + ts_info->phc_index = -1; + + return 0; +} + static u32 get_rss_table_size(struct net_device *dev) { const struct port_info *pi = netdev_priv(dev); @@ -1095,6 +1109,7 @@ static const struct ethtool_ops cxgb_ethtool_ops = { .get_rxfh = get_rss_table, .set_rxfh = set_rss_table, .flash_device = set_flash, + .get_ts_info = get_ts_info }; void cxgb4_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index f5dcde27e402..c29227ee9ee8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -275,7 +275,7 @@ static void link_report(struct net_device *dev) else { static const char *fc[] = { "no", "Rx", "Tx", "Tx/Rx" }; - const char *s = "10Mbps"; + const char *s; const struct port_info *p = netdev_priv(dev); switch (p->link_cfg.speed) { @@ -291,6 +291,10 @@ static void link_report(struct net_device *dev) case 40000: s = "40Gbps"; break; + default: + pr_info("%s: unsupported speed: %d\n", + dev->name, p->link_cfg.speed); + return; } netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s, @@ -2959,6 +2963,30 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) ret = t4_mdio_wr(pi->adapter, mbox, prtad, devad, data->reg_num, data->val_in); break; + case SIOCGHWTSTAMP: + return copy_to_user(req->ifr_data, &pi->tstamp_config, + sizeof(pi->tstamp_config)) ? + -EFAULT : 0; + case SIOCSHWTSTAMP: + if (copy_from_user(&pi->tstamp_config, req->ifr_data, + sizeof(pi->tstamp_config))) + return -EFAULT; + + switch (pi->tstamp_config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + pi->rxtstamp = false; + break; + case HWTSTAMP_FILTER_ALL: + pi->rxtstamp = true; + break; + default: + pi->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + return copy_to_user(req->ifr_data, &pi->tstamp_config, + sizeof(pi->tstamp_config)) ? + -EFAULT : 0; default: return -EOPNOTSUPP; } @@ -3670,7 +3698,7 @@ static int adap_init0(struct adapter *adap) t4_get_tp_version(adap, &adap->params.tp_vers); ret = t4_check_fw_version(adap); /* If firmware is too old (not supported by driver) force an update. */ - if (ret == -EFAULT) + if (ret) state = DEV_STATE_UNINIT; if ((adap->flags & MASTER_PF) && state != DEV_STATE_INIT) { struct fw_info *fw_info; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 9162746d7729..b7b93e7a643d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1820,11 +1820,34 @@ static noinline int handle_trace_pkt(struct adapter *adap, return 0; } +/** + * cxgb4_sgetim_to_hwtstamp - convert sge time stamp to hw time stamp + * @adap: the adapter + * @hwtstamps: time stamp structure to update + * @sgetstamp: 60bit iqe timestamp + * + * Every ingress queue entry has the 60-bit timestamp, convert that timestamp + * which is in Core Clock ticks into ktime_t and assign it + **/ +static void cxgb4_sgetim_to_hwtstamp(struct adapter *adap, + struct skb_shared_hwtstamps *hwtstamps, + u64 sgetstamp) +{ + u64 ns; + u64 tmp = (sgetstamp * 1000 * 1000 + adap->params.vpd.cclk / 2); + + ns = div_u64(tmp, adap->params.vpd.cclk); + + memset(hwtstamps, 0, sizeof(*hwtstamps)); + hwtstamps->hwtstamp = ns_to_ktime(ns); +} + static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, const struct cpl_rx_pkt *pkt) { struct adapter *adapter = rxq->rspq.adap; struct sge *s = &adapter->sge; + struct port_info *pi; int ret; struct sk_buff *skb; @@ -1842,6 +1865,10 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, skb->ip_summed = CHECKSUM_UNNECESSARY; skb_record_rx_queue(skb, rxq->rspq.idx); skb_mark_napi_id(skb, &rxq->rspq.napi); + pi = netdev_priv(skb->dev); + if (pi->rxtstamp) + cxgb4_sgetim_to_hwtstamp(adapter, skb_hwtstamps(skb), + gl->sgetstamp); if (rxq->rspq.netdev->features & NETIF_F_RXHASH) skb_set_hash(skb, (__force u32)pkt->rsshdr.hash_val, PKT_HASH_TYPE_L3); @@ -1877,9 +1904,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, struct sge *s = &q->adap->sge; int cpl_trace_pkt = is_t4(q->adap->params.chip) ? CPL_TRACE_PKT : CPL_TRACE_PKT_T5; -#ifdef CONFIG_CHELSIO_T4_FCOE struct port_info *pi; -#endif if (unlikely(*(u8 *)rsp == cpl_trace_pkt)) return handle_trace_pkt(q->adap, si); @@ -1910,6 +1935,10 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, rxq->stats.pkts++; + pi = netdev_priv(skb->dev); + if (pi->rxtstamp) + cxgb4_sgetim_to_hwtstamp(q->adap, skb_hwtstamps(skb), + si->sgetstamp); if (csum_ok && (pkt->l2info & htonl(RXF_UDP_F | RXF_TCP_F))) { if (!pkt->ip_frag) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1926,7 +1955,6 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, #define CPL_RX_PKT_FLAGS (RXF_PSH_F | RXF_SYN_F | RXF_UDP_F | \ RXF_TCP_F | RXF_IP_F | RXF_IP6_F | RXF_LRO_F) - pi = netdev_priv(skb->dev); if (!(pkt->l2info & cpu_to_be32(CPL_RX_PKT_FLAGS))) { if ((pkt->l2info & cpu_to_be32(RXF_FCOE_F)) && (pi->fcoe.flags & CXGB_FCOE_ENABLED)) { @@ -2067,6 +2095,8 @@ static int process_responses(struct sge_rspq *q, int budget) unmap_rx_buf(q->adap, &rxq->fl); } + si.sgetstamp = SGE_TIMESTAMP_G( + be64_to_cpu(rc->last_flit)); /* * Last buffer remains mapped so explicitly make it * coherent for CPU access. diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 44806253c178..cf61a5869c6e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -699,50 +699,107 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) { static const unsigned int t4_reg_ranges[] = { 0x1008, 0x1108, - 0x1180, 0x11b4, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x123c, 0x1300, 0x173c, 0x1800, 0x18fc, - 0x3000, 0x305c, - 0x3068, 0x30d8, - 0x30e0, 0x5924, - 0x5960, 0x59d4, - 0x5a00, 0x5af8, + 0x3000, 0x30d8, + 0x30e0, 0x30e4, + 0x30ec, 0x5910, + 0x5920, 0x5924, + 0x5960, 0x5960, + 0x5968, 0x5968, + 0x5970, 0x5970, + 0x5978, 0x5978, + 0x5980, 0x5980, + 0x5988, 0x5988, + 0x5990, 0x5990, + 0x5998, 0x5998, + 0x59a0, 0x59d4, + 0x5a00, 0x5ae0, + 0x5ae8, 0x5ae8, + 0x5af0, 0x5af0, + 0x5af8, 0x5af8, 0x6000, 0x6098, 0x6100, 0x6150, 0x6200, 0x6208, 0x6240, 0x6248, - 0x6280, 0x6338, + 0x6280, 0x62b0, + 0x62c0, 0x6338, 0x6370, 0x638c, 0x6400, 0x643c, 0x6500, 0x6524, - 0x6a00, 0x6a38, - 0x6a60, 0x6a78, - 0x6b00, 0x6b84, - 0x6bf0, 0x6c84, - 0x6cf0, 0x6d84, - 0x6df0, 0x6e84, - 0x6ef0, 0x6f84, - 0x6ff0, 0x7084, - 0x70f0, 0x7184, - 0x71f0, 0x7284, - 0x72f0, 0x7384, - 0x73f0, 0x7450, + 0x6a00, 0x6a04, + 0x6a14, 0x6a38, + 0x6a60, 0x6a70, + 0x6a78, 0x6a78, + 0x6b00, 0x6b0c, + 0x6b1c, 0x6b84, + 0x6bf0, 0x6bf8, + 0x6c00, 0x6c0c, + 0x6c1c, 0x6c84, + 0x6cf0, 0x6cf8, + 0x6d00, 0x6d0c, + 0x6d1c, 0x6d84, + 0x6df0, 0x6df8, + 0x6e00, 0x6e0c, + 0x6e1c, 0x6e84, + 0x6ef0, 0x6ef8, + 0x6f00, 0x6f0c, + 0x6f1c, 0x6f84, + 0x6ff0, 0x6ff8, + 0x7000, 0x700c, + 0x701c, 0x7084, + 0x70f0, 0x70f8, + 0x7100, 0x710c, + 0x711c, 0x7184, + 0x71f0, 0x71f8, + 0x7200, 0x720c, + 0x721c, 0x7284, + 0x72f0, 0x72f8, + 0x7300, 0x730c, + 0x731c, 0x7384, + 0x73f0, 0x73f8, + 0x7400, 0x7450, 0x7500, 0x7530, - 0x7600, 0x761c, + 0x7600, 0x760c, + 0x7614, 0x761c, 0x7680, 0x76cc, 0x7700, 0x7798, 0x77c0, 0x77fc, 0x7900, 0x79fc, - 0x7b00, 0x7c38, - 0x7d00, 0x7efc, - 0x8dc0, 0x8e1c, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c38, + 0x7d00, 0x7d38, + 0x7d40, 0x7d80, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7ea4, + 0x7eac, 0x7edc, + 0x7ee8, 0x7efc, + 0x8dc0, 0x8e04, + 0x8e10, 0x8e1c, 0x8e30, 0x8e78, - 0x8ea0, 0x8f6c, - 0x8fc0, 0x9074, + 0x8ea0, 0x8eb8, + 0x8ec0, 0x8f6c, + 0x8fc0, 0x9008, + 0x9010, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x9074, 0x90fc, 0x90fc, - 0x9400, 0x9458, - 0x9600, 0x96bc, + 0x9400, 0x9408, + 0x9410, 0x9458, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x96bc, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -754,23 +811,42 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9e80, 0x9eec, 0x9f00, 0x9f6c, 0x9f80, 0x9fec, - 0xd004, 0xd03c, + 0xd004, 0xd004, + 0xd010, 0xd03c, 0xdfc0, 0xdfe0, 0xe000, 0xea7c, - 0xf000, 0x11110, - 0x11118, 0x11190, + 0xf000, 0x11190, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e4, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, 0x19238, 0x1924c, - 0x193f8, 0x19474, - 0x19490, 0x194f8, - 0x19800, 0x19f4c, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x193f8, 0x1943c, + 0x1944c, 0x19474, + 0x19490, 0x194e0, + 0x194f0, 0x194f8, + 0x19800, 0x19c08, + 0x19c10, 0x19c90, + 0x19ca0, 0x19ce4, + 0x19cf0, 0x19d40, + 0x19d50, 0x19d94, + 0x19da0, 0x19de8, + 0x19df0, 0x19e40, + 0x19e50, 0x19e90, + 0x19ea0, 0x19f4c, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f4, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e040, 0x1e04c, @@ -823,9 +899,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1ffc0, 0x1ffc8, 0x20000, 0x2002c, 0x20100, 0x2013c, - 0x20190, 0x201c8, + 0x20190, 0x201a0, + 0x201a8, 0x201b8, + 0x201c4, 0x201c8, 0x20200, 0x20318, - 0x20400, 0x20528, + 0x20400, 0x204b4, + 0x204c0, 0x20528, 0x20540, 0x20614, 0x21000, 0x21040, 0x2104c, 0x21060, @@ -834,22 +913,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x21270, 0x21284, 0x212fc, 0x21388, 0x21400, 0x21404, - 0x21500, 0x21518, - 0x2152c, 0x2153c, + 0x21500, 0x21500, + 0x21510, 0x21518, + 0x2152c, 0x21530, + 0x2153c, 0x2153c, 0x21550, 0x21554, 0x21600, 0x21600, - 0x21608, 0x21628, - 0x21630, 0x2163c, + 0x21608, 0x2161c, + 0x21624, 0x21628, + 0x21630, 0x21634, + 0x2163c, 0x2163c, 0x21700, 0x2171c, 0x21780, 0x2178c, - 0x21800, 0x21c38, - 0x21c80, 0x21d7c, + 0x21800, 0x21818, + 0x21820, 0x21828, + 0x21830, 0x21848, + 0x21850, 0x21854, + 0x21860, 0x21868, + 0x21870, 0x21870, + 0x21878, 0x21898, + 0x218a0, 0x218a8, + 0x218b0, 0x218c8, + 0x218d0, 0x218d4, + 0x218e0, 0x218e8, + 0x218f0, 0x218f0, + 0x218f8, 0x21a18, + 0x21a20, 0x21a28, + 0x21a30, 0x21a48, + 0x21a50, 0x21a54, + 0x21a60, 0x21a68, + 0x21a70, 0x21a70, + 0x21a78, 0x21a98, + 0x21aa0, 0x21aa8, + 0x21ab0, 0x21ac8, + 0x21ad0, 0x21ad4, + 0x21ae0, 0x21ae8, + 0x21af0, 0x21af0, + 0x21af8, 0x21c18, + 0x21c20, 0x21c20, + 0x21c28, 0x21c30, + 0x21c38, 0x21c38, + 0x21c80, 0x21c98, + 0x21ca0, 0x21ca8, + 0x21cb0, 0x21cc8, + 0x21cd0, 0x21cd4, + 0x21ce0, 0x21ce8, + 0x21cf0, 0x21cf0, + 0x21cf8, 0x21d7c, 0x21e00, 0x21e04, 0x22000, 0x2202c, 0x22100, 0x2213c, - 0x22190, 0x221c8, + 0x22190, 0x221a0, + 0x221a8, 0x221b8, + 0x221c4, 0x221c8, 0x22200, 0x22318, - 0x22400, 0x22528, + 0x22400, 0x224b4, + 0x224c0, 0x22528, 0x22540, 0x22614, 0x23000, 0x23040, 0x2304c, 0x23060, @@ -858,22 +977,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x23270, 0x23284, 0x232fc, 0x23388, 0x23400, 0x23404, - 0x23500, 0x23518, - 0x2352c, 0x2353c, + 0x23500, 0x23500, + 0x23510, 0x23518, + 0x2352c, 0x23530, + 0x2353c, 0x2353c, 0x23550, 0x23554, 0x23600, 0x23600, - 0x23608, 0x23628, - 0x23630, 0x2363c, + 0x23608, 0x2361c, + 0x23624, 0x23628, + 0x23630, 0x23634, + 0x2363c, 0x2363c, 0x23700, 0x2371c, 0x23780, 0x2378c, - 0x23800, 0x23c38, - 0x23c80, 0x23d7c, + 0x23800, 0x23818, + 0x23820, 0x23828, + 0x23830, 0x23848, + 0x23850, 0x23854, + 0x23860, 0x23868, + 0x23870, 0x23870, + 0x23878, 0x23898, + 0x238a0, 0x238a8, + 0x238b0, 0x238c8, + 0x238d0, 0x238d4, + 0x238e0, 0x238e8, + 0x238f0, 0x238f0, + 0x238f8, 0x23a18, + 0x23a20, 0x23a28, + 0x23a30, 0x23a48, + 0x23a50, 0x23a54, + 0x23a60, 0x23a68, + 0x23a70, 0x23a70, + 0x23a78, 0x23a98, + 0x23aa0, 0x23aa8, + 0x23ab0, 0x23ac8, + 0x23ad0, 0x23ad4, + 0x23ae0, 0x23ae8, + 0x23af0, 0x23af0, + 0x23af8, 0x23c18, + 0x23c20, 0x23c20, + 0x23c28, 0x23c30, + 0x23c38, 0x23c38, + 0x23c80, 0x23c98, + 0x23ca0, 0x23ca8, + 0x23cb0, 0x23cc8, + 0x23cd0, 0x23cd4, + 0x23ce0, 0x23ce8, + 0x23cf0, 0x23cf0, + 0x23cf8, 0x23d7c, 0x23e00, 0x23e04, 0x24000, 0x2402c, 0x24100, 0x2413c, - 0x24190, 0x241c8, + 0x24190, 0x241a0, + 0x241a8, 0x241b8, + 0x241c4, 0x241c8, 0x24200, 0x24318, - 0x24400, 0x24528, + 0x24400, 0x244b4, + 0x244c0, 0x24528, 0x24540, 0x24614, 0x25000, 0x25040, 0x2504c, 0x25060, @@ -882,22 +1041,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x25270, 0x25284, 0x252fc, 0x25388, 0x25400, 0x25404, - 0x25500, 0x25518, - 0x2552c, 0x2553c, + 0x25500, 0x25500, + 0x25510, 0x25518, + 0x2552c, 0x25530, + 0x2553c, 0x2553c, 0x25550, 0x25554, 0x25600, 0x25600, - 0x25608, 0x25628, - 0x25630, 0x2563c, + 0x25608, 0x2561c, + 0x25624, 0x25628, + 0x25630, 0x25634, + 0x2563c, 0x2563c, 0x25700, 0x2571c, 0x25780, 0x2578c, - 0x25800, 0x25c38, - 0x25c80, 0x25d7c, + 0x25800, 0x25818, + 0x25820, 0x25828, + 0x25830, 0x25848, + 0x25850, 0x25854, + 0x25860, 0x25868, + 0x25870, 0x25870, + 0x25878, 0x25898, + 0x258a0, 0x258a8, + 0x258b0, 0x258c8, + 0x258d0, 0x258d4, + 0x258e0, 0x258e8, + 0x258f0, 0x258f0, + 0x258f8, 0x25a18, + 0x25a20, 0x25a28, + 0x25a30, 0x25a48, + 0x25a50, 0x25a54, + 0x25a60, 0x25a68, + 0x25a70, 0x25a70, + 0x25a78, 0x25a98, + 0x25aa0, 0x25aa8, + 0x25ab0, 0x25ac8, + 0x25ad0, 0x25ad4, + 0x25ae0, 0x25ae8, + 0x25af0, 0x25af0, + 0x25af8, 0x25c18, + 0x25c20, 0x25c20, + 0x25c28, 0x25c30, + 0x25c38, 0x25c38, + 0x25c80, 0x25c98, + 0x25ca0, 0x25ca8, + 0x25cb0, 0x25cc8, + 0x25cd0, 0x25cd4, + 0x25ce0, 0x25ce8, + 0x25cf0, 0x25cf0, + 0x25cf8, 0x25d7c, 0x25e00, 0x25e04, 0x26000, 0x2602c, 0x26100, 0x2613c, - 0x26190, 0x261c8, + 0x26190, 0x261a0, + 0x261a8, 0x261b8, + 0x261c4, 0x261c8, 0x26200, 0x26318, - 0x26400, 0x26528, + 0x26400, 0x264b4, + 0x264c0, 0x26528, 0x26540, 0x26614, 0x27000, 0x27040, 0x2704c, 0x27060, @@ -906,51 +1105,120 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x27270, 0x27284, 0x272fc, 0x27388, 0x27400, 0x27404, - 0x27500, 0x27518, - 0x2752c, 0x2753c, + 0x27500, 0x27500, + 0x27510, 0x27518, + 0x2752c, 0x27530, + 0x2753c, 0x2753c, 0x27550, 0x27554, 0x27600, 0x27600, - 0x27608, 0x27628, - 0x27630, 0x2763c, + 0x27608, 0x2761c, + 0x27624, 0x27628, + 0x27630, 0x27634, + 0x2763c, 0x2763c, 0x27700, 0x2771c, 0x27780, 0x2778c, - 0x27800, 0x27c38, - 0x27c80, 0x27d7c, + 0x27800, 0x27818, + 0x27820, 0x27828, + 0x27830, 0x27848, + 0x27850, 0x27854, + 0x27860, 0x27868, + 0x27870, 0x27870, + 0x27878, 0x27898, + 0x278a0, 0x278a8, + 0x278b0, 0x278c8, + 0x278d0, 0x278d4, + 0x278e0, 0x278e8, + 0x278f0, 0x278f0, + 0x278f8, 0x27a18, + 0x27a20, 0x27a28, + 0x27a30, 0x27a48, + 0x27a50, 0x27a54, + 0x27a60, 0x27a68, + 0x27a70, 0x27a70, + 0x27a78, 0x27a98, + 0x27aa0, 0x27aa8, + 0x27ab0, 0x27ac8, + 0x27ad0, 0x27ad4, + 0x27ae0, 0x27ae8, + 0x27af0, 0x27af0, + 0x27af8, 0x27c18, + 0x27c20, 0x27c20, + 0x27c28, 0x27c30, + 0x27c38, 0x27c38, + 0x27c80, 0x27c98, + 0x27ca0, 0x27ca8, + 0x27cb0, 0x27cc8, + 0x27cd0, 0x27cd4, + 0x27ce0, 0x27ce8, + 0x27cf0, 0x27cf0, + 0x27cf8, 0x27d7c, 0x27e00, 0x27e04, }; static const unsigned int t5_reg_ranges[] = { - 0x1008, 0x1148, - 0x1180, 0x11b4, + 0x1008, 0x10c0, + 0x10cc, 0x10f8, + 0x1100, 0x1100, + 0x110c, 0x1148, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x123c, 0x1280, 0x173c, 0x1800, 0x18fc, 0x3000, 0x3028, - 0x3068, 0x30d8, + 0x3060, 0x30b0, + 0x30b8, 0x30d8, 0x30e0, 0x30fc, 0x3140, 0x357c, 0x35a8, 0x35cc, 0x35ec, 0x35ec, 0x3600, 0x5624, - 0x56cc, 0x575c, + 0x56cc, 0x56ec, + 0x56f4, 0x5720, + 0x5728, 0x575c, 0x580c, 0x5814, - 0x5890, 0x58bc, - 0x5940, 0x59dc, + 0x5890, 0x589c, + 0x58a4, 0x58ac, + 0x58b8, 0x58bc, + 0x5940, 0x59c8, + 0x59d0, 0x59dc, 0x59fc, 0x5a18, - 0x5a60, 0x5a9c, + 0x5a60, 0x5a70, + 0x5a80, 0x5a9c, 0x5b94, 0x5bfc, - 0x6000, 0x6040, - 0x6058, 0x614c, + 0x6000, 0x6020, + 0x6028, 0x6040, + 0x6058, 0x609c, + 0x60a8, 0x614c, 0x7700, 0x7798, 0x77c0, 0x78fc, - 0x7b00, 0x7c54, - 0x7d00, 0x7efc, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c54, + 0x7d00, 0x7d38, + 0x7d40, 0x7d80, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7edc, + 0x7ee8, 0x7efc, 0x8dc0, 0x8de0, - 0x8df8, 0x8e84, + 0x8df8, 0x8e04, + 0x8e10, 0x8e84, 0x8ea0, 0x8f84, - 0x8fc0, 0x90f8, - 0x9400, 0x9470, - 0x9600, 0x96f4, + 0x8fc0, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x90f8, + 0x9400, 0x9408, + 0x9410, 0x9470, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x96f4, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -962,103 +1230,143 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9e80, 0x9eec, 0x9f00, 0x9f6c, 0x9f80, 0xa020, - 0xd004, 0xd03c, + 0xd004, 0xd004, + 0xd010, 0xd03c, 0xdfc0, 0xdfe0, - 0xe000, 0x11088, - 0x1109c, 0x11110, - 0x11118, 0x1117c, + 0xe000, 0x1106c, + 0x11074, 0x11088, + 0x1109c, 0x1117c, 0x11190, 0x11204, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e8, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, 0x19238, 0x19290, - 0x193f8, 0x19474, + 0x193f8, 0x19428, + 0x19430, 0x19444, + 0x1944c, 0x1946c, + 0x19474, 0x19474, 0x19490, 0x194cc, 0x194f0, 0x194f8, - 0x19c00, 0x19c60, - 0x19c94, 0x19e10, - 0x19e50, 0x19f34, + 0x19c00, 0x19c08, + 0x19c10, 0x19c60, + 0x19c94, 0x19ce4, + 0x19cf0, 0x19d40, + 0x19d50, 0x19d94, + 0x19da0, 0x19de8, + 0x19df0, 0x19e10, + 0x19e50, 0x19e90, + 0x19ea0, 0x19f24, + 0x19f34, 0x19f34, 0x19f40, 0x19f50, - 0x19f90, 0x19fe4, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x19f90, 0x19fb4, + 0x19fc4, 0x19fe4, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f8, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e008, 0x1e00c, - 0x1e040, 0x1e04c, + 0x1e040, 0x1e044, + 0x1e04c, 0x1e04c, 0x1e284, 0x1e290, 0x1e2c0, 0x1e2c0, 0x1e2e0, 0x1e2e0, 0x1e300, 0x1e384, 0x1e3c0, 0x1e3c8, 0x1e408, 0x1e40c, - 0x1e440, 0x1e44c, + 0x1e440, 0x1e444, + 0x1e44c, 0x1e44c, 0x1e684, 0x1e690, 0x1e6c0, 0x1e6c0, 0x1e6e0, 0x1e6e0, 0x1e700, 0x1e784, 0x1e7c0, 0x1e7c8, 0x1e808, 0x1e80c, - 0x1e840, 0x1e84c, + 0x1e840, 0x1e844, + 0x1e84c, 0x1e84c, 0x1ea84, 0x1ea90, 0x1eac0, 0x1eac0, 0x1eae0, 0x1eae0, 0x1eb00, 0x1eb84, 0x1ebc0, 0x1ebc8, 0x1ec08, 0x1ec0c, - 0x1ec40, 0x1ec4c, + 0x1ec40, 0x1ec44, + 0x1ec4c, 0x1ec4c, 0x1ee84, 0x1ee90, 0x1eec0, 0x1eec0, 0x1eee0, 0x1eee0, 0x1ef00, 0x1ef84, 0x1efc0, 0x1efc8, 0x1f008, 0x1f00c, - 0x1f040, 0x1f04c, + 0x1f040, 0x1f044, + 0x1f04c, 0x1f04c, 0x1f284, 0x1f290, 0x1f2c0, 0x1f2c0, 0x1f2e0, 0x1f2e0, 0x1f300, 0x1f384, 0x1f3c0, 0x1f3c8, 0x1f408, 0x1f40c, - 0x1f440, 0x1f44c, + 0x1f440, 0x1f444, + 0x1f44c, 0x1f44c, 0x1f684, 0x1f690, 0x1f6c0, 0x1f6c0, 0x1f6e0, 0x1f6e0, 0x1f700, 0x1f784, 0x1f7c0, 0x1f7c8, 0x1f808, 0x1f80c, - 0x1f840, 0x1f84c, + 0x1f840, 0x1f844, + 0x1f84c, 0x1f84c, 0x1fa84, 0x1fa90, 0x1fac0, 0x1fac0, 0x1fae0, 0x1fae0, 0x1fb00, 0x1fb84, 0x1fbc0, 0x1fbc8, 0x1fc08, 0x1fc0c, - 0x1fc40, 0x1fc4c, + 0x1fc40, 0x1fc44, + 0x1fc4c, 0x1fc4c, 0x1fe84, 0x1fe90, 0x1fec0, 0x1fec0, 0x1fee0, 0x1fee0, 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x30000, 0x30030, + 0x30038, 0x30038, + 0x30040, 0x30040, 0x30100, 0x30144, - 0x30190, 0x301d0, + 0x30190, 0x301a0, + 0x301a8, 0x301b8, + 0x301c4, 0x301c8, + 0x301d0, 0x301d0, 0x30200, 0x30318, - 0x30400, 0x3052c, + 0x30400, 0x304b4, + 0x304c0, 0x3052c, 0x30540, 0x3061c, - 0x30800, 0x30834, + 0x30800, 0x30828, + 0x30834, 0x30834, 0x308c0, 0x30908, 0x30910, 0x309ac, - 0x30a00, 0x30a2c, + 0x30a00, 0x30a14, + 0x30a1c, 0x30a2c, 0x30a44, 0x30a50, - 0x30a74, 0x30c24, + 0x30a74, 0x30a74, + 0x30a7c, 0x30afc, + 0x30b08, 0x30c24, 0x30d00, 0x30d00, 0x30d08, 0x30d14, 0x30d1c, 0x30d20, - 0x30d3c, 0x30d50, + 0x30d3c, 0x30d3c, + 0x30d48, 0x30d50, 0x31200, 0x3120c, 0x31220, 0x31220, 0x31240, 0x31240, @@ -1078,27 +1386,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x322c8, 0x322fc, 0x32600, 0x32630, 0x32a00, 0x32abc, - 0x32b00, 0x32b70, - 0x33000, 0x33048, - 0x33060, 0x3309c, - 0x330f0, 0x33148, - 0x33160, 0x3319c, - 0x331f0, 0x332e4, - 0x332f8, 0x333e4, - 0x333f8, 0x33448, - 0x33460, 0x3349c, - 0x334f0, 0x33548, - 0x33560, 0x3359c, - 0x335f0, 0x336e4, - 0x336f8, 0x337e4, + 0x32b00, 0x32b10, + 0x32b20, 0x32b30, + 0x32b40, 0x32b50, + 0x32b60, 0x32b70, + 0x33000, 0x33028, + 0x33030, 0x33048, + 0x33060, 0x33068, + 0x33070, 0x3309c, + 0x330f0, 0x33128, + 0x33130, 0x33148, + 0x33160, 0x33168, + 0x33170, 0x3319c, + 0x331f0, 0x33238, + 0x33240, 0x33240, + 0x33248, 0x33250, + 0x3325c, 0x33264, + 0x33270, 0x332b8, + 0x332c0, 0x332e4, + 0x332f8, 0x33338, + 0x33340, 0x33340, + 0x33348, 0x33350, + 0x3335c, 0x33364, + 0x33370, 0x333b8, + 0x333c0, 0x333e4, + 0x333f8, 0x33428, + 0x33430, 0x33448, + 0x33460, 0x33468, + 0x33470, 0x3349c, + 0x334f0, 0x33528, + 0x33530, 0x33548, + 0x33560, 0x33568, + 0x33570, 0x3359c, + 0x335f0, 0x33638, + 0x33640, 0x33640, + 0x33648, 0x33650, + 0x3365c, 0x33664, + 0x33670, 0x336b8, + 0x336c0, 0x336e4, + 0x336f8, 0x33738, + 0x33740, 0x33740, + 0x33748, 0x33750, + 0x3375c, 0x33764, + 0x33770, 0x337b8, + 0x337c0, 0x337e4, 0x337f8, 0x337fc, 0x33814, 0x33814, 0x3382c, 0x3382c, 0x33880, 0x3388c, 0x338e8, 0x338ec, - 0x33900, 0x33948, - 0x33960, 0x3399c, - 0x339f0, 0x33ae4, + 0x33900, 0x33928, + 0x33930, 0x33948, + 0x33960, 0x33968, + 0x33970, 0x3399c, + 0x339f0, 0x33a38, + 0x33a40, 0x33a40, + 0x33a48, 0x33a50, + 0x33a5c, 0x33a64, + 0x33a70, 0x33ab8, + 0x33ac0, 0x33ae4, 0x33af8, 0x33b10, 0x33b28, 0x33b28, 0x33b3c, 0x33b50, @@ -1107,21 +1453,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x33c3c, 0x33c50, 0x33cf0, 0x33cfc, 0x34000, 0x34030, + 0x34038, 0x34038, + 0x34040, 0x34040, 0x34100, 0x34144, - 0x34190, 0x341d0, + 0x34190, 0x341a0, + 0x341a8, 0x341b8, + 0x341c4, 0x341c8, + 0x341d0, 0x341d0, 0x34200, 0x34318, - 0x34400, 0x3452c, + 0x34400, 0x344b4, + 0x344c0, 0x3452c, 0x34540, 0x3461c, - 0x34800, 0x34834, + 0x34800, 0x34828, + 0x34834, 0x34834, 0x348c0, 0x34908, 0x34910, 0x349ac, - 0x34a00, 0x34a2c, + 0x34a00, 0x34a14, + 0x34a1c, 0x34a2c, 0x34a44, 0x34a50, - 0x34a74, 0x34c24, + 0x34a74, 0x34a74, + 0x34a7c, 0x34afc, + 0x34b08, 0x34c24, 0x34d00, 0x34d00, 0x34d08, 0x34d14, 0x34d1c, 0x34d20, - 0x34d3c, 0x34d50, + 0x34d3c, 0x34d3c, + 0x34d48, 0x34d50, 0x35200, 0x3520c, 0x35220, 0x35220, 0x35240, 0x35240, @@ -1141,27 +1498,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x362c8, 0x362fc, 0x36600, 0x36630, 0x36a00, 0x36abc, - 0x36b00, 0x36b70, - 0x37000, 0x37048, - 0x37060, 0x3709c, - 0x370f0, 0x37148, - 0x37160, 0x3719c, - 0x371f0, 0x372e4, - 0x372f8, 0x373e4, - 0x373f8, 0x37448, - 0x37460, 0x3749c, - 0x374f0, 0x37548, - 0x37560, 0x3759c, - 0x375f0, 0x376e4, - 0x376f8, 0x377e4, + 0x36b00, 0x36b10, + 0x36b20, 0x36b30, + 0x36b40, 0x36b50, + 0x36b60, 0x36b70, + 0x37000, 0x37028, + 0x37030, 0x37048, + 0x37060, 0x37068, + 0x37070, 0x3709c, + 0x370f0, 0x37128, + 0x37130, 0x37148, + 0x37160, 0x37168, + 0x37170, 0x3719c, + 0x371f0, 0x37238, + 0x37240, 0x37240, + 0x37248, 0x37250, + 0x3725c, 0x37264, + 0x37270, 0x372b8, + 0x372c0, 0x372e4, + 0x372f8, 0x37338, + 0x37340, 0x37340, + 0x37348, 0x37350, + 0x3735c, 0x37364, + 0x37370, 0x373b8, + 0x373c0, 0x373e4, + 0x373f8, 0x37428, + 0x37430, 0x37448, + 0x37460, 0x37468, + 0x37470, 0x3749c, + 0x374f0, 0x37528, + 0x37530, 0x37548, + 0x37560, 0x37568, + 0x37570, 0x3759c, + 0x375f0, 0x37638, + 0x37640, 0x37640, + 0x37648, 0x37650, + 0x3765c, 0x37664, + 0x37670, 0x376b8, + 0x376c0, 0x376e4, + 0x376f8, 0x37738, + 0x37740, 0x37740, + 0x37748, 0x37750, + 0x3775c, 0x37764, + 0x37770, 0x377b8, + 0x377c0, 0x377e4, 0x377f8, 0x377fc, 0x37814, 0x37814, 0x3782c, 0x3782c, 0x37880, 0x3788c, 0x378e8, 0x378ec, - 0x37900, 0x37948, - 0x37960, 0x3799c, - 0x379f0, 0x37ae4, + 0x37900, 0x37928, + 0x37930, 0x37948, + 0x37960, 0x37968, + 0x37970, 0x3799c, + 0x379f0, 0x37a38, + 0x37a40, 0x37a40, + 0x37a48, 0x37a50, + 0x37a5c, 0x37a64, + 0x37a70, 0x37ab8, + 0x37ac0, 0x37ae4, 0x37af8, 0x37b10, 0x37b28, 0x37b28, 0x37b3c, 0x37b50, @@ -1170,21 +1565,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x37c3c, 0x37c50, 0x37cf0, 0x37cfc, 0x38000, 0x38030, + 0x38038, 0x38038, + 0x38040, 0x38040, 0x38100, 0x38144, - 0x38190, 0x381d0, + 0x38190, 0x381a0, + 0x381a8, 0x381b8, + 0x381c4, 0x381c8, + 0x381d0, 0x381d0, 0x38200, 0x38318, - 0x38400, 0x3852c, + 0x38400, 0x384b4, + 0x384c0, 0x3852c, 0x38540, 0x3861c, - 0x38800, 0x38834, + 0x38800, 0x38828, + 0x38834, 0x38834, 0x388c0, 0x38908, 0x38910, 0x389ac, - 0x38a00, 0x38a2c, + 0x38a00, 0x38a14, + 0x38a1c, 0x38a2c, 0x38a44, 0x38a50, - 0x38a74, 0x38c24, + 0x38a74, 0x38a74, + 0x38a7c, 0x38afc, + 0x38b08, 0x38c24, 0x38d00, 0x38d00, 0x38d08, 0x38d14, 0x38d1c, 0x38d20, - 0x38d3c, 0x38d50, + 0x38d3c, 0x38d3c, + 0x38d48, 0x38d50, 0x39200, 0x3920c, 0x39220, 0x39220, 0x39240, 0x39240, @@ -1204,27 +1610,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3a2c8, 0x3a2fc, 0x3a600, 0x3a630, 0x3aa00, 0x3aabc, - 0x3ab00, 0x3ab70, - 0x3b000, 0x3b048, - 0x3b060, 0x3b09c, - 0x3b0f0, 0x3b148, - 0x3b160, 0x3b19c, - 0x3b1f0, 0x3b2e4, - 0x3b2f8, 0x3b3e4, - 0x3b3f8, 0x3b448, - 0x3b460, 0x3b49c, - 0x3b4f0, 0x3b548, - 0x3b560, 0x3b59c, - 0x3b5f0, 0x3b6e4, - 0x3b6f8, 0x3b7e4, + 0x3ab00, 0x3ab10, + 0x3ab20, 0x3ab30, + 0x3ab40, 0x3ab50, + 0x3ab60, 0x3ab70, + 0x3b000, 0x3b028, + 0x3b030, 0x3b048, + 0x3b060, 0x3b068, + 0x3b070, 0x3b09c, + 0x3b0f0, 0x3b128, + 0x3b130, 0x3b148, + 0x3b160, 0x3b168, + 0x3b170, 0x3b19c, + 0x3b1f0, 0x3b238, + 0x3b240, 0x3b240, + 0x3b248, 0x3b250, + 0x3b25c, 0x3b264, + 0x3b270, 0x3b2b8, + 0x3b2c0, 0x3b2e4, + 0x3b2f8, 0x3b338, + 0x3b340, 0x3b340, + 0x3b348, 0x3b350, + 0x3b35c, 0x3b364, + 0x3b370, 0x3b3b8, + 0x3b3c0, 0x3b3e4, + 0x3b3f8, 0x3b428, + 0x3b430, 0x3b448, + 0x3b460, 0x3b468, + 0x3b470, 0x3b49c, + 0x3b4f0, 0x3b528, + 0x3b530, 0x3b548, + 0x3b560, 0x3b568, + 0x3b570, 0x3b59c, + 0x3b5f0, 0x3b638, + 0x3b640, 0x3b640, + 0x3b648, 0x3b650, + 0x3b65c, 0x3b664, + 0x3b670, 0x3b6b8, + 0x3b6c0, 0x3b6e4, + 0x3b6f8, 0x3b738, + 0x3b740, 0x3b740, + 0x3b748, 0x3b750, + 0x3b75c, 0x3b764, + 0x3b770, 0x3b7b8, + 0x3b7c0, 0x3b7e4, 0x3b7f8, 0x3b7fc, 0x3b814, 0x3b814, 0x3b82c, 0x3b82c, 0x3b880, 0x3b88c, 0x3b8e8, 0x3b8ec, - 0x3b900, 0x3b948, - 0x3b960, 0x3b99c, - 0x3b9f0, 0x3bae4, + 0x3b900, 0x3b928, + 0x3b930, 0x3b948, + 0x3b960, 0x3b968, + 0x3b970, 0x3b99c, + 0x3b9f0, 0x3ba38, + 0x3ba40, 0x3ba40, + 0x3ba48, 0x3ba50, + 0x3ba5c, 0x3ba64, + 0x3ba70, 0x3bab8, + 0x3bac0, 0x3bae4, 0x3baf8, 0x3bb10, 0x3bb28, 0x3bb28, 0x3bb3c, 0x3bb50, @@ -1233,21 +1677,32 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3bc3c, 0x3bc50, 0x3bcf0, 0x3bcfc, 0x3c000, 0x3c030, + 0x3c038, 0x3c038, + 0x3c040, 0x3c040, 0x3c100, 0x3c144, - 0x3c190, 0x3c1d0, + 0x3c190, 0x3c1a0, + 0x3c1a8, 0x3c1b8, + 0x3c1c4, 0x3c1c8, + 0x3c1d0, 0x3c1d0, 0x3c200, 0x3c318, - 0x3c400, 0x3c52c, + 0x3c400, 0x3c4b4, + 0x3c4c0, 0x3c52c, 0x3c540, 0x3c61c, - 0x3c800, 0x3c834, + 0x3c800, 0x3c828, + 0x3c834, 0x3c834, 0x3c8c0, 0x3c908, 0x3c910, 0x3c9ac, - 0x3ca00, 0x3ca2c, + 0x3ca00, 0x3ca14, + 0x3ca1c, 0x3ca2c, 0x3ca44, 0x3ca50, - 0x3ca74, 0x3cc24, + 0x3ca74, 0x3ca74, + 0x3ca7c, 0x3cafc, + 0x3cb08, 0x3cc24, 0x3cd00, 0x3cd00, 0x3cd08, 0x3cd14, 0x3cd1c, 0x3cd20, - 0x3cd3c, 0x3cd50, + 0x3cd3c, 0x3cd3c, + 0x3cd48, 0x3cd50, 0x3d200, 0x3d20c, 0x3d220, 0x3d220, 0x3d240, 0x3d240, @@ -1267,27 +1722,65 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3e2c8, 0x3e2fc, 0x3e600, 0x3e630, 0x3ea00, 0x3eabc, - 0x3eb00, 0x3eb70, - 0x3f000, 0x3f048, - 0x3f060, 0x3f09c, - 0x3f0f0, 0x3f148, - 0x3f160, 0x3f19c, - 0x3f1f0, 0x3f2e4, - 0x3f2f8, 0x3f3e4, - 0x3f3f8, 0x3f448, - 0x3f460, 0x3f49c, - 0x3f4f0, 0x3f548, - 0x3f560, 0x3f59c, - 0x3f5f0, 0x3f6e4, - 0x3f6f8, 0x3f7e4, + 0x3eb00, 0x3eb10, + 0x3eb20, 0x3eb30, + 0x3eb40, 0x3eb50, + 0x3eb60, 0x3eb70, + 0x3f000, 0x3f028, + 0x3f030, 0x3f048, + 0x3f060, 0x3f068, + 0x3f070, 0x3f09c, + 0x3f0f0, 0x3f128, + 0x3f130, 0x3f148, + 0x3f160, 0x3f168, + 0x3f170, 0x3f19c, + 0x3f1f0, 0x3f238, + 0x3f240, 0x3f240, + 0x3f248, 0x3f250, + 0x3f25c, 0x3f264, + 0x3f270, 0x3f2b8, + 0x3f2c0, 0x3f2e4, + 0x3f2f8, 0x3f338, + 0x3f340, 0x3f340, + 0x3f348, 0x3f350, + 0x3f35c, 0x3f364, + 0x3f370, 0x3f3b8, + 0x3f3c0, 0x3f3e4, + 0x3f3f8, 0x3f428, + 0x3f430, 0x3f448, + 0x3f460, 0x3f468, + 0x3f470, 0x3f49c, + 0x3f4f0, 0x3f528, + 0x3f530, 0x3f548, + 0x3f560, 0x3f568, + 0x3f570, 0x3f59c, + 0x3f5f0, 0x3f638, + 0x3f640, 0x3f640, + 0x3f648, 0x3f650, + 0x3f65c, 0x3f664, + 0x3f670, 0x3f6b8, + 0x3f6c0, 0x3f6e4, + 0x3f6f8, 0x3f738, + 0x3f740, 0x3f740, + 0x3f748, 0x3f750, + 0x3f75c, 0x3f764, + 0x3f770, 0x3f7b8, + 0x3f7c0, 0x3f7e4, 0x3f7f8, 0x3f7fc, 0x3f814, 0x3f814, 0x3f82c, 0x3f82c, 0x3f880, 0x3f88c, 0x3f8e8, 0x3f8ec, - 0x3f900, 0x3f948, - 0x3f960, 0x3f99c, - 0x3f9f0, 0x3fae4, + 0x3f900, 0x3f928, + 0x3f930, 0x3f948, + 0x3f960, 0x3f968, + 0x3f970, 0x3f99c, + 0x3f9f0, 0x3fa38, + 0x3fa40, 0x3fa40, + 0x3fa48, 0x3fa50, + 0x3fa5c, 0x3fa64, + 0x3fa70, 0x3fab8, + 0x3fac0, 0x3fae4, 0x3faf8, 0x3fb10, 0x3fb28, 0x3fb28, 0x3fb3c, 0x3fb50, @@ -1296,108 +1789,224 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3fc3c, 0x3fc50, 0x3fcf0, 0x3fcfc, 0x40000, 0x4000c, - 0x40040, 0x40068, - 0x4007c, 0x40144, + 0x40040, 0x40050, + 0x40060, 0x40068, + 0x4007c, 0x4008c, + 0x40094, 0x400b0, + 0x400c0, 0x40144, 0x40180, 0x4018c, - 0x40200, 0x40298, - 0x402ac, 0x4033c, + 0x40200, 0x40254, + 0x40260, 0x40264, + 0x40270, 0x40288, + 0x40290, 0x40298, + 0x402ac, 0x402c8, + 0x402d0, 0x402e0, + 0x402f0, 0x402f0, + 0x40300, 0x4033c, 0x403f8, 0x403fc, 0x41304, 0x413c4, - 0x41400, 0x4141c, + 0x41400, 0x4140c, + 0x41414, 0x4141c, 0x41480, 0x414d0, - 0x44000, 0x44078, - 0x440c0, 0x44278, - 0x442c0, 0x44478, - 0x444c0, 0x44678, - 0x446c0, 0x44878, - 0x448c0, 0x449fc, - 0x45000, 0x45068, + 0x44000, 0x44054, + 0x4405c, 0x44078, + 0x440c0, 0x44174, + 0x44180, 0x441ac, + 0x441b4, 0x441b8, + 0x441c0, 0x44254, + 0x4425c, 0x44278, + 0x442c0, 0x44374, + 0x44380, 0x443ac, + 0x443b4, 0x443b8, + 0x443c0, 0x44454, + 0x4445c, 0x44478, + 0x444c0, 0x44574, + 0x44580, 0x445ac, + 0x445b4, 0x445b8, + 0x445c0, 0x44654, + 0x4465c, 0x44678, + 0x446c0, 0x44774, + 0x44780, 0x447ac, + 0x447b4, 0x447b8, + 0x447c0, 0x44854, + 0x4485c, 0x44878, + 0x448c0, 0x44974, + 0x44980, 0x449ac, + 0x449b4, 0x449b8, + 0x449c0, 0x449fc, + 0x45000, 0x45004, + 0x45010, 0x45030, + 0x45040, 0x45060, + 0x45068, 0x45068, 0x45080, 0x45084, 0x450a0, 0x450b0, - 0x45200, 0x45268, + 0x45200, 0x45204, + 0x45210, 0x45230, + 0x45240, 0x45260, + 0x45268, 0x45268, 0x45280, 0x45284, 0x452a0, 0x452b0, 0x460c0, 0x460e4, - 0x47000, 0x4708c, + 0x47000, 0x4703c, + 0x47044, 0x4708c, 0x47200, 0x47250, - 0x47400, 0x47420, + 0x47400, 0x47408, + 0x47414, 0x47420, 0x47600, 0x47618, 0x47800, 0x47814, 0x48000, 0x4800c, - 0x48040, 0x48068, - 0x4807c, 0x48144, + 0x48040, 0x48050, + 0x48060, 0x48068, + 0x4807c, 0x4808c, + 0x48094, 0x480b0, + 0x480c0, 0x48144, 0x48180, 0x4818c, - 0x48200, 0x48298, - 0x482ac, 0x4833c, + 0x48200, 0x48254, + 0x48260, 0x48264, + 0x48270, 0x48288, + 0x48290, 0x48298, + 0x482ac, 0x482c8, + 0x482d0, 0x482e0, + 0x482f0, 0x482f0, + 0x48300, 0x4833c, 0x483f8, 0x483fc, 0x49304, 0x493c4, - 0x49400, 0x4941c, + 0x49400, 0x4940c, + 0x49414, 0x4941c, 0x49480, 0x494d0, - 0x4c000, 0x4c078, - 0x4c0c0, 0x4c278, - 0x4c2c0, 0x4c478, - 0x4c4c0, 0x4c678, - 0x4c6c0, 0x4c878, - 0x4c8c0, 0x4c9fc, - 0x4d000, 0x4d068, + 0x4c000, 0x4c054, + 0x4c05c, 0x4c078, + 0x4c0c0, 0x4c174, + 0x4c180, 0x4c1ac, + 0x4c1b4, 0x4c1b8, + 0x4c1c0, 0x4c254, + 0x4c25c, 0x4c278, + 0x4c2c0, 0x4c374, + 0x4c380, 0x4c3ac, + 0x4c3b4, 0x4c3b8, + 0x4c3c0, 0x4c454, + 0x4c45c, 0x4c478, + 0x4c4c0, 0x4c574, + 0x4c580, 0x4c5ac, + 0x4c5b4, 0x4c5b8, + 0x4c5c0, 0x4c654, + 0x4c65c, 0x4c678, + 0x4c6c0, 0x4c774, + 0x4c780, 0x4c7ac, + 0x4c7b4, 0x4c7b8, + 0x4c7c0, 0x4c854, + 0x4c85c, 0x4c878, + 0x4c8c0, 0x4c974, + 0x4c980, 0x4c9ac, + 0x4c9b4, 0x4c9b8, + 0x4c9c0, 0x4c9fc, + 0x4d000, 0x4d004, + 0x4d010, 0x4d030, + 0x4d040, 0x4d060, + 0x4d068, 0x4d068, 0x4d080, 0x4d084, 0x4d0a0, 0x4d0b0, - 0x4d200, 0x4d268, + 0x4d200, 0x4d204, + 0x4d210, 0x4d230, + 0x4d240, 0x4d260, + 0x4d268, 0x4d268, 0x4d280, 0x4d284, 0x4d2a0, 0x4d2b0, 0x4e0c0, 0x4e0e4, - 0x4f000, 0x4f08c, + 0x4f000, 0x4f03c, + 0x4f044, 0x4f08c, 0x4f200, 0x4f250, - 0x4f400, 0x4f420, + 0x4f400, 0x4f408, + 0x4f414, 0x4f420, 0x4f600, 0x4f618, 0x4f800, 0x4f814, - 0x50000, 0x500cc, + 0x50000, 0x50084, + 0x50090, 0x500cc, 0x50400, 0x50400, - 0x50800, 0x508cc, + 0x50800, 0x50884, + 0x50890, 0x508cc, 0x50c00, 0x50c00, 0x51000, 0x5101c, 0x51300, 0x51308, }; static const unsigned int t6_reg_ranges[] = { - 0x1008, 0x1124, - 0x1138, 0x114c, - 0x1180, 0x11b4, + 0x1008, 0x101c, + 0x1024, 0x10a8, + 0x10b4, 0x10f8, + 0x1100, 0x1114, + 0x111c, 0x112c, + 0x1138, 0x113c, + 0x1144, 0x114c, + 0x1180, 0x1184, + 0x1190, 0x1194, + 0x11a0, 0x11a4, + 0x11b0, 0x11b4, 0x11fc, 0x1254, 0x1280, 0x133c, 0x1800, 0x18fc, 0x3000, 0x302c, - 0x3060, 0x30d8, + 0x3060, 0x30b0, + 0x30b8, 0x30d8, 0x30e0, 0x30fc, 0x3140, 0x357c, 0x35a8, 0x35cc, 0x35ec, 0x35ec, 0x3600, 0x5624, - 0x56cc, 0x575c, + 0x56cc, 0x56ec, + 0x56f4, 0x5720, + 0x5728, 0x575c, 0x580c, 0x5814, - 0x5890, 0x58bc, + 0x5890, 0x589c, + 0x58a4, 0x58ac, + 0x58b8, 0x58bc, 0x5940, 0x595c, 0x5980, 0x598c, - 0x59b0, 0x59dc, + 0x59b0, 0x59c8, + 0x59d0, 0x59dc, 0x59fc, 0x5a18, 0x5a60, 0x5a6c, - 0x5a80, 0x5a9c, + 0x5a80, 0x5a8c, + 0x5a94, 0x5a9c, 0x5b94, 0x5bfc, - 0x5c10, 0x5ec0, + 0x5c10, 0x5e48, + 0x5e50, 0x5e94, + 0x5ea0, 0x5eb0, + 0x5ec0, 0x5ec0, 0x5ec8, 0x5ecc, - 0x6000, 0x6040, - 0x6058, 0x619c, + 0x6000, 0x6020, + 0x6028, 0x6040, + 0x6058, 0x609c, + 0x60a8, 0x619c, 0x7700, 0x7798, 0x77c0, 0x7880, 0x78cc, 0x78fc, - 0x7b00, 0x7c54, - 0x7d00, 0x7efc, + 0x7b00, 0x7b58, + 0x7b60, 0x7b84, + 0x7b8c, 0x7c54, + 0x7d00, 0x7d38, + 0x7d40, 0x7d84, + 0x7d8c, 0x7ddc, + 0x7de4, 0x7e04, + 0x7e10, 0x7e1c, + 0x7e24, 0x7e38, + 0x7e40, 0x7e44, + 0x7e4c, 0x7e78, + 0x7e80, 0x7edc, + 0x7ee8, 0x7efc, 0x8dc0, 0x8de4, - 0x8df8, 0x8e84, + 0x8df8, 0x8e04, + 0x8e10, 0x8e84, 0x8ea0, 0x8f88, - 0x8fb8, 0x9124, + 0x8fb8, 0x9058, + 0x9060, 0x9060, + 0x9068, 0x90f8, + 0x9100, 0x9124, 0x9400, 0x9470, - 0x9600, 0x971c, + 0x9600, 0x9600, + 0x9608, 0x9638, + 0x9640, 0x9704, + 0x9710, 0x971c, 0x9800, 0x9808, 0x9820, 0x983c, 0x9850, 0x9864, @@ -1411,109 +2020,170 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x9f80, 0xa020, 0xd004, 0xd03c, 0xd100, 0xd118, - 0xd200, 0xd31c, + 0xd200, 0xd214, + 0xd220, 0xd234, + 0xd240, 0xd254, + 0xd260, 0xd274, + 0xd280, 0xd294, + 0xd2a0, 0xd2b4, + 0xd2c0, 0xd2d4, + 0xd2e0, 0xd2f4, + 0xd300, 0xd31c, 0xdfc0, 0xdfe0, 0xe000, 0xf008, 0x11000, 0x11014, - 0x11048, 0x1117c, - 0x11190, 0x11270, + 0x11048, 0x1106c, + 0x11074, 0x11088, + 0x11098, 0x11120, + 0x1112c, 0x1117c, + 0x11190, 0x112e0, 0x11300, 0x1130c, 0x12000, 0x1206c, 0x19040, 0x1906c, 0x19078, 0x19080, - 0x1908c, 0x19124, - 0x19150, 0x191b0, + 0x1908c, 0x190e8, + 0x190f0, 0x190f8, + 0x19100, 0x19110, + 0x19120, 0x19124, + 0x19150, 0x19194, + 0x1919c, 0x191b0, 0x191d0, 0x191e8, - 0x19238, 0x192bc, - 0x193f8, 0x19474, + 0x19238, 0x192b0, + 0x192bc, 0x192bc, + 0x19348, 0x1934c, + 0x193f8, 0x19418, + 0x19420, 0x19428, + 0x19430, 0x19444, + 0x1944c, 0x1946c, + 0x19474, 0x19474, 0x19490, 0x194cc, 0x194f0, 0x194f8, - 0x19c00, 0x19c80, - 0x19c94, 0x19cbc, - 0x19ce4, 0x19d28, + 0x19c00, 0x19c48, + 0x19c50, 0x19c80, + 0x19c94, 0x19c98, + 0x19ca0, 0x19cbc, + 0x19ce4, 0x19ce4, + 0x19cf0, 0x19cf8, + 0x19d00, 0x19d28, 0x19d50, 0x19d78, - 0x19d94, 0x19dc8, + 0x19d94, 0x19d98, + 0x19da0, 0x19dc8, 0x19df0, 0x19e10, 0x19e50, 0x19e6c, - 0x19ea0, 0x19f34, + 0x19ea0, 0x19ebc, + 0x19ec4, 0x19ef4, + 0x19f04, 0x19f2c, + 0x19f34, 0x19f34, 0x19f40, 0x19f50, 0x19f90, 0x19fac, - 0x19fc4, 0x19fe4, - 0x1a000, 0x1a06c, - 0x1a0b0, 0x1a120, - 0x1a128, 0x1a138, + 0x19fc4, 0x19fc8, + 0x19fd0, 0x19fe4, + 0x1a000, 0x1a004, + 0x1a010, 0x1a06c, + 0x1a0b0, 0x1a0e4, + 0x1a0ec, 0x1a0f8, + 0x1a100, 0x1a108, + 0x1a114, 0x1a120, + 0x1a128, 0x1a130, + 0x1a138, 0x1a138, 0x1a190, 0x1a1c4, 0x1a1fc, 0x1a1fc, 0x1e008, 0x1e00c, - 0x1e040, 0x1e04c, + 0x1e040, 0x1e044, + 0x1e04c, 0x1e04c, 0x1e284, 0x1e290, 0x1e2c0, 0x1e2c0, 0x1e2e0, 0x1e2e0, 0x1e300, 0x1e384, 0x1e3c0, 0x1e3c8, 0x1e408, 0x1e40c, - 0x1e440, 0x1e44c, + 0x1e440, 0x1e444, + 0x1e44c, 0x1e44c, 0x1e684, 0x1e690, 0x1e6c0, 0x1e6c0, 0x1e6e0, 0x1e6e0, 0x1e700, 0x1e784, 0x1e7c0, 0x1e7c8, 0x1e808, 0x1e80c, - 0x1e840, 0x1e84c, + 0x1e840, 0x1e844, + 0x1e84c, 0x1e84c, 0x1ea84, 0x1ea90, 0x1eac0, 0x1eac0, 0x1eae0, 0x1eae0, 0x1eb00, 0x1eb84, 0x1ebc0, 0x1ebc8, 0x1ec08, 0x1ec0c, - 0x1ec40, 0x1ec4c, + 0x1ec40, 0x1ec44, + 0x1ec4c, 0x1ec4c, 0x1ee84, 0x1ee90, 0x1eec0, 0x1eec0, 0x1eee0, 0x1eee0, 0x1ef00, 0x1ef84, 0x1efc0, 0x1efc8, 0x1f008, 0x1f00c, - 0x1f040, 0x1f04c, + 0x1f040, 0x1f044, + 0x1f04c, 0x1f04c, 0x1f284, 0x1f290, 0x1f2c0, 0x1f2c0, 0x1f2e0, 0x1f2e0, 0x1f300, 0x1f384, 0x1f3c0, 0x1f3c8, 0x1f408, 0x1f40c, - 0x1f440, 0x1f44c, + 0x1f440, 0x1f444, + 0x1f44c, 0x1f44c, 0x1f684, 0x1f690, 0x1f6c0, 0x1f6c0, 0x1f6e0, 0x1f6e0, 0x1f700, 0x1f784, 0x1f7c0, 0x1f7c8, 0x1f808, 0x1f80c, - 0x1f840, 0x1f84c, + 0x1f840, 0x1f844, + 0x1f84c, 0x1f84c, 0x1fa84, 0x1fa90, 0x1fac0, 0x1fac0, 0x1fae0, 0x1fae0, 0x1fb00, 0x1fb84, 0x1fbc0, 0x1fbc8, 0x1fc08, 0x1fc0c, - 0x1fc40, 0x1fc4c, + 0x1fc40, 0x1fc44, + 0x1fc4c, 0x1fc4c, 0x1fe84, 0x1fe90, 0x1fec0, 0x1fec0, 0x1fee0, 0x1fee0, 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, - 0x30000, 0x30070, - 0x30100, 0x301d0, + 0x30000, 0x30030, + 0x30038, 0x30038, + 0x30040, 0x30040, + 0x30048, 0x30048, + 0x30050, 0x30050, + 0x3005c, 0x30060, + 0x30068, 0x30068, + 0x30070, 0x30070, + 0x30100, 0x30168, + 0x30190, 0x301a0, + 0x301a8, 0x301b8, + 0x301c4, 0x301c8, + 0x301d0, 0x301d0, 0x30200, 0x30320, - 0x30400, 0x3052c, + 0x30400, 0x304b4, + 0x304c0, 0x3052c, 0x30540, 0x3061c, - 0x30800, 0x30890, + 0x30800, 0x308a0, 0x308c0, 0x30908, 0x30910, 0x309b8, 0x30a00, 0x30a04, - 0x30a0c, 0x30a2c, + 0x30a0c, 0x30a14, + 0x30a1c, 0x30a2c, 0x30a44, 0x30a50, - 0x30a74, 0x30c24, - 0x30d00, 0x30d3c, - 0x30d44, 0x30d7c, + 0x30a74, 0x30a74, + 0x30a7c, 0x30afc, + 0x30b08, 0x30c24, + 0x30d00, 0x30d14, + 0x30d1c, 0x30d3c, + 0x30d44, 0x30d4c, + 0x30d54, 0x30d74, + 0x30d7c, 0x30d7c, 0x30de0, 0x30de0, 0x30e00, 0x30ed4, 0x30f00, 0x30fa4, @@ -1542,7 +2212,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x31bb0, 0x31bb4, 0x31bc8, 0x31bd4, 0x32140, 0x3218c, - 0x321f0, 0x32200, + 0x321f0, 0x321f4, + 0x32200, 0x32200, 0x32218, 0x32218, 0x32400, 0x32400, 0x32408, 0x3241c, @@ -1551,46 +2222,108 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x326a8, 0x326a8, 0x326ec, 0x326ec, 0x32a00, 0x32abc, - 0x32b00, 0x32b78, + 0x32b00, 0x32b38, + 0x32b40, 0x32b58, + 0x32b60, 0x32b78, 0x32c00, 0x32c00, 0x32c08, 0x32c3c, 0x32e00, 0x32e2c, 0x32f00, 0x32f2c, - 0x33000, 0x330ac, - 0x330c0, 0x331ac, - 0x331c0, 0x332c4, - 0x332e4, 0x333c4, - 0x333e4, 0x334ac, - 0x334c0, 0x335ac, - 0x335c0, 0x336c4, - 0x336e4, 0x337c4, + 0x33000, 0x3302c, + 0x33034, 0x33050, + 0x33058, 0x33058, + 0x33060, 0x3308c, + 0x3309c, 0x330ac, + 0x330c0, 0x330c0, + 0x330c8, 0x330d0, + 0x330d8, 0x330e0, + 0x330ec, 0x3312c, + 0x33134, 0x33150, + 0x33158, 0x33158, + 0x33160, 0x3318c, + 0x3319c, 0x331ac, + 0x331c0, 0x331c0, + 0x331c8, 0x331d0, + 0x331d8, 0x331e0, + 0x331ec, 0x33290, + 0x33298, 0x332c4, + 0x332e4, 0x33390, + 0x33398, 0x333c4, + 0x333e4, 0x3342c, + 0x33434, 0x33450, + 0x33458, 0x33458, + 0x33460, 0x3348c, + 0x3349c, 0x334ac, + 0x334c0, 0x334c0, + 0x334c8, 0x334d0, + 0x334d8, 0x334e0, + 0x334ec, 0x3352c, + 0x33534, 0x33550, + 0x33558, 0x33558, + 0x33560, 0x3358c, + 0x3359c, 0x335ac, + 0x335c0, 0x335c0, + 0x335c8, 0x335d0, + 0x335d8, 0x335e0, + 0x335ec, 0x33690, + 0x33698, 0x336c4, + 0x336e4, 0x33790, + 0x33798, 0x337c4, 0x337e4, 0x337fc, 0x33814, 0x33814, 0x33854, 0x33868, 0x33880, 0x3388c, 0x338c0, 0x338d0, 0x338e8, 0x338ec, - 0x33900, 0x339ac, - 0x339c0, 0x33ac4, + 0x33900, 0x3392c, + 0x33934, 0x33950, + 0x33958, 0x33958, + 0x33960, 0x3398c, + 0x3399c, 0x339ac, + 0x339c0, 0x339c0, + 0x339c8, 0x339d0, + 0x339d8, 0x339e0, + 0x339ec, 0x33a90, + 0x33a98, 0x33ac4, 0x33ae4, 0x33b10, - 0x33b24, 0x33b50, + 0x33b24, 0x33b28, + 0x33b38, 0x33b50, 0x33bf0, 0x33c10, - 0x33c24, 0x33c50, + 0x33c24, 0x33c28, + 0x33c38, 0x33c50, 0x33cf0, 0x33cfc, - 0x34000, 0x34070, - 0x34100, 0x341d0, + 0x34000, 0x34030, + 0x34038, 0x34038, + 0x34040, 0x34040, + 0x34048, 0x34048, + 0x34050, 0x34050, + 0x3405c, 0x34060, + 0x34068, 0x34068, + 0x34070, 0x34070, + 0x34100, 0x34168, + 0x34190, 0x341a0, + 0x341a8, 0x341b8, + 0x341c4, 0x341c8, + 0x341d0, 0x341d0, 0x34200, 0x34320, - 0x34400, 0x3452c, + 0x34400, 0x344b4, + 0x344c0, 0x3452c, 0x34540, 0x3461c, - 0x34800, 0x34890, + 0x34800, 0x348a0, 0x348c0, 0x34908, 0x34910, 0x349b8, 0x34a00, 0x34a04, - 0x34a0c, 0x34a2c, + 0x34a0c, 0x34a14, + 0x34a1c, 0x34a2c, 0x34a44, 0x34a50, - 0x34a74, 0x34c24, - 0x34d00, 0x34d3c, - 0x34d44, 0x34d7c, + 0x34a74, 0x34a74, + 0x34a7c, 0x34afc, + 0x34b08, 0x34c24, + 0x34d00, 0x34d14, + 0x34d1c, 0x34d3c, + 0x34d44, 0x34d4c, + 0x34d54, 0x34d74, + 0x34d7c, 0x34d7c, 0x34de0, 0x34de0, 0x34e00, 0x34ed4, 0x34f00, 0x34fa4, @@ -1619,7 +2352,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x35bb0, 0x35bb4, 0x35bc8, 0x35bd4, 0x36140, 0x3618c, - 0x361f0, 0x36200, + 0x361f0, 0x361f4, + 0x36200, 0x36200, 0x36218, 0x36218, 0x36400, 0x36400, 0x36408, 0x3641c, @@ -1628,31 +2362,75 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x366a8, 0x366a8, 0x366ec, 0x366ec, 0x36a00, 0x36abc, - 0x36b00, 0x36b78, + 0x36b00, 0x36b38, + 0x36b40, 0x36b58, + 0x36b60, 0x36b78, 0x36c00, 0x36c00, 0x36c08, 0x36c3c, 0x36e00, 0x36e2c, 0x36f00, 0x36f2c, - 0x37000, 0x370ac, - 0x370c0, 0x371ac, - 0x371c0, 0x372c4, - 0x372e4, 0x373c4, - 0x373e4, 0x374ac, - 0x374c0, 0x375ac, - 0x375c0, 0x376c4, - 0x376e4, 0x377c4, + 0x37000, 0x3702c, + 0x37034, 0x37050, + 0x37058, 0x37058, + 0x37060, 0x3708c, + 0x3709c, 0x370ac, + 0x370c0, 0x370c0, + 0x370c8, 0x370d0, + 0x370d8, 0x370e0, + 0x370ec, 0x3712c, + 0x37134, 0x37150, + 0x37158, 0x37158, + 0x37160, 0x3718c, + 0x3719c, 0x371ac, + 0x371c0, 0x371c0, + 0x371c8, 0x371d0, + 0x371d8, 0x371e0, + 0x371ec, 0x37290, + 0x37298, 0x372c4, + 0x372e4, 0x37390, + 0x37398, 0x373c4, + 0x373e4, 0x3742c, + 0x37434, 0x37450, + 0x37458, 0x37458, + 0x37460, 0x3748c, + 0x3749c, 0x374ac, + 0x374c0, 0x374c0, + 0x374c8, 0x374d0, + 0x374d8, 0x374e0, + 0x374ec, 0x3752c, + 0x37534, 0x37550, + 0x37558, 0x37558, + 0x37560, 0x3758c, + 0x3759c, 0x375ac, + 0x375c0, 0x375c0, + 0x375c8, 0x375d0, + 0x375d8, 0x375e0, + 0x375ec, 0x37690, + 0x37698, 0x376c4, + 0x376e4, 0x37790, + 0x37798, 0x377c4, 0x377e4, 0x377fc, 0x37814, 0x37814, 0x37854, 0x37868, 0x37880, 0x3788c, 0x378c0, 0x378d0, 0x378e8, 0x378ec, - 0x37900, 0x379ac, - 0x379c0, 0x37ac4, + 0x37900, 0x3792c, + 0x37934, 0x37950, + 0x37958, 0x37958, + 0x37960, 0x3798c, + 0x3799c, 0x379ac, + 0x379c0, 0x379c0, + 0x379c8, 0x379d0, + 0x379d8, 0x379e0, + 0x379ec, 0x37a90, + 0x37a98, 0x37ac4, 0x37ae4, 0x37b10, - 0x37b24, 0x37b50, + 0x37b24, 0x37b28, + 0x37b38, 0x37b50, 0x37bf0, 0x37c10, - 0x37c24, 0x37c50, + 0x37c24, 0x37c28, + 0x37c38, 0x37c50, 0x37cf0, 0x37cfc, 0x40040, 0x40040, 0x40080, 0x40084, @@ -1664,36 +2442,62 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x40280, 0x40280, 0x40304, 0x40304, 0x40330, 0x4033c, - 0x41304, 0x413dc, - 0x41400, 0x4141c, + 0x41304, 0x413c8, + 0x413d0, 0x413dc, + 0x413f0, 0x413f0, + 0x41400, 0x4140c, + 0x41414, 0x4141c, 0x41480, 0x414d0, 0x44000, 0x4407c, - 0x440c0, 0x4427c, - 0x442c0, 0x4447c, - 0x444c0, 0x4467c, - 0x446c0, 0x4487c, - 0x448c0, 0x44a7c, - 0x44ac0, 0x44c7c, - 0x44cc0, 0x44e7c, - 0x44ec0, 0x4507c, - 0x450c0, 0x451fc, - 0x45800, 0x45868, + 0x440c0, 0x441ac, + 0x441b4, 0x4427c, + 0x442c0, 0x443ac, + 0x443b4, 0x4447c, + 0x444c0, 0x445ac, + 0x445b4, 0x4467c, + 0x446c0, 0x447ac, + 0x447b4, 0x4487c, + 0x448c0, 0x449ac, + 0x449b4, 0x44a7c, + 0x44ac0, 0x44bac, + 0x44bb4, 0x44c7c, + 0x44cc0, 0x44dac, + 0x44db4, 0x44e7c, + 0x44ec0, 0x44fac, + 0x44fb4, 0x4507c, + 0x450c0, 0x451ac, + 0x451b4, 0x451fc, + 0x45800, 0x45804, + 0x45810, 0x45830, + 0x45840, 0x45860, + 0x45868, 0x45868, 0x45880, 0x45884, 0x458a0, 0x458b0, - 0x45a00, 0x45a68, + 0x45a00, 0x45a04, + 0x45a10, 0x45a30, + 0x45a40, 0x45a60, + 0x45a68, 0x45a68, 0x45a80, 0x45a84, 0x45aa0, 0x45ab0, 0x460c0, 0x460e4, - 0x47000, 0x4708c, + 0x47000, 0x4703c, + 0x47044, 0x4708c, 0x47200, 0x47250, - 0x47400, 0x47420, + 0x47400, 0x47408, + 0x47414, 0x47420, 0x47600, 0x47618, - 0x47800, 0x4782c, - 0x50000, 0x500cc, + 0x47800, 0x47814, + 0x47820, 0x4782c, + 0x50000, 0x50084, + 0x50090, 0x500cc, + 0x50300, 0x50384, 0x50400, 0x50400, - 0x50800, 0x508cc, + 0x50800, 0x50884, + 0x50890, 0x508cc, + 0x50b00, 0x50b84, 0x50c00, 0x50c00, - 0x51000, 0x510b0, + 0x51000, 0x51020, + 0x51028, 0x510b0, 0x51300, 0x51324, }; @@ -2177,11 +2981,15 @@ int t4_get_exprom_version(struct adapter *adap, u32 *vers) */ int t4_check_fw_version(struct adapter *adap) { - int ret, major, minor, micro; + int i, ret, major, minor, micro; int exp_major, exp_minor, exp_micro; unsigned int chip_version = CHELSIO_CHIP_VERSION(adap->params.chip); ret = t4_get_fw_version(adap, &adap->params.fw_vers); + /* Try multiple times before returning error */ + for (i = 0; (ret == -EBUSY || ret == -EAGAIN) && i < 3; i++) + ret = t4_get_fw_version(adap, &adap->params.fw_vers); + if (ret) return ret; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index 640369df8b3a..13708fde1668 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -263,4 +263,9 @@ enum { #undef FLASH_START #undef FLASH_MAX_SIZE +#define SGE_TIMESTAMP_S 0 +#define SGE_TIMESTAMP_M 0xfffffffffffffffULL +#define SGE_TIMESTAMP_V(x) ((__u64)(x) << SGE_TIMESTAMP_S) +#define SGE_TIMESTAMP_G(x) (((__u64)(x) >> SGE_TIMESTAMP_S) & SGE_TIMESTAMP_M) + #endif /* __T4_HW_H */ diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 8b53f7d4bebf..6401ba99457f 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -143,6 +143,7 @@ struct enic { struct vnic_dev *vdev; struct timer_list notify_timer; struct work_struct reset; + struct work_struct tx_hang_reset; struct work_struct change_mtu_work; struct msix_entry msix_entry[ENIC_INTR_MAX]; struct enic_msix_entry msix[ENIC_INTR_MAX]; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 3352d027ab89..0c22fd014378 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -178,13 +178,15 @@ static int enic_wq_service(struct vnic_dev *vdev, struct cq_desc *cq_desc, return 0; } -static void enic_log_q_error(struct enic *enic) +static bool enic_log_q_error(struct enic *enic) { unsigned int i; u32 error_status; + bool err = false; for (i = 0; i < enic->wq_count; i++) { error_status = vnic_wq_error_status(&enic->wq[i]); + err |= error_status; if (error_status) netdev_err(enic->netdev, "WQ[%d] error_status %d\n", i, error_status); @@ -192,10 +194,13 @@ static void enic_log_q_error(struct enic *enic) for (i = 0; i < enic->rq_count; i++) { error_status = vnic_rq_error_status(&enic->rq[i]); + err |= error_status; if (error_status) netdev_err(enic->netdev, "RQ[%d] error_status %d\n", i, error_status); } + + return err; } static void enic_msglvl_check(struct enic *enic) @@ -333,10 +338,9 @@ static irqreturn_t enic_isr_msix_err(int irq, void *data) vnic_intr_return_all_credits(&enic->intr[intr]); - enic_log_q_error(enic); - - /* schedule recovery from WQ/RQ error */ - schedule_work(&enic->reset); + if (enic_log_q_error(enic)) + /* schedule recovery from WQ/RQ error */ + schedule_work(&enic->reset); return IRQ_HANDLED; } @@ -804,7 +808,7 @@ static void enic_set_rx_mode(struct net_device *netdev) static void enic_tx_timeout(struct net_device *netdev) { struct enic *enic = netdev_priv(netdev); - schedule_work(&enic->reset); + schedule_work(&enic->tx_hang_reset); } static int enic_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) @@ -1924,6 +1928,19 @@ static int enic_dev_open(struct enic *enic) return err; } +static int enic_dev_soft_reset(struct enic *enic) +{ + int err; + + err = enic_dev_wait(enic->vdev, vnic_dev_soft_reset, + vnic_dev_soft_reset_done, 0); + if (err) + netdev_err(enic->netdev, "vNIC soft reset failed, err %d\n", + err); + + return err; +} + static int enic_dev_hang_reset(struct enic *enic) { int err; @@ -2060,6 +2077,26 @@ static void enic_reset(struct work_struct *work) rtnl_lock(); spin_lock(&enic->enic_api_lock); + enic_stop(enic->netdev); + enic_dev_soft_reset(enic); + enic_reset_addr_lists(enic); + enic_init_vnic_resources(enic); + enic_set_rss_nic_cfg(enic); + enic_dev_set_ig_vlan_rewrite_mode(enic); + enic_open(enic->netdev); + spin_unlock(&enic->enic_api_lock); + call_netdevice_notifiers(NETDEV_REBOOT, enic->netdev); + + rtnl_unlock(); +} + +static void enic_tx_hang_reset(struct work_struct *work) +{ + struct enic *enic = container_of(work, struct enic, tx_hang_reset); + + rtnl_lock(); + + spin_lock(&enic->enic_api_lock); enic_dev_hang_notify(enic); enic_stop(enic->netdev); enic_dev_hang_reset(enic); @@ -2583,6 +2620,7 @@ static int enic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) enic_set_rx_coal_setting(enic); INIT_WORK(&enic->reset, enic_reset); + INIT_WORK(&enic->tx_hang_reset, enic_tx_hang_reset); INIT_WORK(&enic->change_mtu_work, enic_change_mtu_work); for (i = 0; i < enic->wq_count; i++) diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index a3badefaf360..1ffd1050860b 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c @@ -659,14 +659,14 @@ int vnic_dev_open_done(struct vnic_dev *vdev, int *done) return 0; } -static int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg) { u64 a0 = (u32)arg, a1 = 0; int wait = 1000; return vnic_dev_cmd(vdev, CMD_SOFT_RESET, &a0, &a1, wait); } -static int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done) { u64 a0 = 0, a1 = 0; int wait = 1000; diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h index b013b6a78e87..54156c484424 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.h +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h @@ -155,7 +155,9 @@ int vnic_dev_deinit(struct vnic_dev *vdev); void vnic_dev_intr_coal_timer_info_default(struct vnic_dev *vdev); int vnic_dev_intr_coal_timer_info(struct vnic_dev *vdev); int vnic_dev_hang_reset(struct vnic_dev *vdev, int arg); +int vnic_dev_soft_reset(struct vnic_dev *vdev, int arg); int vnic_dev_hang_reset_done(struct vnic_dev *vdev, int *done); +int vnic_dev_soft_reset_done(struct vnic_dev *vdev, int *done); void vnic_dev_set_intr_mode(struct vnic_dev *vdev, enum vnic_dev_intr_mode intr_mode); enum vnic_dev_intr_mode vnic_dev_get_intr_mode(struct vnic_dev *vdev); diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index a02ecc4f9002..cadcee645f74 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -1597,7 +1597,6 @@ static void de_get_drvinfo (struct net_device *dev,struct ethtool_drvinfo *info) strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->bus_info, pci_name(de->pdev), sizeof(info->bus_info)); - info->eedump_len = DE_EEPROM_SIZE; } static int de_get_regs_len(struct net_device *dev) diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 2c9ed1710ba6..f4cb8e425853 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -234,9 +234,6 @@ static void be_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name) diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index a2c96fd88393..ff665493ca97 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -201,6 +201,7 @@ struct ethoc { void __iomem *membase; int dma_alloc; resource_size_t io_region_size; + bool big_endian; unsigned int num_bd; unsigned int num_tx; @@ -236,12 +237,18 @@ struct ethoc_bd { static inline u32 ethoc_read(struct ethoc *dev, loff_t offset) { - return ioread32(dev->iobase + offset); + if (dev->big_endian) + return ioread32be(dev->iobase + offset); + else + return ioread32(dev->iobase + offset); } static inline void ethoc_write(struct ethoc *dev, loff_t offset, u32 data) { - iowrite32(data, dev->iobase + offset); + if (dev->big_endian) + iowrite32be(data, dev->iobase + offset); + else + iowrite32(data, dev->iobase + offset); } static inline void ethoc_read_bd(struct ethoc *dev, int index, @@ -1106,6 +1113,9 @@ static int ethoc_probe(struct platform_device *pdev) priv->dma_alloc = buffer_size; } + priv->big_endian = pdata ? pdata->big_endian : + of_device_is_big_endian(pdev->dev.of_node); + /* calculate the number of TX/RX buffers, maximum 128 supported */ num_bd = min_t(unsigned int, 128, (netdev->mem_end - netdev->mem_start + 1) / ETHOC_BUFSIZ); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index dd4ca39d5d8f..501e1438d153 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3070,7 +3070,6 @@ static void fec_poll_controller(struct net_device *dev) } #endif -#define FEATURES_NEED_QUIESCE NETIF_F_RXCSUM static inline void fec_enet_set_netdev_features(struct net_device *netdev, netdev_features_t features) { @@ -3094,7 +3093,7 @@ static int fec_set_features(struct net_device *netdev, struct fec_enet_private *fep = netdev_priv(netdev); netdev_features_t changed = features ^ netdev->features; - if (netif_running(netdev) && changed & FEATURES_NEED_QUIESCE) { + if (netif_running(netdev) && changed & NETIF_F_RXCSUM) { napi_disable(&fep->napi); netif_tx_lock_bh(netdev); fec_stop(netdev); diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 1543cf0e8ef6..f9e74461bdc0 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -112,9 +112,8 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) unsigned long flags; u32 val, tempval; int inc; - struct timespec ts; + struct timespec64 ts; u64 ns; - u32 remainder; val = 0; if (!(fep->hwts_tx_en || fep->hwts_rx_en)) { @@ -163,8 +162,7 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable) tempval = readl(fep->hwp + FEC_ATIME); /* Convert the ptp local counter to 1588 timestamp */ ns = timecounter_cyc2time(&fep->tc, tempval); - ts.tv_sec = div_u64_rem(ns, 1000000000ULL, &remainder); - ts.tv_nsec = remainder; + ts = ns_to_timespec64(ns); /* The tempval is less than 3 seconds, and so val is less than * 4 seconds. No overflow for 32bit calculation. diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 710715fcb23d..7f5389c3c0cf 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -907,6 +907,9 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) if (of_find_property(np, "fsl,magic-packet", NULL)) priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; + if (of_get_property(np, "fsl,wake-on-filer", NULL)) + priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER; + priv->phy_node = of_parse_phandle(np, "phy-handle", 0); /* In the case of a fixed PHY, the DT node associated @@ -1415,8 +1418,14 @@ static int gfar_probe(struct platform_device *ofdev) goto register_fail; } - device_set_wakeup_capable(&dev->dev, priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) + priv->wol_supported |= GFAR_WOL_MAGIC; + + if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) && + priv->rx_filer_enable) + priv->wol_supported |= GFAR_WOL_FILER_UCAST; + + device_set_wakeup_capable(&ofdev->dev, priv->wol_supported); /* fill out IRQ number and name fields */ for (i = 0; i < priv->num_grps; i++) { @@ -1479,15 +1488,122 @@ static int gfar_remove(struct platform_device *ofdev) #ifdef CONFIG_PM +static void __gfar_filer_disable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT); + gfar_write(®s->rctrl, temp); +} + +static void __gfar_filer_enable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT; + gfar_write(®s->rctrl, temp); +} + +/* Filer rules implementing wol capabilities */ +static void gfar_filer_config_wol(struct gfar_private *priv) +{ + unsigned int i; + u32 rqfcr; + + __gfar_filer_disable(priv); + + /* clear the filer table, reject any packet by default */ + rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH; + for (i = 0; i <= MAX_FILER_IDX; i++) + gfar_write_filer(priv, i, rqfcr, 0); + + i = 0; + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) { + /* unicast packet, accept it */ + struct net_device *ndev = priv->ndev; + /* get the default rx queue index */ + u8 qindex = (u8)priv->gfargrp[0].rx_queue->qindex; + u32 dest_mac_addr = (ndev->dev_addr[0] << 16) | + (ndev->dev_addr[1] << 8) | + ndev->dev_addr[2]; + + rqfcr = (qindex << 10) | RQFCR_AND | + RQFCR_CMP_EXACT | RQFCR_PID_DAH; + + gfar_write_filer(priv, i++, rqfcr, dest_mac_addr); + + dest_mac_addr = (ndev->dev_addr[3] << 16) | + (ndev->dev_addr[4] << 8) | + ndev->dev_addr[5]; + rqfcr = (qindex << 10) | RQFCR_GPI | + RQFCR_CMP_EXACT | RQFCR_PID_DAL; + gfar_write_filer(priv, i++, rqfcr, dest_mac_addr); + } + + __gfar_filer_enable(priv); +} + +static void gfar_filer_restore_table(struct gfar_private *priv) +{ + u32 rqfcr, rqfpr; + unsigned int i; + + __gfar_filer_disable(priv); + + for (i = 0; i <= MAX_FILER_IDX; i++) { + rqfcr = priv->ftp_rqfcr[i]; + rqfpr = priv->ftp_rqfpr[i]; + gfar_write_filer(priv, i, rqfcr, rqfpr); + } + + __gfar_filer_enable(priv); +} + +/* gfar_start() for Rx only and with the FGPI filer interrupt enabled */ +static void gfar_start_wol_filer(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 tempval; + int i = 0; + + /* Enable Rx hw queues */ + gfar_write(®s->rqueue, priv->rqueue); + + /* Initialize DMACTRL to have WWR and WOP */ + tempval = gfar_read(®s->dmactrl); + tempval |= DMACTRL_INIT_SETTINGS; + gfar_write(®s->dmactrl, tempval); + + /* Make sure we aren't stopped */ + tempval = gfar_read(®s->dmactrl); + tempval &= ~DMACTRL_GRS; + gfar_write(®s->dmactrl, tempval); + + for (i = 0; i < priv->num_grps; i++) { + regs = priv->gfargrp[i].regs; + /* Clear RHLT, so that the DMA starts polling now */ + gfar_write(®s->rstat, priv->gfargrp[i].rstat); + /* enable the Filer General Purpose Interrupt */ + gfar_write(®s->imask, IMASK_FGPI); + } + + /* Enable Rx DMA */ + tempval = gfar_read(®s->maccfg1); + tempval |= MACCFG1_RX_EN; + gfar_write(®s->maccfg1, tempval); +} + static int gfar_suspend(struct device *dev) { struct gfar_private *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->ndev; struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + u16 wol = priv->wol_opts; if (!netif_running(ndev)) return 0; @@ -1499,7 +1615,7 @@ static int gfar_suspend(struct device *dev) gfar_halt(priv); - if (magic_packet) { + if (wol & GFAR_WOL_MAGIC) { /* Enable interrupt on Magic Packet */ gfar_write(®s->imask, IMASK_MAG); @@ -1513,6 +1629,10 @@ static int gfar_suspend(struct device *dev) tempval |= MACCFG1_RX_EN; gfar_write(®s->maccfg1, tempval); + } else if (wol & GFAR_WOL_FILER_UCAST) { + gfar_filer_config_wol(priv); + gfar_start_wol_filer(priv); + } else { phy_stop(priv->phydev); } @@ -1526,18 +1646,22 @@ static int gfar_resume(struct device *dev) struct net_device *ndev = priv->ndev; struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 tempval; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + u16 wol = priv->wol_opts; if (!netif_running(ndev)) return 0; - if (magic_packet) { + if (wol & GFAR_WOL_MAGIC) { /* Disable Magic Packet mode */ tempval = gfar_read(®s->maccfg2); tempval &= ~MACCFG2_MPEN; gfar_write(®s->maccfg2, tempval); + + } else if (wol & GFAR_WOL_FILER_UCAST) { + /* need to stop rx only, tx is already down */ + gfar_halt(priv); + gfar_filer_restore_table(priv); + } else { phy_start(priv->phydev); } @@ -1998,6 +2122,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) gfar_irq(grp, RX)->irq); goto rx_irq_fail; } + enable_irq_wake(gfar_irq(grp, RX)->irq); + } else { err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0, gfar_irq(grp, TX)->name, grp); @@ -2743,7 +2869,14 @@ irqreturn_t gfar_receive(int irq, void *grp_id) { struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id; unsigned long flags; - u32 imask; + u32 imask, ievent; + + ievent = gfar_read(&grp->regs->ievent); + + if (unlikely(ievent & IEVENT_FGPI)) { + gfar_write(&grp->regs->ievent, IEVENT_FGPI); + return IRQ_HANDLED; + } if (likely(napi_schedule_prep(&grp->napi_rx))) { spin_lock_irqsave(&grp->grplock, flags); diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index 8c1994856e93..f266b20f9ef5 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -340,6 +340,7 @@ extern const char gfar_driver_version[]; #define IEVENT_MAG 0x00000800 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 +#define IEVENT_FGPI 0x00000010 #define IEVENT_FIR 0x00000008 #define IEVENT_FIQ 0x00000004 #define IEVENT_DPE 0x00000002 @@ -372,6 +373,7 @@ extern const char gfar_driver_version[]; #define IMASK_MAG 0x00000800 #define IMASK_GRSC 0x00000100 #define IMASK_RXFEN0 0x00000080 +#define IMASK_FGPI 0x00000010 #define IMASK_FIR 0x00000008 #define IMASK_FIQ 0x00000004 #define IMASK_DPE 0x00000002 @@ -540,6 +542,9 @@ extern const char gfar_driver_version[]; #define GFAR_INT_NAME_MAX (IFNAMSIZ + 6) /* '_g#_xx' */ +#define GFAR_WOL_MAGIC 0x00000001 +#define GFAR_WOL_FILER_UCAST 0x00000002 + struct txbd8 { union { @@ -917,6 +922,7 @@ struct gfar { #define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200 #define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400 #define FSL_GIANFAR_DEV_HAS_TIMER 0x00000800 +#define FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER 0x00001000 #if (MAXGROUPS == 2) #define DEFAULT_MAPPING 0xAA @@ -1161,8 +1167,6 @@ struct gfar_private { extended_hash:1, bd_stash_en:1, rx_filer_enable:1, - /* Wake-on-LAN enabled */ - wol_en:1, /* Enable priorty based Tx scheduling in Hw */ prio_sched_en:1, /* Flow control flags */ @@ -1191,6 +1195,10 @@ struct gfar_private { u32 __iomem *hash_regs[16]; int hash_width; + /* wake-on-lan settings */ + u16 wol_opts; + u16 wol_supported; + /*Filer table*/ unsigned int ftp_rqfpr[MAX_FILER_IDX + 1]; unsigned int ftp_rqfcr[MAX_FILER_IDX + 1]; diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 6bdc89179b72..928ca2bdd238 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -182,8 +182,6 @@ static void gfar_gdrvinfo(struct net_device *dev, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "N/A", sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } @@ -644,28 +642,49 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); - if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { - wol->supported = WAKE_MAGIC; - wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; - } else { - wol->supported = wol->wolopts = 0; - } + wol->supported = 0; + wol->wolopts = 0; + + if (priv->wol_supported & GFAR_WOL_MAGIC) + wol->supported |= WAKE_MAGIC; + + if (priv->wol_supported & GFAR_WOL_FILER_UCAST) + wol->supported |= WAKE_UCAST; + + if (priv->wol_opts & GFAR_WOL_MAGIC) + wol->wolopts |= WAKE_MAGIC; + + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) + wol->wolopts |= WAKE_UCAST; } static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); + u16 wol_opts = 0; + int err; - if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && - wol->wolopts != 0) + if (!priv->wol_supported && wol->wolopts) return -EINVAL; - if (wol->wolopts & ~WAKE_MAGIC) + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_UCAST)) return -EINVAL; - device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC); + if (wol->wolopts & WAKE_MAGIC) { + wol_opts |= GFAR_WOL_MAGIC; + } else { + if (wol->wolopts & WAKE_UCAST) + wol_opts |= GFAR_WOL_FILER_UCAST; + } + + wol_opts &= priv->wol_supported; + priv->wol_opts = 0; + + err = device_set_wakeup_enable(priv->dev, wol_opts); + if (err) + return err; - priv->wol_en = !!device_may_wakeup(&dev->dev); + priv->wol_opts = wol_opts; return 0; } diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index cc83350d56ba..89714f5e0dfc 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -351,8 +351,6 @@ uec_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "QUICC ENGINE", sizeof(drvinfo->bus_info)); - drvinfo->eedump_len = 0; - drvinfo->regdump_len = uec_get_regs_len(netdev); } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index dead17b5d769..f250dec488fd 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_HISILICON bool "Hisilicon devices" default y - depends on ARM + depends on OF && (ARM || ARM64 || COMPILE_TEST) ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -24,11 +24,42 @@ config HIX5HD2_GMAC config HIP04_ETH tristate "HISILICON P04 Ethernet support" - select PHYLIB select MARVELL_PHY select MFD_SYSCON + select HNS_MDIO ---help--- If you wish to compile a kernel for a hardware with hisilicon p04 SoC and want to use the internal ethernet then you should answer Y to this. +config HNS_MDIO + tristate + select PHYLIB + ---help--- + This selects the HNS MDIO support. It is needed by HNS_DSAF to access + the PHY + +config HNS + tristate "Hisilicon Network Subsystem Support (Framework)" + ---help--- + This selects the framework support for Hisilicon Network Subsystem. It + is needed by any driver which provides HNS acceleration engine or make + use of the engine + +config HNS_DSAF + tristate "Hisilicon HNS DSAF device Support" + select HNS + select HNS_MDIO + ---help--- + This selects the DSAF (Distributed System Area Frabric) network + acceleration engine support. The engine is used in Hisilicon hip05, + Hi1610 and further ICT SoC + +config HNS_ENET + tristate "Hisilicon HNS Ethernet Device Support" + select PHYLIB + select HNS + ---help--- + This selects the general ethernet driver for HNS. This module make + use of any HNS AE driver, such as HNS_DSAF + endif # NET_VENDOR_HISILICON diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile index 6c14540a4dc5..390b71fb3000 100644 --- a/drivers/net/ethernet/hisilicon/Makefile +++ b/drivers/net/ethernet/hisilicon/Makefile @@ -3,4 +3,6 @@ # obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o -obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o hip04_eth.o +obj-$(CONFIG_HIP04_ETH) += hip04_eth.o +obj-$(CONFIG_HNS_MDIO) += hns_mdio.o +obj-$(CONFIG_HNS) += hns/ diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c deleted file mode 100644 index fca0a5be1f0f..000000000000 --- a/drivers/net/ethernet/hisilicon/hip04_mdio.c +++ /dev/null @@ -1,185 +0,0 @@ -/* Copyright (c) 2014 Linaro Ltd. - * Copyright (c) 2014 Hisilicon Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/of_mdio.h> -#include <linux/delay.h> - -#define MDIO_CMD_REG 0x0 -#define MDIO_ADDR_REG 0x4 -#define MDIO_WDATA_REG 0x8 -#define MDIO_RDATA_REG 0xc -#define MDIO_STA_REG 0x10 - -#define MDIO_START BIT(14) -#define MDIO_R_VALID BIT(1) -#define MDIO_READ (BIT(12) | BIT(11) | MDIO_START) -#define MDIO_WRITE (BIT(12) | BIT(10) | MDIO_START) - -struct hip04_mdio_priv { - void __iomem *base; -}; - -#define WAIT_TIMEOUT 10 -static int hip04_mdio_wait_ready(struct mii_bus *bus) -{ - struct hip04_mdio_priv *priv = bus->priv; - int i; - - for (i = 0; readl_relaxed(priv->base + MDIO_CMD_REG) & MDIO_START; i++) { - if (i == WAIT_TIMEOUT) - return -ETIMEDOUT; - msleep(20); - } - - return 0; -} - -static int hip04_mdio_read(struct mii_bus *bus, int mii_id, int regnum) -{ - struct hip04_mdio_priv *priv = bus->priv; - u32 val; - int ret; - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - val = regnum | (mii_id << 5) | MDIO_READ; - writel_relaxed(val, priv->base + MDIO_CMD_REG); - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - val = readl_relaxed(priv->base + MDIO_STA_REG); - if (val & MDIO_R_VALID) { - dev_err(bus->parent, "SMI bus read not valid\n"); - ret = -ENODEV; - goto out; - } - - val = readl_relaxed(priv->base + MDIO_RDATA_REG); - ret = val & 0xFFFF; -out: - return ret; -} - -static int hip04_mdio_write(struct mii_bus *bus, int mii_id, - int regnum, u16 value) -{ - struct hip04_mdio_priv *priv = bus->priv; - u32 val; - int ret; - - ret = hip04_mdio_wait_ready(bus); - if (ret < 0) - goto out; - - writel_relaxed(value, priv->base + MDIO_WDATA_REG); - val = regnum | (mii_id << 5) | MDIO_WRITE; - writel_relaxed(val, priv->base + MDIO_CMD_REG); -out: - return ret; -} - -static int hip04_mdio_reset(struct mii_bus *bus) -{ - int temp, i; - - for (i = 0; i < PHY_MAX_ADDR; i++) { - hip04_mdio_write(bus, i, 22, 0); - temp = hip04_mdio_read(bus, i, MII_BMCR); - if (temp < 0) - continue; - - temp |= BMCR_RESET; - if (hip04_mdio_write(bus, i, MII_BMCR, temp) < 0) - continue; - } - - mdelay(500); - return 0; -} - -static int hip04_mdio_probe(struct platform_device *pdev) -{ - struct resource *r; - struct mii_bus *bus; - struct hip04_mdio_priv *priv; - int ret; - - bus = mdiobus_alloc_size(sizeof(struct hip04_mdio_priv)); - if (!bus) { - dev_err(&pdev->dev, "Cannot allocate MDIO bus\n"); - return -ENOMEM; - } - - bus->name = "hip04_mdio_bus"; - bus->read = hip04_mdio_read; - bus->write = hip04_mdio_write; - bus->reset = hip04_mdio_reset; - snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev)); - bus->parent = &pdev->dev; - priv = bus->priv; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(priv->base)) { - ret = PTR_ERR(priv->base); - goto out_mdio; - } - - ret = of_mdiobus_register(bus, pdev->dev.of_node); - if (ret < 0) { - dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); - goto out_mdio; - } - - platform_set_drvdata(pdev, bus); - - return 0; - -out_mdio: - mdiobus_free(bus); - return ret; -} - -static int hip04_mdio_remove(struct platform_device *pdev) -{ - struct mii_bus *bus = platform_get_drvdata(pdev); - - mdiobus_unregister(bus); - mdiobus_free(bus); - - return 0; -} - -static const struct of_device_id hip04_mdio_match[] = { - { .compatible = "hisilicon,hip04-mdio" }, - { } -}; -MODULE_DEVICE_TABLE(of, hip04_mdio_match); - -static struct platform_driver hip04_mdio_driver = { - .probe = hip04_mdio_probe, - .remove = hip04_mdio_remove, - .driver = { - .name = "hip04-mdio", - .of_match_table = hip04_mdio_match, - }, -}; - -module_platform_driver(hip04_mdio_driver); - -MODULE_DESCRIPTION("HISILICON P04 MDIO interface driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:hip04-mdio"); diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index a5e077eac99a..e51892d518ff 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -371,7 +371,7 @@ static void hix5hd2_port_enable(struct hix5hd2_priv *priv) static void hix5hd2_port_disable(struct hix5hd2_priv *priv) { - writel_relaxed(~(BITS_RX_EN | BITS_TX_EN), priv->base + PORT_EN); + writel_relaxed(~(u32)(BITS_RX_EN | BITS_TX_EN), priv->base + PORT_EN); writel_relaxed(0, priv->base + DESC_WR_RD_ENA); } diff --git a/drivers/net/ethernet/hisilicon/hns/Makefile b/drivers/net/ethernet/hisilicon/hns/Makefile new file mode 100644 index 000000000000..6010c83e38d8 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the HISILICON network device drivers. +# + +obj-$(CONFIG_HNS) += hnae.o + +obj-$(CONFIG_HNS_DSAF) += hns_dsaf.o +hns_dsaf-objs = hns_ae_adapt.o hns_dsaf_gmac.o hns_dsaf_mac.o hns_dsaf_misc.o \ + hns_dsaf_main.o hns_dsaf_ppe.o hns_dsaf_rcb.o hns_dsaf_xgmac.o + +obj-$(CONFIG_HNS_ENET) += hns_enet_drv.o +hns_enet_drv-objs = hns_enet.o hns_ethtool.o diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c new file mode 100644 index 000000000000..9d3bb8349a73 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/skbuff.h> +#include <linux/slab.h> + +#include "hnae.h" + +#define cls_to_ae_dev(dev) container_of(dev, struct hnae_ae_dev, cls_dev) + +static struct class *hnae_class; + +static void +hnae_list_add(spinlock_t *lock, struct list_head *node, struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + list_add_tail_rcu(node, head); + spin_unlock_irqrestore(lock, flags); +} + +static void hnae_list_del(spinlock_t *lock, struct list_head *node) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + list_del_rcu(node); + spin_unlock_irqrestore(lock, flags); +} + +static int hnae_alloc_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + unsigned int order = hnae_page_order(ring); + struct page *p = dev_alloc_pages(order); + + if (!p) + return -ENOMEM; + + cb->priv = p; + cb->page_offset = 0; + cb->reuse_flag = 0; + cb->buf = page_address(p); + cb->length = hnae_page_size(ring); + cb->type = DESC_TYPE_PAGE; + + return 0; +} + +static void hnae_free_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dev_kfree_skb_any((struct sk_buff *)cb->priv); + else if (unlikely(is_rx_ring(ring))) + put_page((struct page *)cb->priv); + memset(cb, 0, sizeof(*cb)); +} + +static int hnae_map_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + cb->dma = dma_map_page(ring_to_dev(ring), cb->priv, 0, + cb->length, ring_to_dma_dir(ring)); + + if (dma_mapping_error(ring_to_dev(ring), cb->dma)) + return -EIO; + + return 0; +} + +static void hnae_unmap_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dma_unmap_single(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); + else + dma_unmap_page(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); +} + +static struct hnae_buf_ops hnae_bops = { + .alloc_buffer = hnae_alloc_buffer, + .free_buffer = hnae_free_buffer, + .map_buffer = hnae_map_buffer, + .unmap_buffer = hnae_unmap_buffer, +}; + +static int __ae_match(struct device *dev, const void *data) +{ + struct hnae_ae_dev *hdev = cls_to_ae_dev(dev); + const char *ae_id = data; + + if (!strncmp(ae_id, hdev->name, AE_NAME_SIZE)) + return 1; + + return 0; +} + +static struct hnae_ae_dev *find_ae(const char *ae_id) +{ + struct device *dev; + + WARN_ON(!ae_id); + + dev = class_find_device(hnae_class, NULL, ae_id, __ae_match); + + return dev ? cls_to_ae_dev(dev) : NULL; +} + +static void hnae_free_buffers(struct hnae_ring *ring) +{ + int i; + + for (i = 0; i < ring->desc_num; i++) + hnae_free_buffer_detach(ring, i); +} + +/* Allocate memory for raw pkg, and map with dma */ +static int hnae_alloc_buffers(struct hnae_ring *ring) +{ + int i, j, ret; + + for (i = 0; i < ring->desc_num; i++) { + ret = hnae_alloc_buffer_attach(ring, i); + if (ret) + goto out_buffer_fail; + } + + return 0; + +out_buffer_fail: + for (j = i - 1; j >= 0; j--) + hnae_free_buffer_detach(ring, j); + return ret; +} + +/* free desc along with its attached buffer */ +static void hnae_free_desc(struct hnae_ring *ring) +{ + hnae_free_buffers(ring); + dma_unmap_single(ring_to_dev(ring), ring->desc_dma_addr, + ring->desc_num * sizeof(ring->desc[0]), + ring_to_dma_dir(ring)); + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; +} + +/* alloc desc, without buffer attached */ +static int hnae_alloc_desc(struct hnae_ring *ring) +{ + int size = ring->desc_num * sizeof(ring->desc[0]); + + ring->desc = kzalloc(size, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_dma_addr = dma_map_single(ring_to_dev(ring), + ring->desc, size, ring_to_dma_dir(ring)); + if (dma_mapping_error(ring_to_dev(ring), ring->desc_dma_addr)) { + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; + return -ENOMEM; + } + + return 0; +} + +/* fini ring, also free the buffer for the ring */ +static void hnae_fini_ring(struct hnae_ring *ring) +{ + hnae_free_desc(ring); + kfree(ring->desc_cb); + ring->desc_cb = NULL; + ring->next_to_clean = 0; + ring->next_to_use = 0; +} + +/* init ring, and with buffer for rx ring */ +static int +hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags) +{ + int ret; + + if (ring->desc_num <= 0 || ring->buf_size <= 0) + return -EINVAL; + + ring->q = q; + ring->flags = flags; + assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr); + + /* not matter for tx or rx ring, the ntc and ntc start from 0 */ + assert(ring->next_to_use == 0); + assert(ring->next_to_clean == 0); + + ring->desc_cb = kcalloc(ring->desc_num, sizeof(ring->desc_cb[0]), + GFP_KERNEL); + if (!ring->desc_cb) { + ret = -ENOMEM; + goto out; + } + + ret = hnae_alloc_desc(ring); + if (ret) + goto out_with_desc_cb; + + if (is_rx_ring(ring)) { + ret = hnae_alloc_buffers(ring); + if (ret) + goto out_with_desc; + } + + return 0; + +out_with_desc: + hnae_free_desc(ring); +out_with_desc_cb: + kfree(ring->desc_cb); + ring->desc_cb = NULL; +out: + return ret; +} + +static int hnae_init_queue(struct hnae_handle *h, struct hnae_queue *q, + struct hnae_ae_dev *dev) +{ + int ret; + + q->dev = dev; + q->handle = h; + + ret = hnae_init_ring(q, &q->tx_ring, q->tx_ring.flags | RINGF_DIR); + if (ret) + goto out; + + ret = hnae_init_ring(q, &q->rx_ring, q->rx_ring.flags & ~RINGF_DIR); + if (ret) + goto out_with_tx_ring; + + if (dev->ops->init_queue) + dev->ops->init_queue(q); + + return 0; + +out_with_tx_ring: + hnae_fini_ring(&q->tx_ring); +out: + return ret; +} + +static void hnae_fini_queue(struct hnae_queue *q) +{ + if (q->dev->ops->fini_queue) + q->dev->ops->fini_queue(q); + + hnae_fini_ring(&q->tx_ring); + hnae_fini_ring(&q->rx_ring); +} + +/** + * ae_chain - define ae chain head + */ +static RAW_NOTIFIER_HEAD(ae_chain); + +int hnae_register_notifier(struct notifier_block *nb) +{ + return raw_notifier_chain_register(&ae_chain, nb); +} +EXPORT_SYMBOL(hnae_register_notifier); + +void hnae_unregister_notifier(struct notifier_block *nb) +{ + if (raw_notifier_chain_unregister(&ae_chain, nb)) + dev_err(NULL, "notifier chain unregister fail\n"); +} +EXPORT_SYMBOL(hnae_unregister_notifier); + +int hnae_reinit_handle(struct hnae_handle *handle) +{ + int i, j; + int ret; + + for (i = 0; i < handle->q_num; i++) /* free ring*/ + hnae_fini_queue(handle->qs[i]); + + if (handle->dev->ops->reset) + handle->dev->ops->reset(handle); + + for (i = 0; i < handle->q_num; i++) {/* reinit ring*/ + ret = hnae_init_queue(handle, handle->qs[i], handle->dev); + if (ret) + goto out_when_init_queue; + } + return 0; +out_when_init_queue: + for (j = i - 1; j >= 0; j--) + hnae_fini_queue(handle->qs[j]); + return ret; +} +EXPORT_SYMBOL(hnae_reinit_handle); + +/* hnae_get_handle - get a handle from the AE + * @owner_dev: the dev use this handle + * @ae_id: the id of the ae to be used + * @ae_opts: the options set for the handle + * @bops: the callbacks for buffer management + * + * return handle ptr or ERR_PTR + */ +struct hnae_handle *hnae_get_handle(struct device *owner_dev, + const char *ae_id, u32 port_id, + struct hnae_buf_ops *bops) +{ + struct hnae_ae_dev *dev; + struct hnae_handle *handle; + int i, j; + int ret; + + dev = find_ae(ae_id); + if (!dev) + return ERR_PTR(-ENODEV); + + handle = dev->ops->get_handle(dev, port_id); + if (IS_ERR(handle)) + return handle; + + handle->dev = dev; + handle->owner_dev = owner_dev; + handle->bops = bops ? bops : &hnae_bops; + handle->eport_id = port_id; + + for (i = 0; i < handle->q_num; i++) { + ret = hnae_init_queue(handle, handle->qs[i], dev); + if (ret) + goto out_when_init_queue; + } + + __module_get(dev->owner); + + hnae_list_add(&dev->lock, &handle->node, &dev->handle_list); + + return handle; + +out_when_init_queue: + for (j = i - 1; j >= 0; j--) + hnae_fini_queue(handle->qs[j]); + + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL(hnae_get_handle); + +void hnae_put_handle(struct hnae_handle *h) +{ + struct hnae_ae_dev *dev = h->dev; + int i; + + for (i = 0; i < h->q_num; i++) + hnae_fini_queue(h->qs[i]); + + if (h->dev->ops->reset) + h->dev->ops->reset(h); + + hnae_list_del(&dev->lock, &h->node); + + if (dev->ops->put_handle) + dev->ops->put_handle(h); + + module_put(dev->owner); +} +EXPORT_SYMBOL(hnae_put_handle); + +static void hnae_release(struct device *dev) +{ +} + +/** + * hnae_ae_register - register a AE engine to hnae framework + * @hdev: the hnae ae engine device + * @owner: the module who provides this dev + * NOTE: the duplicated name will not be checked + */ +int hnae_ae_register(struct hnae_ae_dev *hdev, struct module *owner) +{ + static atomic_t id = ATOMIC_INIT(-1); + int ret; + + if (!hdev->dev) + return -ENODEV; + + if (!hdev->ops || !hdev->ops->get_handle || + !hdev->ops->toggle_ring_irq || + !hdev->ops->toggle_queue_status || + !hdev->ops->get_status || !hdev->ops->adjust_link) + return -EINVAL; + + hdev->owner = owner; + hdev->id = (int)atomic_inc_return(&id); + hdev->cls_dev.parent = hdev->dev; + hdev->cls_dev.class = hnae_class; + hdev->cls_dev.release = hnae_release; + (void)dev_set_name(&hdev->cls_dev, "hnae%d", hdev->id); + ret = device_register(&hdev->cls_dev); + if (ret) + return ret; + + __module_get(THIS_MODULE); + + INIT_LIST_HEAD(&hdev->handle_list); + spin_lock_init(&hdev->lock); + + ret = raw_notifier_call_chain(&ae_chain, HNAE_AE_REGISTER, NULL); + if (ret) + dev_dbg(hdev->dev, + "has not notifier for AE: %s\n", hdev->name); + + return 0; +} +EXPORT_SYMBOL(hnae_ae_register); + +/** + * hnae_ae_unregister - unregisters a HNAE AE engine + * @cdev: the device to unregister + */ +void hnae_ae_unregister(struct hnae_ae_dev *hdev) +{ + device_unregister(&hdev->cls_dev); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL(hnae_ae_unregister); + +static int __init hnae_init(void) +{ + hnae_class = class_create(THIS_MODULE, "hnae"); + if (IS_ERR(hnae_class)) + return PTR_ERR(hnae_class); + + return 0; +} + +static void __exit hnae_exit(void) +{ + class_destroy(hnae_class); +} + +subsys_initcall(hnae_init); +module_exit(hnae_exit); + +MODULE_AUTHOR("Hisilicon, Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Hisilicon Network Acceleration Engine Framework"); + +/* vi: set tw=78 noet: */ diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h new file mode 100644 index 000000000000..cec95ac8687d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNAE_H +#define __HNAE_H + +/* Names used in this framework: + * ae handle (handle): + * a set of queues provided by AE + * ring buffer queue (rbq): + * the channel between upper layer and the AE, can do tx and rx + * ring: + * a tx or rx channel within a rbq + * ring description (desc): + * an element in the ring with packet information + * buffer: + * a memory region referred by desc with the full packet payload + * + * "num" means a static number set as a parameter, "count" mean a dynamic + * number set while running + * "cb" means control block + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/notifier.h> +#include <linux/phy.h> +#include <linux/types.h> + +#define HNAE_DRIVER_VERSION "1.3.0" +#define HNAE_DRIVER_NAME "hns" +#define HNAE_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." +#define HNAE_DRIVER_STRING "Hisilicon Network Subsystem Driver" +#define HNAE_DEFAULT_DEVICE_DESCR "Hisilicon Network Subsystem" + +#ifdef DEBUG + +#ifndef assert +#define assert(expr) \ +do { \ + if (!(expr)) { \ + pr_err("Assertion failed! %s, %s, %s, line %d\n", \ + #expr, __FILE__, __func__, __LINE__); \ + } \ +} while (0) +#endif + +#else + +#ifndef assert +#define assert(expr) +#endif + +#endif + +#define AE_VERSION_1 ('6' << 16 | '6' << 8 | '0') +#define AE_VERSION_2 ('1' << 24 | '6' << 16 | '1' << 8 | '0') +#define AE_NAME_SIZE 16 + +/* some said the RX and TX RCB format should not be the same in the future. But + * it is the same now... + */ +#define RCB_REG_BASEADDR_L 0x00 /* P660 support only 32bit accessing */ +#define RCB_REG_BASEADDR_H 0x04 +#define RCB_REG_BD_NUM 0x08 +#define RCB_REG_BD_LEN 0x0C +#define RCB_REG_PKTLINE 0x10 +#define RCB_REG_TAIL 0x18 +#define RCB_REG_HEAD 0x1C +#define RCB_REG_FBDNUM 0x20 +#define RCB_REG_OFFSET 0x24 /* pkt num to be handled */ +#define RCB_REG_PKTNUM_RECORD 0x2C /* total pkt received */ + +#define HNS_RX_HEAD_SIZE 256 + +#define HNAE_AE_REGISTER 0x1 + +#define RCB_RING_NAME_LEN 16 + +enum hnae_led_state { + HNAE_LED_INACTIVE, + HNAE_LED_ACTIVE, + HNAE_LED_ON, + HNAE_LED_OFF +}; + +#define HNS_RX_FLAG_VLAN_PRESENT 0x1 +#define HNS_RX_FLAG_L3ID_IPV4 0x0 +#define HNS_RX_FLAG_L3ID_IPV6 0x1 +#define HNS_RX_FLAG_L4ID_UDP 0x0 +#define HNS_RX_FLAG_L4ID_TCP 0x1 + +#define HNS_TXD_ASID_S 0 +#define HNS_TXD_ASID_M (0xff << HNS_TXD_ASID_S) +#define HNS_TXD_BUFNUM_S 8 +#define HNS_TXD_BUFNUM_M (0x3 << HNS_TXD_BUFNUM_S) +#define HNS_TXD_PORTID_S 10 +#define HNS_TXD_PORTID_M (0x7 << HNS_TXD_PORTID_S) + +#define HNS_TXD_RA_B 8 +#define HNS_TXD_RI_B 9 +#define HNS_TXD_L4CS_B 10 +#define HNS_TXD_L3CS_B 11 +#define HNS_TXD_FE_B 12 +#define HNS_TXD_VLD_B 13 +#define HNS_TXD_IPOFFSET_S 14 +#define HNS_TXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S) + +#define HNS_RXD_IPOFFSET_S 0 +#define HNS_RXD_IPOFFSET_M (0xff << HNS_TXD_IPOFFSET_S) +#define HNS_RXD_BUFNUM_S 8 +#define HNS_RXD_BUFNUM_M (0x3 << HNS_RXD_BUFNUM_S) +#define HNS_RXD_PORTID_S 10 +#define HNS_RXD_PORTID_M (0x7 << HNS_RXD_PORTID_S) +#define HNS_RXD_DMAC_S 13 +#define HNS_RXD_DMAC_M (0x3 << HNS_RXD_DMAC_S) +#define HNS_RXD_VLAN_S 15 +#define HNS_RXD_VLAN_M (0x3 << HNS_RXD_VLAN_S) +#define HNS_RXD_L3ID_S 17 +#define HNS_RXD_L3ID_M (0xf << HNS_RXD_L3ID_S) +#define HNS_RXD_L4ID_S 21 +#define HNS_RXD_L4ID_M (0xf << HNS_RXD_L4ID_S) +#define HNS_RXD_FE_B 25 +#define HNS_RXD_FRAG_B 26 +#define HNS_RXD_VLD_B 27 +#define HNS_RXD_L2E_B 28 +#define HNS_RXD_L3E_B 29 +#define HNS_RXD_L4E_B 30 +#define HNS_RXD_DROP_B 31 + +#define HNS_RXD_VLANID_S 8 +#define HNS_RXD_VLANID_M (0xfff << HNS_RXD_VLANID_S) +#define HNS_RXD_CFI_B 20 +#define HNS_RXD_PRI_S 21 +#define HNS_RXD_PRI_M (0x7 << HNS_RXD_PRI_S) +#define HNS_RXD_ASID_S 24 +#define HNS_RXD_ASID_M (0xff << HNS_RXD_ASID_S) + +/* hardware spec ring buffer format */ +struct __packed hnae_desc { + __le64 addr; + union { + struct { + __le16 asid_bufnum_pid; + __le16 send_size; + __le32 flag_ipoffset; + __le32 reserved_3[4]; + } tx; + + struct { + __le32 ipoff_bnum_pid_flag; + __le16 pkt_len; + __le16 size; + __le32 vlan_pri_asid; + __le32 reserved_2[3]; + } rx; + }; +}; + +struct hnae_desc_cb { + dma_addr_t dma; /* dma address of this desc */ + void *buf; /* cpu addr for a desc */ + + /* priv data for the desc, e.g. skb when use with ip stack*/ + void *priv; + u16 page_offset; + u16 reuse_flag; + + u16 length; /* length of the buffer */ + + /* desc type, used by the ring user to mark the type of the priv data */ + u16 type; +}; + +#define setflags(flags, bits) ((flags) |= (bits)) +#define unsetflags(flags, bits) ((flags) &= ~(bits)) + +/* hnae_ring->flags fields */ +#define RINGF_DIR 0x1 /* TX or RX ring, set if TX */ +#define is_tx_ring(ring) ((ring)->flags & RINGF_DIR) +#define is_rx_ring(ring) (!is_tx_ring(ring)) +#define ring_to_dma_dir(ring) (is_tx_ring(ring) ? \ + DMA_TO_DEVICE : DMA_FROM_DEVICE) + +struct ring_stats { + u64 io_err_cnt; + u64 sw_err_cnt; + u64 seg_pkt_cnt; + union { + struct { + u64 tx_pkts; + u64 tx_bytes; + u64 tx_err_cnt; + u64 restart_queue; + u64 tx_busy; + }; + struct { + u64 rx_pkts; + u64 rx_bytes; + u64 rx_err_cnt; + u64 reuse_pg_cnt; + u64 err_pkt_len; + u64 non_vld_descs; + u64 err_bd_num; + u64 l2_err; + u64 l3l4_csum_err; + }; + }; +}; + +struct hnae_queue; + +struct hnae_ring { + u8 __iomem *io_base; /* base io address for the ring */ + struct hnae_desc *desc; /* dma map address space */ + struct hnae_desc_cb *desc_cb; + struct hnae_queue *q; + int irq; + char ring_name[RCB_RING_NAME_LEN]; + + /* statistic */ + struct ring_stats stats; + + dma_addr_t desc_dma_addr; + u32 buf_size; /* size for hnae_desc->addr, preset by AE */ + u16 desc_num; /* total number of desc */ + u16 max_desc_num_per_pkt; + u16 max_raw_data_sz_per_desc; + u16 max_pkt_size; + int next_to_use; /* idx of next spare desc */ + + /* idx of lastest sent desc, the ring is empty when equal to + * next_to_use + */ + int next_to_clean; + + int flags; /* ring attribute */ + int irq_init_flag; +}; + +#define ring_ptr_move_fw(ring, p) \ + ((ring)->p = ((ring)->p + 1) % (ring)->desc_num) +#define ring_ptr_move_bw(ring, p) \ + ((ring)->p = ((ring)->p - 1 + (ring)->desc_num) % (ring)->desc_num) + +enum hns_desc_type { + DESC_TYPE_SKB, + DESC_TYPE_PAGE, +}; + +#define assert_is_ring_idx(ring, idx) \ + assert((idx) >= 0 && (idx) < (ring)->desc_num) + +/* the distance between [begin, end) in a ring buffer + * note: there is a unuse slot between the begin and the end + */ +static inline int ring_dist(struct hnae_ring *ring, int begin, int end) +{ + assert_is_ring_idx(ring, begin); + assert_is_ring_idx(ring, end); + + return (end - begin + ring->desc_num) % ring->desc_num; +} + +static inline int ring_space(struct hnae_ring *ring) +{ + return ring->desc_num - + ring_dist(ring, ring->next_to_clean, ring->next_to_use) - 1; +} + +static inline int is_ring_empty(struct hnae_ring *ring) +{ + assert_is_ring_idx(ring, ring->next_to_use); + assert_is_ring_idx(ring, ring->next_to_clean); + + return ring->next_to_use == ring->next_to_clean; +} + +#define hnae_buf_size(_ring) ((_ring)->buf_size) +#define hnae_page_order(_ring) (get_order(hnae_buf_size(_ring))) +#define hnae_page_size(_ring) (PAGE_SIZE << hnae_page_order(_ring)) + +struct hnae_handle; + +/* allocate and dma map space for hnae desc */ +struct hnae_buf_ops { + int (*alloc_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + void (*free_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + int (*map_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); + void (*unmap_buffer)(struct hnae_ring *ring, struct hnae_desc_cb *cb); +}; + +struct hnae_queue { + void __iomem *io_base; + phys_addr_t phy_base; + struct hnae_ae_dev *dev; /* the device who use this queue */ + struct hnae_ring rx_ring, tx_ring; + struct hnae_handle *handle; +}; + +/*hnae loop mode*/ +enum hnae_loop { + MAC_INTERNALLOOP_MAC = 0, + MAC_INTERNALLOOP_SERDES, + MAC_INTERNALLOOP_PHY, + MAC_LOOP_NONE, +}; + +/*hnae port type*/ +enum hnae_port_type { + HNAE_PORT_SERVICE = 0, + HNAE_PORT_DEBUG +}; + +/* This struct defines the operation on the handle. + * + * get_handle(): (mandatory) + * Get a handle from AE according to its name and options. + * the AE driver should manage the space used by handle and its queues while + * the HNAE framework will allocate desc and desc_cb for all rings in the + * queues. + * put_handle(): + * Release the handle. + * start(): + * Enable the hardware, include all queues + * stop(): + * Disable the hardware + * set_opts(): (mandatory) + * Set options to the AE + * get_opts(): (mandatory) + * Get options from the AE + * get_status(): + * Get the carrier state of the back channel of the handle, 1 for ok, 0 for + * non-ok + * toggle_ring_irq(): (mandatory) + * Set the ring irq to be enabled(0) or disable(1) + * toggle_queue_status(): (mandatory) + * Set the queue to be enabled(1) or disable(0), this will not change the + * ring irq state + * adjust_link() + * adjust link status + * set_loopback() + * set loopback + * get_ring_bdnum_limit() + * get ring bd number limit + * get_pauseparam() + * get tx and rx of pause frame use + * set_autoneg() + * set auto autonegotiation of pause frame use + * get_autoneg() + * get auto autonegotiation of pause frame use + * set_pauseparam() + * set tx and rx of pause frame use + * get_coalesce_usecs() + * get usecs to delay a TX interrupt after a packet is sent + * get_rx_max_coalesced_frames() + * get Maximum number of packets to be sent before a TX interrupt. + * set_coalesce_usecs() + * set usecs to delay a TX interrupt after a packet is sent + * set_coalesce_frames() + * set Maximum number of packets to be sent before a TX interrupt. + * get_ringnum() + * get RX/TX ring number + * get_max_ringnum() + * get RX/TX ring maximum number + * get_mac_addr() + * get mac address + * set_mac_addr() + * set mac address + * set_mc_addr() + * set multicast mode + * set_mtu() + * set mtu + * update_stats() + * update Old network device statistics + * get_ethtool_stats() + * get ethtool network device statistics + * get_strings() + * get a set of strings that describe the requested objects + * get_sset_count() + * get number of strings that @get_strings will write + * update_led_status() + * update the led status + * set_led_id() + * set led id + * get_regs() + * get regs dump + * get_regs_len() + * get the len of the regs dump + */ +struct hnae_ae_ops { + struct hnae_handle *(*get_handle)(struct hnae_ae_dev *dev, + u32 port_id); + void (*put_handle)(struct hnae_handle *handle); + void (*init_queue)(struct hnae_queue *q); + void (*fini_queue)(struct hnae_queue *q); + int (*start)(struct hnae_handle *handle); + void (*stop)(struct hnae_handle *handle); + void (*reset)(struct hnae_handle *handle); + int (*set_opts)(struct hnae_handle *handle, int type, void *opts); + int (*get_opts)(struct hnae_handle *handle, int type, void **opts); + int (*get_status)(struct hnae_handle *handle); + int (*get_info)(struct hnae_handle *handle, + u8 *auto_neg, u16 *speed, u8 *duplex); + void (*toggle_ring_irq)(struct hnae_ring *ring, u32 val); + void (*toggle_queue_status)(struct hnae_queue *queue, u32 val); + void (*adjust_link)(struct hnae_handle *handle, int speed, int duplex); + int (*set_loopback)(struct hnae_handle *handle, + enum hnae_loop loop_mode, int en); + void (*get_ring_bdnum_limit)(struct hnae_queue *queue, + u32 *uplimit); + void (*get_pauseparam)(struct hnae_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en); + int (*set_autoneg)(struct hnae_handle *handle, u8 enable); + int (*get_autoneg)(struct hnae_handle *handle); + int (*set_pauseparam)(struct hnae_handle *handle, + u32 auto_neg, u32 rx_en, u32 tx_en); + void (*get_coalesce_usecs)(struct hnae_handle *handle, + u32 *tx_usecs, u32 *rx_usecs); + void (*get_rx_max_coalesced_frames)(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames); + void (*set_coalesce_usecs)(struct hnae_handle *handle, u32 timeout); + int (*set_coalesce_frames)(struct hnae_handle *handle, + u32 coalesce_frames); + void (*set_promisc_mode)(struct hnae_handle *handle, u32 en); + int (*get_mac_addr)(struct hnae_handle *handle, void **p); + int (*set_mac_addr)(struct hnae_handle *handle, void *p); + int (*set_mc_addr)(struct hnae_handle *handle, void *addr); + int (*set_mtu)(struct hnae_handle *handle, int new_mtu); + void (*update_stats)(struct hnae_handle *handle, + struct net_device_stats *net_stats); + void (*get_stats)(struct hnae_handle *handle, u64 *data); + void (*get_strings)(struct hnae_handle *handle, + u32 stringset, u8 *data); + int (*get_sset_count)(struct hnae_handle *handle, int stringset); + void (*update_led_status)(struct hnae_handle *handle); + int (*set_led_id)(struct hnae_handle *handle, + enum hnae_led_state status); + void (*get_regs)(struct hnae_handle *handle, void *data); + int (*get_regs_len)(struct hnae_handle *handle); +}; + +struct hnae_ae_dev { + struct device cls_dev; /* the class dev */ + struct device *dev; /* the presented dev */ + struct hnae_ae_ops *ops; + struct list_head node; + struct module *owner; /* the module who provides this dev */ + int id; + char name[AE_NAME_SIZE]; + struct list_head handle_list; + spinlock_t lock; /* lock to protect the handle_list */ +}; + +struct hnae_handle { + struct device *owner_dev; /* the device which make use of this handle */ + struct hnae_ae_dev *dev; /* the device who provides this handle */ + struct device_node *phy_node; + phy_interface_t phy_if; + u32 if_support; + int q_num; + int vf_id; + u32 eport_id; + enum hnae_port_type port_type; + struct list_head node; /* list to hnae_ae_dev->handle_list */ + struct hnae_buf_ops *bops; /* operation for the buffer */ + struct hnae_queue **qs; /* array base of all queues */ +}; + +#define ring_to_dev(ring) ((ring)->q->dev->dev) + +struct hnae_handle *hnae_get_handle(struct device *owner_dev, const char *ae_id, + u32 port_id, struct hnae_buf_ops *bops); +void hnae_put_handle(struct hnae_handle *handle); +int hnae_ae_register(struct hnae_ae_dev *dev, struct module *owner); +void hnae_ae_unregister(struct hnae_ae_dev *dev); + +int hnae_register_notifier(struct notifier_block *nb); +void hnae_unregister_notifier(struct notifier_block *nb); +int hnae_reinit_handle(struct hnae_handle *handle); + +#define hnae_queue_xmit(q, buf_num) writel_relaxed(buf_num, \ + (q)->tx_ring.io_base + RCB_REG_TAIL) + +#ifndef assert +#define assert(cond) +#endif + +static inline int hnae_reserve_buffer_map(struct hnae_ring *ring, + struct hnae_desc_cb *cb) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + int ret; + + ret = bops->alloc_buffer(ring, cb); + if (ret) + goto out; + + ret = bops->map_buffer(ring, cb); + if (ret) + goto out_with_buf; + + return 0; + +out_with_buf: + bops->free_buffer(ring, cb); +out: + return ret; +} + +static inline int hnae_alloc_buffer_attach(struct hnae_ring *ring, int i) +{ + int ret = hnae_reserve_buffer_map(ring, &ring->desc_cb[i]); + + if (ret) + return ret; + + ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + + return 0; +} + +static inline void hnae_buffer_detach(struct hnae_ring *ring, int i) +{ + ring->q->handle->bops->unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc[i].addr = 0; +} + +static inline void hnae_free_buffer_detach(struct hnae_ring *ring, int i) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + struct hnae_desc_cb *cb = &ring->desc_cb[i]; + + if (!ring->desc_cb[i].dma) + return; + + hnae_buffer_detach(ring, i); + bops->free_buffer(ring, cb); +} + +/* detach a in-used buffer and replace with a reserved one */ +static inline void hnae_replace_buffer(struct hnae_ring *ring, int i, + struct hnae_desc_cb *res_cb) +{ + struct hnae_buf_ops *bops = ring->q->handle->bops; + struct hnae_desc_cb tmp_cb = ring->desc_cb[i]; + + bops->unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc_cb[i] = *res_cb; + *res_cb = tmp_cb; + ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; + ring->desc[i].rx.ipoff_bnum_pid_flag = 0; +} + +static inline void hnae_reuse_buffer(struct hnae_ring *ring, int i) +{ + ring->desc_cb[i].reuse_flag = 0; + ring->desc[i].addr = (__le64)(ring->desc_cb[i].dma + + ring->desc_cb[i].page_offset); + ring->desc[i].rx.ipoff_bnum_pid_flag = 0; +} + +#define hnae_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= ((val) << (shift)) & (mask); \ + } while (0) + +#define hnae_set_bit(origin, shift, val) \ + hnae_set_field((origin), (0x1 << (shift)), (shift), (val)) + +#define hnae_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define hnae_get_bit(origin, shift) \ + hnae_get_field((origin), (0x1 << (shift)), (shift)) + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c new file mode 100644 index 000000000000..1a16c0307b47 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -0,0 +1,783 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/spinlock.h> + +#include "hnae.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_main.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_rcb.h" + +#define AE_NAME_PORT_ID_IDX 6 +#define ETH_STATIC_REG 1 +#define ETH_DUMP_REG 5 +#define ETH_GSTRING_LEN 32 + +static struct hns_mac_cb *hns_get_mac_cb(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + return vf_cb->mac_cb; +} + +/** + * hns_ae_map_eport_to_dport - translate enet port id to dsaf port id + * @port_id: enet port id + *: debug port 0-1, service port 2 -7 (dsaf mode only 2) + * return: dsaf port id + *: service ports 0 - 5, debug port 6-7 + **/ +static int hns_ae_map_eport_to_dport(u32 port_id) +{ + int port_index; + + if (port_id < DSAF_DEBUG_NW_NUM) + port_index = port_id + DSAF_SERVICE_PORT_NUM_PER_DSAF; + else + port_index = port_id - DSAF_DEBUG_NW_NUM; + + return port_index; +} + +static struct dsaf_device *hns_ae_get_dsaf_dev(struct hnae_ae_dev *dev) +{ + return container_of(dev, struct dsaf_device, ae_dev); +} + +static struct hns_ppe_cb *hns_get_ppe_cb(struct hnae_handle *handle) +{ + int ppe_index; + int ppe_common_index; + struct ppe_common_cb *ppe_comm; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + if (vf_cb->port_index < DSAF_SERVICE_PORT_NUM_PER_DSAF) { + ppe_index = vf_cb->port_index; + ppe_common_index = 0; + } else { + ppe_index = 0; + ppe_common_index = + vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1; + } + ppe_comm = vf_cb->dsaf_dev->ppe_common[ppe_common_index]; + return &ppe_comm->ppe_cb[ppe_index]; +} + +static int hns_ae_get_q_num_per_vf( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + + return dsaf_dev->rcb_common[common_idx]->max_q_per_vf; +} + +static int hns_ae_get_vf_num_per_port( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + + return dsaf_dev->rcb_common[common_idx]->max_vfn; +} + +static struct ring_pair_cb *hns_ae_get_base_ring_pair( + struct dsaf_device *dsaf_dev, int port) +{ + int common_idx = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[common_idx]; + int q_num = rcb_comm->max_q_per_vf; + int vf_num = rcb_comm->max_vfn; + + if (common_idx == HNS_DSAF_COMM_SERVICE_NW_IDX) + return &rcb_comm->ring_pair_cb[port * q_num * vf_num]; + else + return &rcb_comm->ring_pair_cb[0]; +} + +static struct ring_pair_cb *hns_ae_get_ring_pair(struct hnae_queue *q) +{ + return container_of(q, struct ring_pair_cb, q); +} + +struct hnae_handle *hns_ae_get_handle(struct hnae_ae_dev *dev, + u32 port_id) +{ + int port_idx; + int vfnum_per_port; + int qnum_per_vf; + int i; + struct dsaf_device *dsaf_dev; + struct hnae_handle *ae_handle; + struct ring_pair_cb *ring_pair_cb; + struct hnae_vf_cb *vf_cb; + + dsaf_dev = hns_ae_get_dsaf_dev(dev); + port_idx = hns_ae_map_eport_to_dport(port_id); + + ring_pair_cb = hns_ae_get_base_ring_pair(dsaf_dev, port_idx); + vfnum_per_port = hns_ae_get_vf_num_per_port(dsaf_dev, port_idx); + qnum_per_vf = hns_ae_get_q_num_per_vf(dsaf_dev, port_idx); + + vf_cb = kzalloc(sizeof(*vf_cb) + + qnum_per_vf * sizeof(struct hnae_queue *), GFP_KERNEL); + if (unlikely(!vf_cb)) { + dev_err(dsaf_dev->dev, "malloc vf_cb fail!\n"); + ae_handle = ERR_PTR(-ENOMEM); + goto handle_err; + } + ae_handle = &vf_cb->ae_handle; + /* ae_handle Init */ + ae_handle->owner_dev = dsaf_dev->dev; + ae_handle->dev = dev; + ae_handle->q_num = qnum_per_vf; + + /* find ring pair, and set vf id*/ + for (ae_handle->vf_id = 0; + ae_handle->vf_id < vfnum_per_port; ae_handle->vf_id++) { + if (!ring_pair_cb->used_by_vf) + break; + ring_pair_cb += qnum_per_vf; + } + if (ae_handle->vf_id >= vfnum_per_port) { + dev_err(dsaf_dev->dev, "malloc queue fail!\n"); + ae_handle = ERR_PTR(-EINVAL); + goto vf_id_err; + } + + ae_handle->qs = (struct hnae_queue **)(&ae_handle->qs + 1); + for (i = 0; i < qnum_per_vf; i++) { + ae_handle->qs[i] = &ring_pair_cb->q; + ae_handle->qs[i]->rx_ring.q = ae_handle->qs[i]; + ae_handle->qs[i]->tx_ring.q = ae_handle->qs[i]; + + ring_pair_cb->used_by_vf = 1; + if (port_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + ring_pair_cb->port_id_in_dsa = port_idx; + else + ring_pair_cb->port_id_in_dsa = 0; + + ring_pair_cb++; + } + + vf_cb->dsaf_dev = dsaf_dev; + vf_cb->port_index = port_idx; + vf_cb->mac_cb = &dsaf_dev->mac_cb[port_idx]; + + ae_handle->phy_if = vf_cb->mac_cb->phy_if; + ae_handle->phy_node = vf_cb->mac_cb->phy_node; + ae_handle->if_support = vf_cb->mac_cb->if_support; + ae_handle->port_type = vf_cb->mac_cb->mac_type; + + return ae_handle; +vf_id_err: + kfree(vf_cb); +handle_err: + return ae_handle; +} + +static void hns_ae_put_handle(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + int i; + + vf_cb->mac_cb = NULL; + + kfree(vf_cb); + + for (i = 0; i < handle->q_num; i++) + hns_ae_get_ring_pair(handle->qs[i])->used_by_vf = 0; +} + +static void hns_ae_ring_enable_all(struct hnae_handle *handle, int val) +{ + int q_num = handle->q_num; + int i; + + for (i = 0; i < q_num; i++) + hns_rcb_ring_enable_hw(handle->qs[i], val); +} + +static void hns_ae_init_queue(struct hnae_queue *q) +{ + struct ring_pair_cb *ring = + container_of(q, struct ring_pair_cb, q); + + hns_rcb_init_hw(ring); +} + +static void hns_ae_fini_queue(struct hnae_queue *q) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(q->handle); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_rcb_reset_ring_hw(q); +} + +static int hns_ae_set_mac_address(struct hnae_handle *handle, void *p) +{ + int ret; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + if (!p || !is_valid_ether_addr((const u8 *)p)) { + dev_err(handle->owner_dev, "is not valid ether addr !\n"); + return -EADDRNOTAVAIL; + } + + ret = hns_mac_change_vf_addr(mac_cb, handle->vf_id, p); + if (ret != 0) { + dev_err(handle->owner_dev, + "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr) +{ + int ret; + char *mac_addr = (char *)addr; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + assert(mac_cb); + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return 0; + + ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, ENABLE); + if (ret) { + dev_err(handle->owner_dev, + "mac add mul_mac:%pM port%d fail, ret = %#x!\n", + mac_addr, mac_cb->mac_id, ret); + return ret; + } + + ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM, + mac_addr, ENABLE); + if (ret) + dev_err(handle->owner_dev, + "mac add mul_mac:%pM port%d fail, ret = %#x!\n", + mac_addr, DSAF_BASE_INNER_PORT_NUM, ret); + + return ret; +} + +static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + return hns_mac_set_mtu(mac_cb, new_mtu); +} + +static int hns_ae_start(struct hnae_handle *handle) +{ + int ret; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + ret = hns_mac_vm_config_bc_en(mac_cb, 0, ENABLE); + if (ret) + return ret; + + hns_ae_ring_enable_all(handle, 1); + msleep(100); + + hns_mac_start(mac_cb); + + return 0; +} + +void hns_ae_stop(struct hnae_handle *handle) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + /* just clean tx fbd, neednot rx fbd*/ + hns_rcb_wait_fbd_clean(handle->qs, handle->q_num, RCB_INT_FLAG_TX); + + msleep(20); + + hns_mac_stop(mac_cb); + + usleep_range(10000, 20000); + + hns_ae_ring_enable_all(handle, 0); + + (void)hns_mac_vm_config_bc_en(mac_cb, 0, DISABLE); +} + +static void hns_ae_reset(struct hnae_handle *handle) +{ + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_DEBUG) { + u8 ppe_common_index = + vf_cb->port_index - DSAF_SERVICE_PORT_NUM_PER_DSAF + 1; + + hns_mac_reset(vf_cb->mac_cb); + hns_ppe_reset_common(vf_cb->dsaf_dev, ppe_common_index); + } +} + +void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask) +{ + u32 flag; + + if (is_tx_ring(ring)) + flag = RCB_INT_FLAG_TX; + else + flag = RCB_INT_FLAG_RX; + + hns_rcb_int_clr_hw(ring->q, flag); + hns_rcb_int_ctrl_hw(ring->q, flag, mask); +} + +static void hns_ae_toggle_queue_status(struct hnae_queue *queue, u32 val) +{ + hns_rcb_start(queue, val); +} + +static int hns_ae_get_link_status(struct hnae_handle *handle) +{ + u32 link_status; + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + hns_mac_get_link_status(mac_cb, &link_status); + + return !!link_status; +} + +static int hns_ae_get_mac_info(struct hnae_handle *handle, + u8 *auto_neg, u16 *speed, u8 *duplex) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + return hns_mac_get_port_info(mac_cb, auto_neg, speed, duplex); +} + +static void hns_ae_adjust_link(struct hnae_handle *handle, int speed, + int duplex) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + + hns_mac_adjust_link(mac_cb, speed, duplex); +} + +static void hns_ae_get_ring_bdnum_limit(struct hnae_queue *queue, + u32 *uplimit) +{ + *uplimit = HNS_RCB_RING_MAX_PENDING_BD; +} + +static void hns_ae_get_pauseparam(struct hnae_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en) +{ + assert(handle); + + hns_mac_get_autoneg(hns_get_mac_cb(handle), auto_neg); + + hns_mac_get_pauseparam(hns_get_mac_cb(handle), rx_en, tx_en); +} + +static int hns_ae_set_autoneg(struct hnae_handle *handle, u8 enable) +{ + assert(handle); + + return hns_mac_set_autoneg(hns_get_mac_cb(handle), enable); +} + +static void hns_ae_set_promisc_mode(struct hnae_handle *handle, u32 en) +{ + hns_dsaf_set_promisc_mode(hns_ae_get_dsaf_dev(handle->dev), en); +} + +static int hns_ae_get_autoneg(struct hnae_handle *handle) +{ + u32 auto_neg; + + assert(handle); + + hns_mac_get_autoneg(hns_get_mac_cb(handle), &auto_neg); + + return auto_neg; +} + +static int hns_ae_set_pauseparam(struct hnae_handle *handle, + u32 autoneg, u32 rx_en, u32 tx_en) +{ + struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + int ret; + + ret = hns_mac_set_autoneg(mac_cb, autoneg); + if (ret) + return ret; + + return hns_mac_set_pauseparam(mac_cb, rx_en, tx_en); +} + +static void hns_ae_get_coalesce_usecs(struct hnae_handle *handle, + u32 *tx_usecs, u32 *rx_usecs) +{ + int port; + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + *tx_usecs = hns_rcb_get_coalesce_usecs( + hns_ae_get_dsaf_dev(handle->dev), + hns_dsaf_get_comm_idx_by_port(port)); + *rx_usecs = hns_rcb_get_coalesce_usecs( + hns_ae_get_dsaf_dev(handle->dev), + hns_dsaf_get_comm_idx_by_port(port)); +} + +static void hns_ae_get_rx_max_coalesced_frames(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames) +{ + int port; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + *tx_frames = hns_rcb_get_coalesced_frames( + hns_ae_get_dsaf_dev(handle->dev), port); + *rx_frames = hns_rcb_get_coalesced_frames( + hns_ae_get_dsaf_dev(handle->dev), port); +} + +static void hns_ae_set_coalesce_usecs(struct hnae_handle *handle, + u32 timeout) +{ + int port; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + hns_rcb_set_coalesce_usecs(hns_ae_get_dsaf_dev(handle->dev), + port, timeout); +} + +static int hns_ae_set_coalesce_frames(struct hnae_handle *handle, + u32 coalesce_frames) +{ + int port; + int ret; + + assert(handle); + + port = hns_ae_map_eport_to_dport(handle->eport_id); + + ret = hns_rcb_set_coalesced_frames(hns_ae_get_dsaf_dev(handle->dev), + port, coalesce_frames); + return ret; +} + +void hns_ae_update_stats(struct hnae_handle *handle, + struct net_device_stats *net_stats) +{ + int port; + int idx; + struct dsaf_device *dsaf_dev; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + struct hnae_queue *queue; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + u64 tx_bytes = 0, rx_bytes = 0, tx_packets = 0, rx_packets = 0; + u64 rx_errors = 0, tx_errors = 0, tx_dropped = 0; + u64 rx_missed_errors = 0; + + dsaf_dev = hns_ae_get_dsaf_dev(handle->dev); + if (!dsaf_dev) + return; + port = vf_cb->port_index; + ppe_cb = hns_get_ppe_cb(handle); + mac_cb = hns_get_mac_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + queue = handle->qs[idx]; + hns_rcb_update_stats(queue); + + tx_bytes += queue->tx_ring.stats.tx_bytes; + tx_packets += queue->tx_ring.stats.tx_pkts; + rx_bytes += queue->rx_ring.stats.rx_bytes; + rx_packets += queue->rx_ring.stats.rx_pkts; + + rx_errors += queue->rx_ring.stats.err_pkt_len + + queue->rx_ring.stats.l2_err + + queue->rx_ring.stats.l3l4_csum_err; + } + + hns_ppe_update_stats(ppe_cb); + rx_missed_errors = ppe_cb->hw_stats.rx_drop_no_buf; + tx_errors += ppe_cb->hw_stats.tx_err_checksum + + ppe_cb->hw_stats.tx_err_fifo_empty; + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) { + hns_dsaf_update_stats(dsaf_dev, port); + /* for port upline direction, i.e., rx. */ + rx_missed_errors += dsaf_dev->hw_stats[port].bp_drop; + rx_missed_errors += dsaf_dev->hw_stats[port].pad_drop; + rx_missed_errors += dsaf_dev->hw_stats[port].crc_false; + + /* for port downline direction, i.e., tx. */ + port = port + DSAF_PPE_INODE_BASE; + hns_dsaf_update_stats(dsaf_dev, port); + tx_dropped += dsaf_dev->hw_stats[port].bp_drop; + tx_dropped += dsaf_dev->hw_stats[port].pad_drop; + tx_dropped += dsaf_dev->hw_stats[port].crc_false; + tx_dropped += dsaf_dev->hw_stats[port].rslt_drop; + tx_dropped += dsaf_dev->hw_stats[port].vlan_drop; + tx_dropped += dsaf_dev->hw_stats[port].stp_drop; + } + + hns_mac_update_stats(mac_cb); + rx_errors += mac_cb->hw_stats.rx_fifo_overrun_err; + + tx_errors += mac_cb->hw_stats.tx_bad_pkts + + mac_cb->hw_stats.tx_fragment_err + + mac_cb->hw_stats.tx_jabber_err + + mac_cb->hw_stats.tx_underrun_err + + mac_cb->hw_stats.tx_crc_err; + + net_stats->tx_bytes = tx_bytes; + net_stats->tx_packets = tx_packets; + net_stats->rx_bytes = rx_bytes; + net_stats->rx_dropped = 0; + net_stats->rx_packets = rx_packets; + net_stats->rx_errors = rx_errors; + net_stats->tx_errors = tx_errors; + net_stats->tx_dropped = tx_dropped; + net_stats->rx_missed_errors = rx_missed_errors; + net_stats->rx_crc_errors = mac_cb->hw_stats.rx_fcs_err; + net_stats->rx_frame_errors = mac_cb->hw_stats.rx_align_err; + net_stats->rx_fifo_errors = mac_cb->hw_stats.rx_fifo_overrun_err; + net_stats->rx_length_errors = mac_cb->hw_stats.rx_len_err; + net_stats->multicast = mac_cb->hw_stats.rx_mc_pkts; +} + +void hns_ae_get_stats(struct hnae_handle *handle, u64 *data) +{ + int idx; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + u64 *p = data; + struct hnae_vf_cb *vf_cb; + + if (!handle || !data) { + pr_err("hns_ae_get_stats NULL handle or data pointer!\n"); + return; + } + + vf_cb = hns_ae_get_vf_cb(handle); + mac_cb = hns_get_mac_cb(handle); + ppe_cb = hns_get_ppe_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + hns_rcb_get_stats(handle->qs[idx], p); + p += hns_rcb_get_ring_sset_count((int)ETH_SS_STATS); + } + + hns_ppe_get_stats(ppe_cb, p); + p += hns_ppe_get_sset_count((int)ETH_SS_STATS); + + hns_mac_get_stats(mac_cb, p); + p += hns_mac_get_sset_count(mac_cb, (int)ETH_SS_STATS); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_stats(vf_cb->dsaf_dev, p, vf_cb->port_index); +} + +void hns_ae_get_strings(struct hnae_handle *handle, + u32 stringset, u8 *data) +{ + int port; + int idx; + struct hns_mac_cb *mac_cb; + struct hns_ppe_cb *ppe_cb; + u8 *p = data; + struct hnae_vf_cb *vf_cb; + + assert(handle); + + vf_cb = hns_ae_get_vf_cb(handle); + port = vf_cb->port_index; + mac_cb = hns_get_mac_cb(handle); + ppe_cb = hns_get_ppe_cb(handle); + + for (idx = 0; idx < handle->q_num; idx++) { + hns_rcb_get_strings(stringset, p, idx); + p += ETH_GSTRING_LEN * hns_rcb_get_ring_sset_count(stringset); + } + + hns_ppe_get_strings(ppe_cb, stringset, p); + p += ETH_GSTRING_LEN * hns_ppe_get_sset_count(stringset); + + hns_mac_get_strings(mac_cb, stringset, p); + p += ETH_GSTRING_LEN * hns_mac_get_sset_count(mac_cb, stringset); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_strings(stringset, p, port); +} + +int hns_ae_get_sset_count(struct hnae_handle *handle, int stringset) +{ + u32 sset_count = 0; + struct hns_mac_cb *mac_cb; + + assert(handle); + + mac_cb = hns_get_mac_cb(handle); + + sset_count += hns_rcb_get_ring_sset_count(stringset) * handle->q_num; + sset_count += hns_ppe_get_sset_count(stringset); + sset_count += hns_mac_get_sset_count(mac_cb, stringset); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + sset_count += hns_dsaf_get_sset_count(stringset); + + return sset_count; +} + +static int hns_ae_config_loopback(struct hnae_handle *handle, + enum hnae_loop loop, int en) +{ + int ret; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + switch (loop) { + case MAC_INTERNALLOOP_SERDES: + ret = hns_mac_config_sds_loopback(vf_cb->mac_cb, en); + break; + case MAC_INTERNALLOOP_MAC: + ret = hns_mac_config_mac_loopback(vf_cb->mac_cb, loop, en); + break; + default: + ret = -EINVAL; + } + return ret; +} + +void hns_ae_update_led_status(struct hnae_handle *handle) +{ + struct hns_mac_cb *mac_cb; + + assert(handle); + mac_cb = hns_get_mac_cb(handle); + if (!mac_cb->cpld_vaddr) + return; + hns_set_led_opt(mac_cb); +} + +int hns_ae_cpld_set_led_id(struct hnae_handle *handle, + enum hnae_led_state status) +{ + struct hns_mac_cb *mac_cb; + + assert(handle); + + mac_cb = hns_get_mac_cb(handle); + + return hns_cpld_led_set_id(mac_cb, status); +} + +void hns_ae_get_regs(struct hnae_handle *handle, void *data) +{ + u32 *p = data; + u32 rcb_com_idx; + int i; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle); + + hns_ppe_get_regs(ppe_cb, p); + p += hns_ppe_get_regs_count(); + + rcb_com_idx = hns_dsaf_get_comm_idx_by_port(vf_cb->port_index); + hns_rcb_get_common_regs(vf_cb->dsaf_dev->rcb_common[rcb_com_idx], p); + p += hns_rcb_get_common_regs_count(); + + for (i = 0; i < handle->q_num; i++) { + hns_rcb_get_ring_regs(handle->qs[i], p); + p += hns_rcb_get_ring_regs_count(); + } + + hns_mac_get_regs(vf_cb->mac_cb, p); + p += hns_mac_get_regs_count(vf_cb->mac_cb); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + hns_dsaf_get_regs(vf_cb->dsaf_dev, vf_cb->port_index, p); +} + +int hns_ae_get_regs_len(struct hnae_handle *handle) +{ + u32 total_num; + struct hnae_vf_cb *vf_cb = hns_ae_get_vf_cb(handle); + + total_num = hns_ppe_get_regs_count(); + total_num += hns_rcb_get_common_regs_count(); + total_num += hns_rcb_get_ring_regs_count() * handle->q_num; + total_num += hns_mac_get_regs_count(vf_cb->mac_cb); + + if (vf_cb->mac_cb->mac_type == HNAE_PORT_SERVICE) + total_num += hns_dsaf_get_regs_count(); + + return total_num; +} + +static struct hnae_ae_ops hns_dsaf_ops = { + .get_handle = hns_ae_get_handle, + .put_handle = hns_ae_put_handle, + .init_queue = hns_ae_init_queue, + .fini_queue = hns_ae_fini_queue, + .start = hns_ae_start, + .stop = hns_ae_stop, + .reset = hns_ae_reset, + .toggle_ring_irq = hns_ae_toggle_ring_irq, + .toggle_queue_status = hns_ae_toggle_queue_status, + .get_status = hns_ae_get_link_status, + .get_info = hns_ae_get_mac_info, + .adjust_link = hns_ae_adjust_link, + .set_loopback = hns_ae_config_loopback, + .get_ring_bdnum_limit = hns_ae_get_ring_bdnum_limit, + .get_pauseparam = hns_ae_get_pauseparam, + .set_autoneg = hns_ae_set_autoneg, + .get_autoneg = hns_ae_get_autoneg, + .set_pauseparam = hns_ae_set_pauseparam, + .get_coalesce_usecs = hns_ae_get_coalesce_usecs, + .get_rx_max_coalesced_frames = hns_ae_get_rx_max_coalesced_frames, + .set_coalesce_usecs = hns_ae_set_coalesce_usecs, + .set_coalesce_frames = hns_ae_set_coalesce_frames, + .set_promisc_mode = hns_ae_set_promisc_mode, + .set_mac_addr = hns_ae_set_mac_address, + .set_mc_addr = hns_ae_set_multicast_one, + .set_mtu = hns_ae_set_mtu, + .update_stats = hns_ae_update_stats, + .get_stats = hns_ae_get_stats, + .get_strings = hns_ae_get_strings, + .get_sset_count = hns_ae_get_sset_count, + .update_led_status = hns_ae_update_led_status, + .set_led_id = hns_ae_cpld_set_led_id, + .get_regs = hns_ae_get_regs, + .get_regs_len = hns_ae_get_regs_len +}; + +int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev) +{ + struct hnae_ae_dev *ae_dev = &dsaf_dev->ae_dev; + + ae_dev->ops = &hns_dsaf_ops; + ae_dev->dev = dsaf_dev->dev; + + return hnae_ae_register(ae_dev, THIS_MODULE); +} + +void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev) +{ + hnae_ae_unregister(&dsaf_dev->ae_dev); +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c new file mode 100644 index 000000000000..b8517b00e706 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/of_mdio.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_gmac.h" + +static const struct mac_stats_string g_gmac_stats_string[] = { + {"gmac_rx_octets_total_ok", MAC_STATS_FIELD_OFF(rx_good_bytes)}, + {"gmac_rx_octets_bad", MAC_STATS_FIELD_OFF(rx_bad_bytes)}, + {"gmac_rx_uc_pkts", MAC_STATS_FIELD_OFF(rx_uc_pkts)}, + {"gamc_rx_mc_pkts", MAC_STATS_FIELD_OFF(rx_mc_pkts)}, + {"gmac_rx_bc_pkts", MAC_STATS_FIELD_OFF(rx_bc_pkts)}, + {"gmac_rx_pkts_64octets", MAC_STATS_FIELD_OFF(rx_64bytes)}, + {"gmac_rx_pkts_65to127", MAC_STATS_FIELD_OFF(rx_65to127)}, + {"gmac_rx_pkts_128to255", MAC_STATS_FIELD_OFF(rx_128to255)}, + {"gmac_rx_pkts_256to511", MAC_STATS_FIELD_OFF(rx_256to511)}, + {"gmac_rx_pkts_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)}, + {"gmac_rx_pkts_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)}, + {"gmac_rx_pkts_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)}, + {"gmac_rx_fcs_errors", MAC_STATS_FIELD_OFF(rx_fcs_err)}, + {"gmac_rx_tagged", MAC_STATS_FIELD_OFF(rx_vlan_pkts)}, + {"gmac_rx_data_err", MAC_STATS_FIELD_OFF(rx_data_err)}, + {"gmac_rx_align_errors", MAC_STATS_FIELD_OFF(rx_align_err)}, + {"gmac_rx_long_errors", MAC_STATS_FIELD_OFF(rx_oversize)}, + {"gmac_rx_jabber_errors", MAC_STATS_FIELD_OFF(rx_jabber_err)}, + {"gmac_rx_pause_maccontrol", MAC_STATS_FIELD_OFF(rx_pfc_tc0)}, + {"gmac_rx_unknown_maccontrol", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)}, + {"gmac_rx_very_long_err", MAC_STATS_FIELD_OFF(rx_long_err)}, + {"gmac_rx_runt_err", MAC_STATS_FIELD_OFF(rx_minto64)}, + {"gmac_rx_short_err", MAC_STATS_FIELD_OFF(rx_under_min)}, + {"gmac_rx_filt_pkt", MAC_STATS_FIELD_OFF(rx_filter_bytes)}, + {"gmac_rx_octets_total_filt", MAC_STATS_FIELD_OFF(rx_filter_pkts)}, + {"gmac_rx_overrun_cnt", MAC_STATS_FIELD_OFF(rx_fifo_overrun_err)}, + {"gmac_rx_length_err", MAC_STATS_FIELD_OFF(rx_len_err)}, + {"gmac_rx_fail_comma", MAC_STATS_FIELD_OFF(rx_comma_err)}, + + {"gmac_tx_octets_ok", MAC_STATS_FIELD_OFF(tx_good_bytes)}, + {"gmac_tx_octets_bad", MAC_STATS_FIELD_OFF(tx_bad_bytes)}, + {"gmac_tx_uc_pkts", MAC_STATS_FIELD_OFF(tx_uc_pkts)}, + {"gmac_tx_mc_pkts", MAC_STATS_FIELD_OFF(tx_mc_pkts)}, + {"gmac_tx_bc_pkts", MAC_STATS_FIELD_OFF(tx_bc_pkts)}, + {"gmac_tx_pkts_64octets", MAC_STATS_FIELD_OFF(tx_64bytes)}, + {"gmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)}, + {"gmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)}, + {"gmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)}, + {"gmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)}, + {"gmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)}, + {"gmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)}, + {"gmac_tx_excessive_length_drop", MAC_STATS_FIELD_OFF(tx_jabber_err)}, + {"gmac_tx_underrun", MAC_STATS_FIELD_OFF(tx_underrun_err)}, + {"gmac_tx_tagged", MAC_STATS_FIELD_OFF(tx_vlan)}, + {"gmac_tx_crc_error", MAC_STATS_FIELD_OFF(tx_crc_err)}, + {"gmac_tx_pause_frames", MAC_STATS_FIELD_OFF(tx_pfc_tc0)} +}; + +static void hns_gmac_enable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /*enable GE rX/tX */ + if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 1); + + if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 1); +} + +static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /*disable GE rX/tX */ + if ((mode == MAC_COMM_MODE_TX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_TX_EN_B, 0); + + if ((mode == MAC_COMM_MODE_RX) || (mode == MAC_COMM_MODE_RX_AND_TX)) + dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0); +} + +/** +*hns_gmac_get_en - get port enable +*@mac_drv:mac device +*@rx:rx enable +*@tx:tx enable +*/ +static void hns_gmac_get_en(void *mac_drv, u32 *rx, u32 *tx) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 porten; + + porten = dsaf_read_dev(drv, GMAC_PORT_EN_REG); + *tx = dsaf_get_bit(porten, GMAC_PORT_TX_EN_B); + *rx = dsaf_get_bit(porten, GMAC_PORT_RX_EN_B); +} + +static void hns_gmac_free(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + u32 mac_id = drv->mac_id; + + hns_dsaf_ge_srst_by_port(dsaf_dev, mac_id, 0); +} + +static void hns_gmac_set_tx_auto_pause_frames(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_field(drv, GMAC_FC_TX_TIMER_REG, GMAC_FC_TX_TIMER_M, + GMAC_FC_TX_TIMER_S, newval); +} + +static void hns_gmac_get_tx_auto_pause_frames(void *mac_drv, u16 *newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *newval = dsaf_get_dev_field(drv, GMAC_FC_TX_TIMER_REG, + GMAC_FC_TX_TIMER_M, GMAC_FC_TX_TIMER_S); +} + +static void hns_gmac_set_rx_auto_pause_frames(void *mac_drv, u32 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_PAUSE_EN_REG, + GMAC_PAUSE_EN_RX_FDFC_B, !!newval); +} + +static void hns_gmac_config_max_frame_length(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_field(drv, GMAC_MAX_FRM_SIZE_REG, GMAC_MAX_FRM_SIZE_M, + GMAC_MAX_FRM_SIZE_S, newval); + + dsaf_set_dev_field(drv, GAMC_RX_MAX_FRAME, GMAC_MAX_FRM_SIZE_M, + GMAC_MAX_FRM_SIZE_S, newval); +} + +static void hns_gmac_config_an_mode(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG, + GMAC_TX_AN_EN_B, !!newval); +} + +static void hns_gmac_tx_loop_pkt_dis(void *mac_drv) +{ + u32 tx_loop_pkt_pri; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + tx_loop_pkt_pri = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG); + dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_EN_B, 1); + dsaf_set_bit(tx_loop_pkt_pri, GMAC_TX_LOOP_PKT_HIG_PRI_B, 0); + dsaf_write_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG, tx_loop_pkt_pri); +} + +static void hns_gmac_set_duplex_type(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG, + GMAC_DUPLEX_TYPE_B, !!newval); +} + +static void hns_gmac_get_duplex_type(void *mac_drv, + enum hns_gmac_duplex_mdoe *duplex_mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *duplex_mode = (enum hns_gmac_duplex_mdoe)dsaf_get_dev_bit( + drv, GMAC_DUPLEX_TYPE_REG, GMAC_DUPLEX_TYPE_B); +} + +static void hns_gmac_get_port_mode(void *mac_drv, enum hns_port_mode *port_mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *port_mode = (enum hns_port_mode)dsaf_get_dev_field( + drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S); +} + +static void hns_gmac_port_mode_get(void *mac_drv, + struct hns_gmac_port_mode_cfg *port_mode) +{ + u32 tx_ctrl; + u32 recv_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + port_mode->port_mode = (enum hns_port_mode)dsaf_get_dev_field( + drv, GMAC_PORT_MODE_REG, GMAC_PORT_MODE_M, GMAC_PORT_MODE_S); + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + recv_ctrl = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG); + + port_mode->max_frm_size = + dsaf_get_dev_field(drv, GMAC_MAX_FRM_SIZE_REG, + GMAC_MAX_FRM_SIZE_M, GMAC_MAX_FRM_SIZE_S); + port_mode->short_runts_thr = + dsaf_get_dev_field(drv, GMAC_SHORT_RUNTS_THR_REG, + GMAC_SHORT_RUNTS_THR_M, + GMAC_SHORT_RUNTS_THR_S); + + port_mode->pad_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_PAD_EN_B); + port_mode->crc_add = dsaf_get_bit(tx_ctrl, GMAC_TX_CRC_ADD_B); + port_mode->an_enable = dsaf_get_bit(tx_ctrl, GMAC_TX_AN_EN_B); + + port_mode->runt_pkt_en = + dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_RUNT_PKT_EN_B); + port_mode->strip_pad_en = + dsaf_get_bit(recv_ctrl, GMAC_RECV_CTRL_STRIP_PAD_EN_B); +} + +static void hns_gmac_pause_frm_cfg(void *mac_drv, u32 rx_pause_en, + u32 tx_pause_en) +{ + u32 pause_en; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + dsaf_set_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B, !!rx_pause_en); + dsaf_set_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B, !!tx_pause_en); + dsaf_write_dev(drv, GMAC_PAUSE_EN_REG, pause_en); +} + +static void hns_gmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_pause_en, + u32 *tx_pause_en) +{ + u32 pause_en; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + pause_en = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + + *rx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_RX_FDFC_B); + *tx_pause_en = dsaf_get_bit(pause_en, GMAC_PAUSE_EN_TX_FDFC_B); +} + +static int hns_gmac_adjust_link(void *mac_drv, enum mac_speed speed, + u32 full_duplex) +{ + u32 tx_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG, + GMAC_DUPLEX_TYPE_B, !!full_duplex); + + switch (speed) { + case MAC_SPEED_10: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x6); + break; + case MAC_SPEED_100: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x7); + break; + case MAC_SPEED_1000: + dsaf_set_dev_field( + drv, GMAC_PORT_MODE_REG, + GMAC_PORT_MODE_M, GMAC_PORT_MODE_S, 0x8); + break; + default: + dev_err(drv->dev, + "hns_gmac_adjust_link fail, speed%d mac%d\n", + speed, drv->mac_id); + return -EINVAL; + } + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, 1); + dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, 1); + dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); + + dsaf_set_dev_bit(drv, GMAC_MODE_CHANGE_EN_REG, + GMAC_MODE_CHANGE_EB_B, 1); + + return 0; +} + +static void hns_gmac_init(void *mac_drv) +{ + u32 port; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + port = drv->mac_id; + + hns_dsaf_ge_srst_by_port(dsaf_dev, port, 0); + mdelay(10); + hns_dsaf_ge_srst_by_port(dsaf_dev, port, 1); + mdelay(10); + hns_gmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX); + hns_gmac_tx_loop_pkt_dis(mac_drv); +} + +void hns_gmac_update_stats(void *mac_drv) +{ + struct mac_hw_stats *hw_stats = NULL; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + hw_stats = &drv->mac_cb->hw_stats; + + /* RX */ + hw_stats->rx_good_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG); + hw_stats->rx_bad_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG); + hw_stats->rx_uc_pkts += dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG); + hw_stats->rx_mc_pkts += dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG); + hw_stats->rx_bc_pkts += dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG); + hw_stats->rx_64bytes + += dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG); + hw_stats->rx_65to127 + += dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG); + hw_stats->rx_128to255 + += dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG); + hw_stats->rx_256to511 + += dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG); + hw_stats->rx_512to1023 + += dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG); + hw_stats->rx_1024to1518 + += dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG); + hw_stats->rx_1519tomax + += dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG); + hw_stats->rx_fcs_err += dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG); + hw_stats->rx_vlan_pkts += dsaf_read_dev(drv, GMAC_RX_TAGGED_REG); + hw_stats->rx_data_err += dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG); + hw_stats->rx_align_err + += dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG); + hw_stats->rx_oversize + += dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG); + hw_stats->rx_jabber_err + += dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG); + hw_stats->rx_pfc_tc0 + += dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG); + hw_stats->rx_unknown_ctrl + += dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG); + hw_stats->rx_long_err + += dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG); + hw_stats->rx_minto64 + += dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG); + hw_stats->rx_under_min + += dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG); + hw_stats->rx_filter_pkts + += dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG); + hw_stats->rx_filter_bytes + += dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG); + hw_stats->rx_fifo_overrun_err + += dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG); + hw_stats->rx_len_err + += dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG); + hw_stats->rx_comma_err + += dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG); + + /* TX */ + hw_stats->tx_good_bytes + += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG); + hw_stats->tx_bad_bytes + += dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG); + hw_stats->tx_uc_pkts += dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG); + hw_stats->tx_mc_pkts += dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG); + hw_stats->tx_bc_pkts += dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG); + hw_stats->tx_64bytes + += dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG); + hw_stats->tx_65to127 + += dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG); + hw_stats->tx_128to255 + += dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG); + hw_stats->tx_256to511 + += dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG); + hw_stats->tx_512to1023 + += dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG); + hw_stats->tx_1024to1518 + += dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG); + hw_stats->tx_1519tomax + += dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG); + hw_stats->tx_jabber_err + += dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG); + hw_stats->tx_underrun_err + += dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG); + hw_stats->tx_vlan += dsaf_read_dev(drv, GMAC_TX_TAGGED_REG); + hw_stats->tx_crc_err += dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG); + hw_stats->tx_pfc_tc0 + += dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG); +} + +static void hns_gmac_set_mac_addr(void *mac_drv, char *mac_addr) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + if (drv->mac_id >= DSAF_SERVICE_NW_NUM) { + u32 high_val = mac_addr[1] | (mac_addr[0] << 8); + + u32 low_val = mac_addr[5] | (mac_addr[4] << 8) + | (mac_addr[3] << 16) | (mac_addr[2] << 24); + dsaf_write_dev(drv, GMAC_STATION_ADDR_LOW_2_REG, low_val); + dsaf_write_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG, high_val); + } +} + +static int hns_gmac_config_loopback(void *mac_drv, enum hnae_loop loop_mode, + u8 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + switch (loop_mode) { + case MAC_INTERNALLOOP_MAC: + dsaf_set_dev_bit(drv, GMAC_LOOP_REG, GMAC_LP_REG_CF2MI_LP_EN_B, + !!enable); + break; + default: + dev_err(drv->dev, "loop_mode error\n"); + return -EINVAL; + } + + return 0; +} + +static void hns_gmac_config_pad_and_crc(void *mac_drv, u8 newval) +{ + u32 tx_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, !!newval); + dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, !!newval); + dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); +} + +static void hns_gmac_get_id(void *mac_drv, u8 *mac_id) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *mac_id = drv->mac_id; +} + +static void hns_gmac_get_info(void *mac_drv, struct mac_info *mac_info) +{ + enum hns_gmac_duplex_mdoe duplex; + enum hns_port_mode speed; + u32 rx_pause; + u32 tx_pause; + u32 rx; + u32 tx; + u16 fc_tx_timer; + struct hns_gmac_port_mode_cfg port_mode = { GMAC_10M_MII, 0 }; + + hns_gmac_port_mode_get(mac_drv, &port_mode); + mac_info->pad_and_crc_en = port_mode.crc_add && port_mode.pad_enable; + mac_info->auto_neg = port_mode.an_enable; + + hns_gmac_get_tx_auto_pause_frames(mac_drv, &fc_tx_timer); + mac_info->tx_pause_time = fc_tx_timer; + + hns_gmac_get_en(mac_drv, &rx, &tx); + mac_info->port_en = rx && tx; + + hns_gmac_get_duplex_type(mac_drv, &duplex); + mac_info->duplex = duplex; + + hns_gmac_get_port_mode(mac_drv, &speed); + switch (speed) { + case GMAC_10M_SGMII: + mac_info->speed = MAC_SPEED_10; + break; + case GMAC_100M_SGMII: + mac_info->speed = MAC_SPEED_100; + break; + case GMAC_1000M_SGMII: + mac_info->speed = MAC_SPEED_1000; + break; + default: + mac_info->speed = 0; + break; + } + + hns_gmac_get_pausefrm_cfg(mac_drv, &rx_pause, &tx_pause); + mac_info->rx_pause_en = rx_pause; + mac_info->tx_pause_en = tx_pause; +} + +static void hns_gmac_autoneg_stat(void *mac_drv, u32 *enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *enable = dsaf_get_dev_bit(drv, GMAC_TRANSMIT_CONTROL_REG, + GMAC_TX_AN_EN_B); +} + +static void hns_gmac_get_link_status(void *mac_drv, u32 *link_stat) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *link_stat = dsaf_get_dev_bit(drv, GMAC_AN_NEG_STATE_REG, + GMAC_AN_NEG_STAT_RX_SYNC_OK_B); +} + +static void hns_gmac_get_regs(void *mac_drv, void *data) +{ + u32 *regs = data; + int i; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + /* base config registers */ + regs[0] = dsaf_read_dev(drv, GMAC_DUPLEX_TYPE_REG); + regs[1] = dsaf_read_dev(drv, GMAC_FD_FC_TYPE_REG); + regs[2] = dsaf_read_dev(drv, GMAC_FC_TX_TIMER_REG); + regs[3] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_LOW_REG); + regs[4] = dsaf_read_dev(drv, GMAC_FD_FC_ADDR_HIGH_REG); + regs[5] = dsaf_read_dev(drv, GMAC_IPG_TX_TIMER_REG); + regs[6] = dsaf_read_dev(drv, GMAC_PAUSE_THR_REG); + regs[7] = dsaf_read_dev(drv, GMAC_MAX_FRM_SIZE_REG); + regs[8] = dsaf_read_dev(drv, GMAC_PORT_MODE_REG); + regs[9] = dsaf_read_dev(drv, GMAC_PORT_EN_REG); + regs[10] = dsaf_read_dev(drv, GMAC_PAUSE_EN_REG); + regs[11] = dsaf_read_dev(drv, GMAC_SHORT_RUNTS_THR_REG); + regs[12] = dsaf_read_dev(drv, GMAC_AN_NEG_STATE_REG); + regs[13] = dsaf_read_dev(drv, GMAC_TX_LOCAL_PAGE_REG); + regs[14] = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + regs[15] = dsaf_read_dev(drv, GMAC_REC_FILT_CONTROL_REG); + regs[16] = dsaf_read_dev(drv, GMAC_PTP_CONFIG_REG); + + /* rx static registers */ + regs[17] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_OK_REG); + regs[18] = dsaf_read_dev(drv, GMAC_RX_OCTETS_BAD_REG); + regs[19] = dsaf_read_dev(drv, GMAC_RX_UC_PKTS_REG); + regs[20] = dsaf_read_dev(drv, GMAC_RX_MC_PKTS_REG); + regs[21] = dsaf_read_dev(drv, GMAC_RX_BC_PKTS_REG); + regs[22] = dsaf_read_dev(drv, GMAC_RX_PKTS_64OCTETS_REG); + regs[23] = dsaf_read_dev(drv, GMAC_RX_PKTS_65TO127OCTETS_REG); + regs[24] = dsaf_read_dev(drv, GMAC_RX_PKTS_128TO255OCTETS_REG); + regs[25] = dsaf_read_dev(drv, GMAC_RX_PKTS_255TO511OCTETS_REG); + regs[26] = dsaf_read_dev(drv, GMAC_RX_PKTS_512TO1023OCTETS_REG); + regs[27] = dsaf_read_dev(drv, GMAC_RX_PKTS_1024TO1518OCTETS_REG); + regs[28] = dsaf_read_dev(drv, GMAC_RX_PKTS_1519TOMAXOCTETS_REG); + regs[29] = dsaf_read_dev(drv, GMAC_RX_FCS_ERRORS_REG); + regs[30] = dsaf_read_dev(drv, GMAC_RX_TAGGED_REG); + regs[31] = dsaf_read_dev(drv, GMAC_RX_DATA_ERR_REG); + regs[32] = dsaf_read_dev(drv, GMAC_RX_ALIGN_ERRORS_REG); + regs[33] = dsaf_read_dev(drv, GMAC_RX_LONG_ERRORS_REG); + regs[34] = dsaf_read_dev(drv, GMAC_RX_JABBER_ERRORS_REG); + regs[35] = dsaf_read_dev(drv, GMAC_RX_PAUSE_MACCTRL_FRAM_REG); + regs[36] = dsaf_read_dev(drv, GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG); + regs[37] = dsaf_read_dev(drv, GMAC_RX_VERY_LONG_ERR_CNT_REG); + regs[38] = dsaf_read_dev(drv, GMAC_RX_RUNT_ERR_CNT_REG); + regs[39] = dsaf_read_dev(drv, GMAC_RX_SHORT_ERR_CNT_REG); + regs[40] = dsaf_read_dev(drv, GMAC_RX_FILT_PKT_CNT_REG); + regs[41] = dsaf_read_dev(drv, GMAC_RX_OCTETS_TOTAL_FILT_REG); + + /* tx static registers */ + regs[42] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_OK_REG); + regs[43] = dsaf_read_dev(drv, GMAC_OCTETS_TRANSMITTED_BAD_REG); + regs[44] = dsaf_read_dev(drv, GMAC_TX_UC_PKTS_REG); + regs[45] = dsaf_read_dev(drv, GMAC_TX_MC_PKTS_REG); + regs[46] = dsaf_read_dev(drv, GMAC_TX_BC_PKTS_REG); + regs[47] = dsaf_read_dev(drv, GMAC_TX_PKTS_64OCTETS_REG); + regs[48] = dsaf_read_dev(drv, GMAC_TX_PKTS_65TO127OCTETS_REG); + regs[49] = dsaf_read_dev(drv, GMAC_TX_PKTS_128TO255OCTETS_REG); + regs[50] = dsaf_read_dev(drv, GMAC_TX_PKTS_255TO511OCTETS_REG); + regs[51] = dsaf_read_dev(drv, GMAC_TX_PKTS_512TO1023OCTETS_REG); + regs[52] = dsaf_read_dev(drv, GMAC_TX_PKTS_1024TO1518OCTETS_REG); + regs[53] = dsaf_read_dev(drv, GMAC_TX_PKTS_1519TOMAXOCTETS_REG); + regs[54] = dsaf_read_dev(drv, GMAC_TX_EXCESSIVE_LENGTH_DROP_REG); + regs[55] = dsaf_read_dev(drv, GMAC_TX_UNDERRUN_REG); + regs[56] = dsaf_read_dev(drv, GMAC_TX_TAGGED_REG); + regs[57] = dsaf_read_dev(drv, GMAC_TX_CRC_ERROR_REG); + regs[58] = dsaf_read_dev(drv, GMAC_TX_PAUSE_FRAMES_REG); + + regs[59] = dsaf_read_dev(drv, GAMC_RX_MAX_FRAME); + regs[60] = dsaf_read_dev(drv, GMAC_LINE_LOOP_BACK_REG); + regs[61] = dsaf_read_dev(drv, GMAC_CF_CRC_STRIP_REG); + regs[62] = dsaf_read_dev(drv, GMAC_MODE_CHANGE_EN_REG); + regs[63] = dsaf_read_dev(drv, GMAC_SIXTEEN_BIT_CNTR_REG); + regs[64] = dsaf_read_dev(drv, GMAC_LD_LINK_COUNTER_REG); + regs[65] = dsaf_read_dev(drv, GMAC_LOOP_REG); + regs[66] = dsaf_read_dev(drv, GMAC_RECV_CONTROL_REG); + regs[67] = dsaf_read_dev(drv, GMAC_VLAN_CODE_REG); + regs[68] = dsaf_read_dev(drv, GMAC_RX_OVERRUN_CNT_REG); + regs[69] = dsaf_read_dev(drv, GMAC_RX_LENGTHFIELD_ERR_CNT_REG); + regs[70] = dsaf_read_dev(drv, GMAC_RX_FAIL_COMMA_CNT_REG); + + regs[71] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_0_REG); + regs[72] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_0_REG); + regs[73] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_1_REG); + regs[74] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_1_REG); + regs[75] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_2_REG); + regs[76] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_2_REG); + regs[77] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_3_REG); + regs[78] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_3_REG); + regs[79] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_4_REG); + regs[80] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_4_REG); + regs[81] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_5_REG); + regs[82] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_5_REG); + regs[83] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_0_REG); + regs[84] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_0_REG); + regs[85] = dsaf_read_dev(drv, GMAC_STATION_ADDR_LOW_MSK_1_REG); + regs[86] = dsaf_read_dev(drv, GMAC_STATION_ADDR_HIGH_MSK_1_REG); + regs[87] = dsaf_read_dev(drv, GMAC_MAC_SKIP_LEN_REG); + regs[88] = dsaf_read_dev(drv, GMAC_TX_LOOP_PKT_PRI_REG); + + /* mark end of mac regs */ + for (i = 89; i < 96; i++) + regs[i] = 0xaaaaaaaa; +} + +static void hns_gmac_get_stats(void *mac_drv, u64 *data) +{ + u32 i; + u64 *buf = data; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = NULL; + + hw_stats = &drv->mac_cb->hw_stats; + + for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) { + buf[i] = DSAF_STATS_READ(hw_stats, + g_gmac_stats_string[i].offset); + } +} + +static void hns_gmac_get_strings(u32 stringset, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(g_gmac_stats_string); i++) { + snprintf(buff, ETH_GSTRING_LEN, g_gmac_stats_string[i].desc); + buff = buff + ETH_GSTRING_LEN; + } +} + +static int hns_gmac_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ARRAY_SIZE(g_gmac_stats_string); + + return 0; +} + +static int hns_gmac_get_regs_count(void) +{ + return ETH_GMAC_DUMP_NUM; +} + +void *hns_gmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) +{ + struct mac_driver *mac_drv; + + mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL); + if (!mac_drv) + return NULL; + + mac_drv->mac_init = hns_gmac_init; + mac_drv->mac_enable = hns_gmac_enable; + mac_drv->mac_disable = hns_gmac_disable; + mac_drv->mac_free = hns_gmac_free; + mac_drv->adjust_link = hns_gmac_adjust_link; + mac_drv->set_tx_auto_pause_frames = hns_gmac_set_tx_auto_pause_frames; + mac_drv->config_max_frame_length = hns_gmac_config_max_frame_length; + mac_drv->mac_pausefrm_cfg = hns_gmac_pause_frm_cfg; + + mac_drv->mac_id = mac_param->mac_id; + mac_drv->mac_mode = mac_param->mac_mode; + mac_drv->io_base = mac_param->vaddr; + mac_drv->dev = mac_param->dev; + mac_drv->mac_cb = mac_cb; + + mac_drv->set_mac_addr = hns_gmac_set_mac_addr; + mac_drv->set_an_mode = hns_gmac_config_an_mode; + mac_drv->config_loopback = hns_gmac_config_loopback; + mac_drv->config_pad_and_crc = hns_gmac_config_pad_and_crc; + mac_drv->config_half_duplex = hns_gmac_set_duplex_type; + mac_drv->set_rx_ignore_pause_frames = hns_gmac_set_rx_auto_pause_frames; + mac_drv->mac_get_id = hns_gmac_get_id; + mac_drv->get_info = hns_gmac_get_info; + mac_drv->autoneg_stat = hns_gmac_autoneg_stat; + mac_drv->get_pause_enable = hns_gmac_get_pausefrm_cfg; + mac_drv->get_link_status = hns_gmac_get_link_status; + mac_drv->get_regs = hns_gmac_get_regs; + mac_drv->get_regs_count = hns_gmac_get_regs_count; + mac_drv->get_ethtool_stats = hns_gmac_get_stats; + mac_drv->get_sset_count = hns_gmac_get_sset_count; + mac_drv->get_strings = hns_gmac_get_strings; + mac_drv->update_stats = hns_gmac_update_stats; + + return (void *)mac_drv; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h new file mode 100644 index 000000000000..44fe3010dc6d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_GMAC_H +#define _HNS_GMAC_H + +#include "hns_dsaf_mac.h" + +enum hns_port_mode { + GMAC_10M_MII = 0, + GMAC_100M_MII, + GMAC_1000M_GMII, + GMAC_10M_RGMII, + GMAC_100M_RGMII, + GMAC_1000M_RGMII, + GMAC_10M_SGMII, + GMAC_100M_SGMII, + GMAC_1000M_SGMII, + GMAC_10000M_SGMII /* 10GE */ +}; + +enum hns_gmac_duplex_mdoe { + GMAC_HALF_DUPLEX_MODE = 0, + GMAC_FULL_DUPLEX_MODE +}; + +struct hns_gmac_port_mode_cfg { + enum hns_port_mode port_mode; + u32 max_frm_size; + u32 short_runts_thr; + u32 pad_enable; + u32 crc_add; + u32 an_enable; /*auto-nego enable */ + u32 runt_pkt_en; + u32 strip_pad_en; +}; + +#define ETH_GMAC_DUMP_NUM 96 +#endif /* __HNS_GMAC_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c new file mode 100644 index 000000000000..026b38676cba --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -0,0 +1,902 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/phy_fixed.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "hns_dsaf_misc.h" +#include "hns_dsaf_main.h" +#include "hns_dsaf_rcb.h" + +#define MAC_EN_FLAG_V 0xada0328 + +static const u16 mac_phy_to_speed[] = { + [PHY_INTERFACE_MODE_MII] = MAC_SPEED_100, + [PHY_INTERFACE_MODE_GMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_SGMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_TBI] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RMII] = MAC_SPEED_100, + [PHY_INTERFACE_MODE_RGMII] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_RTBI] = MAC_SPEED_1000, + [PHY_INTERFACE_MODE_XGMII] = MAC_SPEED_10000 +}; + +static const enum mac_mode g_mac_mode_100[] = { + [PHY_INTERFACE_MODE_MII] = MAC_MODE_MII_100, + [PHY_INTERFACE_MODE_RMII] = MAC_MODE_RMII_100 +}; + +static const enum mac_mode g_mac_mode_1000[] = { + [PHY_INTERFACE_MODE_GMII] = MAC_MODE_GMII_1000, + [PHY_INTERFACE_MODE_SGMII] = MAC_MODE_SGMII_1000, + [PHY_INTERFACE_MODE_TBI] = MAC_MODE_TBI_1000, + [PHY_INTERFACE_MODE_RGMII] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = MAC_MODE_RGMII_1000, + [PHY_INTERFACE_MODE_RTBI] = MAC_MODE_RTBI_1000 +}; + +static enum mac_mode hns_mac_dev_to_enet_if(const struct hns_mac_cb *mac_cb) +{ + switch (mac_cb->max_speed) { + case MAC_SPEED_100: + return g_mac_mode_100[mac_cb->phy_if]; + case MAC_SPEED_1000: + return g_mac_mode_1000[mac_cb->phy_if]; + case MAC_SPEED_10000: + return MAC_MODE_XGMII_10000; + default: + return MAC_MODE_MII_100; + } +} + +static enum mac_mode hns_get_enet_interface(const struct hns_mac_cb *mac_cb) +{ + switch (mac_cb->max_speed) { + case MAC_SPEED_100: + return g_mac_mode_100[mac_cb->phy_if]; + case MAC_SPEED_1000: + return g_mac_mode_1000[mac_cb->phy_if]; + case MAC_SPEED_10000: + return MAC_MODE_XGMII_10000; + default: + return MAC_MODE_MII_100; + } +} + +int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt) +{ + if (!mac_cb->cpld_vaddr) + return -ENODEV; + + *sfp_prsnt = !dsaf_read_b((u8 *)mac_cb->cpld_vaddr + + MAC_SFP_PORT_OFFSET); + + return 0; +} + +void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status) +{ + struct mac_driver *mac_ctrl_drv; + int ret, sfp_prsnt; + + mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->get_link_status) + mac_ctrl_drv->get_link_status(mac_ctrl_drv, link_status); + else + *link_status = 0; + + ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt); + if (!ret) + *link_status = *link_status && sfp_prsnt; + + mac_cb->link = *link_status; +} + +int hns_mac_get_port_info(struct hns_mac_cb *mac_cb, + u8 *auto_neg, u16 *speed, u8 *duplex) +{ + struct mac_driver *mac_ctrl_drv; + struct mac_info info; + + mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (!mac_ctrl_drv->get_info) + return -ENODEV; + + mac_ctrl_drv->get_info(mac_ctrl_drv, &info); + if (auto_neg) + *auto_neg = info.auto_neg; + if (speed) + *speed = info.speed; + if (duplex) + *duplex = info.duplex; + + return 0; +} + +void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex) +{ + int ret; + struct mac_driver *mac_ctrl_drv; + + mac_ctrl_drv = (struct mac_driver *)(mac_cb->priv.mac); + + mac_cb->speed = speed; + mac_cb->half_duplex = !duplex; + mac_ctrl_drv->mac_mode = hns_mac_dev_to_enet_if(mac_cb); + + if (mac_ctrl_drv->adjust_link) { + ret = mac_ctrl_drv->adjust_link(mac_ctrl_drv, + (enum mac_speed)speed, duplex); + if (ret) { + dev_err(mac_cb->dev, + "adjust_link failed,%s mac%d ret = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, ret); + return; + } + } +} + +/** + *hns_mac_get_inner_port_num - get mac table inner port number + *@mac_cb: mac device + *@vmid: vm id + *@port_num:port number + * + */ +static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb, + u8 vmid, u8 *port_num) +{ + u8 tmp_port; + u32 comm_idx; + + if (mac_cb->dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) { + if (mac_cb->mac_id != DSAF_MAX_PORT_NUM_PER_CHIP) { + dev_err(mac_cb->dev, + "input invalid,%s mac%d vmid%d !\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, vmid); + return -EINVAL; + } + } else if (mac_cb->dsaf_dev->dsaf_mode < DSAF_MODE_MAX) { + if (mac_cb->mac_id >= DSAF_MAX_PORT_NUM_PER_CHIP) { + dev_err(mac_cb->dev, + "input invalid,%s mac%d vmid%d!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, vmid); + return -EINVAL; + } + } else { + dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id); + return -EINVAL; + } + + comm_idx = hns_dsaf_get_comm_idx_by_port(mac_cb->mac_id); + + if (vmid >= mac_cb->dsaf_dev->rcb_common[comm_idx]->max_vfn) { + dev_err(mac_cb->dev, "input invalid,%s mac%d vmid%d !\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vmid); + return -EINVAL; + } + + switch (mac_cb->dsaf_dev->dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + tmp_port = 0; + break; + case DSAF_MODE_DISABLE_FIX: + tmp_port = 0; + break; + case DSAF_MODE_ENABLE_0VM: + case DSAF_MODE_ENABLE_8VM: + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_ENABLE_128VM: + case DSAF_MODE_DISABLE_2PORT_8VM: + case DSAF_MODE_DISABLE_2PORT_16VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + case DSAF_MODE_DISABLE_6PORT_0VM: + case DSAF_MODE_DISABLE_6PORT_2VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + tmp_port = vmid; + break; + default: + dev_err(mac_cb->dev, "dsaf mode invalid,%s mac%d!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id); + return -EINVAL; + } + tmp_port += DSAF_BASE_INNER_PORT_NUM; + + *port_num = tmp_port; + + return 0; +} + +/** + *hns_mac_get_inner_port_num - change vf mac address + *@mac_cb: mac device + *@vmid: vmid + *@addr:mac address + */ +int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, + u32 vmid, char *addr) +{ + int ret; + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + struct mac_entry_idx *old_entry; + + old_entry = &mac_cb->addr_entry_idx[vmid]; + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = old_entry->vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, (u8)vmid, + &mac_entry.port_num); + if (ret) + return ret; + + if ((old_entry->valid != 0) && + (memcmp(old_entry->addr, + addr, sizeof(mac_entry.addr)) != 0)) { + ret = hns_dsaf_del_mac_entry(dsaf_dev, + old_entry->vlan_id, + mac_cb->mac_id, + old_entry->addr); + if (ret) + return ret; + } + + ret = hns_dsaf_set_mac_uc_entry(dsaf_dev, &mac_entry); + if (ret) + return ret; + } + + if ((mac_ctrl_drv->set_mac_addr) && (vmid == 0)) + mac_ctrl_drv->set_mac_addr(mac_cb->priv.mac, addr); + + memcpy(old_entry->addr, addr, sizeof(old_entry->addr)); + old_entry->valid = 1; + return 0; +} + +int hns_mac_set_multi(struct hns_mac_cb *mac_cb, + u32 port_num, char *addr, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + if (dsaf_dev && addr) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = 0;/*vlan_id;*/ + mac_entry.in_port_num = mac_cb->mac_id; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + if (ret) { + dev_err(dsaf_dev->dev, + "set mac mc port failed,%s mac%d ret = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, + mac_cb->mac_id, ret); + return ret; + } + } + + return 0; +} + +/** + *hns_mac_del_mac - delete mac address into dsaf table,can't delete the same + * address twice + *@net_dev: net device + *@vfn : vf lan + *@mac : mac address + *return status + */ +int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac) +{ + struct mac_entry_idx *old_mac; + struct dsaf_device *dsaf_dev; + u32 ret; + + dsaf_dev = mac_cb->dsaf_dev; + + if (vfn < DSAF_MAX_VM_NUM) { + old_mac = &mac_cb->addr_entry_idx[vfn]; + } else { + dev_err(mac_cb->dev, + "vf queue is too large,%s mac%d queue = %#x!\n", + mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vfn); + return -EINVAL; + } + + if (dsaf_dev) { + ret = hns_dsaf_del_mac_entry(dsaf_dev, old_mac->vlan_id, + mac_cb->mac_id, old_mac->addr); + if (ret) + return ret; + + if (memcmp(old_mac->addr, mac, sizeof(old_mac->addr)) == 0) + old_mac->valid = 0; + } + + return 0; +} + +static void hns_mac_param_get(struct mac_params *param, + struct hns_mac_cb *mac_cb) +{ + param->vaddr = (void *)mac_cb->vaddr; + param->mac_mode = hns_get_enet_interface(mac_cb); + memcpy(param->addr, mac_cb->addr_entry_idx[0].addr, + MAC_NUM_OCTETS_PER_ADDR); + param->mac_id = mac_cb->mac_id; + param->dev = mac_cb->dev; +} + +/** + *hns_mac_queue_config_bc_en - set broadcast rx&tx enable + *@mac_cb: mac device + *@queue: queue number + *@en:enable + *retuen 0 - success , negative --fail + */ +static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb, + u32 port_num, u16 vlan_id, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + u8 addr[MAC_NUM_OCTETS_PER_ADDR] + = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + /* directy return ok in debug network mode */ + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + return 0; + + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + return ret; + } + + return 0; +} + +/** + *hns_mac_vm_config_bc_en - set broadcast rx&tx enable + *@mac_cb: mac device + *@vmid: vm id + *@en:enable + *retuen 0 - success , negative --fail + */ +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en) +{ + int ret; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + u8 port_num; + u8 addr[MAC_NUM_OCTETS_PER_ADDR] + = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + struct mac_entry_idx *uc_mac_entry; + struct dsaf_drv_mac_single_dest_entry mac_entry; + + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + return 0; + + uc_mac_entry = &mac_cb->addr_entry_idx[vmid]; + + if (dsaf_dev) { + memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr)); + mac_entry.in_vlan_id = uc_mac_entry->vlan_id; + mac_entry.in_port_num = mac_cb->mac_id; + ret = hns_mac_get_inner_port_num(mac_cb, vmid, &port_num); + if (ret) + return ret; + mac_entry.port_num = port_num; + + if (en == DISABLE) + ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); + else + ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); + return ret; + } + + return 0; +} + +void hns_mac_reset(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *drv; + + drv = hns_mac_get_drv(mac_cb); + + drv->mac_init(drv); + + if (drv->config_max_frame_length) + drv->config_max_frame_length(drv, mac_cb->max_frm); + + if (drv->set_tx_auto_pause_frames) + drv->set_tx_auto_pause_frames(drv, mac_cb->tx_pause_frm_time); + + if (drv->set_an_mode) + drv->set_an_mode(drv, 1); + + if (drv->mac_pausefrm_cfg) { + if (mac_cb->mac_type == HNAE_PORT_DEBUG) + drv->mac_pausefrm_cfg(drv, 0, 0); + else /* mac rx must disable, dsaf pfc close instead of it*/ + drv->mac_pausefrm_cfg(drv, 0, 1); + } +} + +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu) +{ + struct mac_driver *drv = hns_mac_get_drv(mac_cb); + u32 buf_size = mac_cb->dsaf_dev->buf_size; + u32 new_frm = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + + if ((new_mtu < MAC_MIN_MTU) || (new_frm > MAC_MAX_MTU) || + (new_frm > HNS_RCB_RING_MAX_BD_PER_PKT * buf_size)) + return -EINVAL; + + if (!drv->config_max_frame_length) + return -ECHILD; + + /* adjust max frame to be at least the size of a standard frame */ + if (new_frm < (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)) + new_frm = (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN); + + drv->config_max_frame_length(drv, new_frm); + + mac_cb->max_frm = new_frm; + + return 0; +} + +void hns_mac_start(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_drv = hns_mac_get_drv(mac_cb); + + /* for virt */ + if (mac_drv->mac_en_flg == MAC_EN_FLAG_V) { + /*plus 1 when the virtual mac has been enabled */ + mac_drv->virt_dev_num += 1; + return; + } + + if (mac_drv->mac_enable) { + mac_drv->mac_enable(mac_cb->priv.mac, MAC_COMM_MODE_RX_AND_TX); + mac_drv->mac_en_flg = MAC_EN_FLAG_V; + } +} + +void hns_mac_stop(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + /*modified for virtualization */ + if (mac_ctrl_drv->virt_dev_num > 0) { + mac_ctrl_drv->virt_dev_num -= 1; + if (mac_ctrl_drv->virt_dev_num > 0) + return; + } + + if (mac_ctrl_drv->mac_disable) + mac_ctrl_drv->mac_disable(mac_cb->priv.mac, + MAC_COMM_MODE_RX_AND_TX); + + mac_ctrl_drv->mac_en_flg = 0; + mac_cb->link = 0; + cpld_led_reset(mac_cb); +} + +/** + * hns_mac_get_autoneg - get auto autonegotiation + * @mac_cb: mac control block + * @enable: enable or not + * retuen 0 - success , negative --fail + */ +void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->autoneg_stat) + mac_ctrl_drv->autoneg_stat(mac_ctrl_drv, auto_neg); + else + *auto_neg = 0; +} + +/** + * hns_mac_get_pauseparam - set rx & tx pause parameter + * @mac_cb: mac control block + * @rx_en: rx enable status + * @tx_en: tx enable status + * retuen 0 - success , negative --fail + */ +void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_ctrl_drv->get_pause_enable) { + mac_ctrl_drv->get_pause_enable(mac_ctrl_drv, rx_en, tx_en); + } else { + *rx_en = 0; + *tx_en = 0; + } + + /* Due to the chip defect, the service mac's rx pause CAN'T be enabled. + * We set the rx pause frm always be true (1), because DSAF deals with + * the rx pause frm instead of service mac. After all, we still support + * rx pause frm. + */ + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + *rx_en = 1; +} + +/** + * hns_mac_set_autoneg - set auto autonegotiation + * @mac_cb: mac control block + * @enable: enable or not + * retuen 0 - success , negative --fail + */ +int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII && enable) { + dev_err(mac_cb->dev, "enable autoneg is not allowed!"); + return -ENOTSUPP; + } + + if (mac_ctrl_drv->set_an_mode) + mac_ctrl_drv->set_an_mode(mac_ctrl_drv, enable); + + return 0; +} + +/** + * hns_mac_set_autoneg - set rx & tx pause parameter + * @mac_cb: mac control block + * @rx_en: rx enable or not + * @tx_en: tx enable or not + * return 0 - success , negative --fail + */ +int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) { + if (!rx_en) { + dev_err(mac_cb->dev, "disable rx_pause is not allowed!"); + return -EINVAL; + } + } else if (mac_cb->mac_type == HNAE_PORT_DEBUG) { + if (tx_en || rx_en) { + dev_err(mac_cb->dev, "enable tx_pause or enable rx_pause are not allowed!"); + return -EINVAL; + } + } else { + dev_err(mac_cb->dev, "Unsupport this operation!"); + return -EINVAL; + } + + if (mac_ctrl_drv->mac_pausefrm_cfg) + mac_ctrl_drv->mac_pausefrm_cfg(mac_ctrl_drv, rx_en, tx_en); + + return 0; +} + +/** + * hns_mac_init_ex - mac init + * @mac_cb: mac control block + * retuen 0 - success , negative --fail + */ +static int hns_mac_init_ex(struct hns_mac_cb *mac_cb) +{ + int ret; + struct mac_params param; + struct mac_driver *drv; + + hns_dsaf_fix_mac_mode(mac_cb); + + memset(¶m, 0, sizeof(struct mac_params)); + hns_mac_param_get(¶m, mac_cb); + + if (MAC_SPEED_FROM_MODE(param.mac_mode) < MAC_SPEED_10000) + drv = (struct mac_driver *)hns_gmac_config(mac_cb, ¶m); + else + drv = (struct mac_driver *)hns_xgmac_config(mac_cb, ¶m); + + if (!drv) + return -ENOMEM; + + mac_cb->priv.mac = (void *)drv; + hns_mac_reset(mac_cb); + + hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex); + + ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, ENABLE); + if (ret) + goto free_mac_drv; + + return 0; + +free_mac_drv: + drv->mac_free(mac_cb->priv.mac); + mac_cb->priv.mac = NULL; + + return ret; +} + +/** + *mac_free_dev - get mac information from device node + *@mac_cb: mac device + *@np:device node + *@mac_mode_idx:mac mode index + */ +static void hns_mac_get_info(struct hns_mac_cb *mac_cb, + struct device_node *np, u32 mac_mode_idx) +{ + mac_cb->link = false; + mac_cb->half_duplex = false; + mac_cb->speed = mac_phy_to_speed[mac_cb->phy_if]; + mac_cb->max_speed = mac_cb->speed; + + if (mac_cb->phy_if == PHY_INTERFACE_MODE_SGMII) { + mac_cb->if_support = MAC_GMAC_SUPPORTED; + mac_cb->if_support |= SUPPORTED_1000baseT_Full; + } else if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) { + mac_cb->if_support = SUPPORTED_10000baseR_FEC; + mac_cb->if_support |= SUPPORTED_10000baseKR_Full; + } + + mac_cb->max_frm = MAC_DEFAULT_MTU; + mac_cb->tx_pause_frm_time = MAC_DEFAULT_PAUSE_TIME; + + /* Get the rest of the PHY information */ + mac_cb->phy_node = of_parse_phandle(np, "phy-handle", mac_cb->mac_id); + if (mac_cb->phy_node) + dev_dbg(mac_cb->dev, "mac%d phy_node: %s\n", + mac_cb->mac_id, mac_cb->phy_node->name); +} + +/** + * hns_mac_get_mode - get mac mode + * @phy_if: phy interface + * retuen 0 - gmac, 1 - xgmac , negative --fail + */ +static int hns_mac_get_mode(phy_interface_t phy_if) +{ + switch (phy_if) { + case PHY_INTERFACE_MODE_SGMII: + return MAC_GMAC_IDX; + case PHY_INTERFACE_MODE_XGMII: + return MAC_XGMAC_IDX; + default: + return -EINVAL; + } +} + +u8 __iomem *hns_mac_get_vaddr(struct dsaf_device *dsaf_dev, + struct hns_mac_cb *mac_cb, u32 mac_mode_idx) +{ + u8 __iomem *base = dsaf_dev->io_base; + int mac_id = mac_cb->mac_id; + + if (mac_cb->mac_type == HNAE_PORT_SERVICE) + return base + 0x40000 + mac_id * 0x4000 - + mac_mode_idx * 0x20000; + else + return mac_cb->serdes_vaddr + 0x1000 + + (mac_id - DSAF_SERVICE_PORT_NUM_PER_DSAF) * 0x100000; +} + +/** + * hns_mac_get_cfg - get mac cfg from dtb or acpi table + * @dsaf_dev: dsa fabric device struct pointer + * @mac_idx: mac index + * retuen 0 - success , negative --fail + */ +int hns_mac_get_cfg(struct dsaf_device *dsaf_dev, int mac_idx) +{ + int ret; + u32 mac_mode_idx; + struct hns_mac_cb *mac_cb = &dsaf_dev->mac_cb[mac_idx]; + + mac_cb->dsaf_dev = dsaf_dev; + mac_cb->dev = dsaf_dev->dev; + mac_cb->mac_id = mac_idx; + + mac_cb->sys_ctl_vaddr = dsaf_dev->sc_base; + mac_cb->serdes_vaddr = dsaf_dev->sds_base; + + if (dsaf_dev->cpld_base && + mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) { + mac_cb->cpld_vaddr = dsaf_dev->cpld_base + + mac_cb->mac_id * CPLD_ADDR_PORT_OFFSET; + cpld_led_reset(mac_cb); + } + mac_cb->sfp_prsnt = 0; + mac_cb->txpkt_for_led = 0; + mac_cb->rxpkt_for_led = 0; + + if (mac_idx < DSAF_SERVICE_PORT_NUM_PER_DSAF) + mac_cb->mac_type = HNAE_PORT_SERVICE; + else + mac_cb->mac_type = HNAE_PORT_DEBUG; + + mac_cb->phy_if = hns_mac_get_phy_if(mac_cb); + + ret = hns_mac_get_mode(mac_cb->phy_if); + if (ret < 0) { + dev_err(dsaf_dev->dev, + "hns_mac_get_mode failed,mac%d ret = %#x!\n", + mac_cb->mac_id, ret); + return ret; + } + mac_mode_idx = (u32)ret; + + hns_mac_get_info(mac_cb, mac_cb->dev->of_node, mac_mode_idx); + + mac_cb->vaddr = hns_mac_get_vaddr(dsaf_dev, mac_cb, mac_mode_idx); + + return 0; +} + +/** + * hns_mac_init - init mac + * @dsaf_dev: dsa fabric device struct pointer + * retuen 0 - success , negative --fail + */ +int hns_mac_init(struct dsaf_device *dsaf_dev) +{ + int i; + int ret; + size_t size; + struct hns_mac_cb *mac_cb; + + size = sizeof(struct hns_mac_cb) * DSAF_MAX_PORT_NUM_PER_CHIP; + dsaf_dev->mac_cb = devm_kzalloc(dsaf_dev->dev, size, GFP_KERNEL); + if (!dsaf_dev->mac_cb) + return -ENOMEM; + + for (i = 0; i < DSAF_MAX_PORT_NUM_PER_CHIP; i++) { + ret = hns_mac_get_cfg(dsaf_dev, i); + if (ret) + goto free_mac_cb; + + mac_cb = &dsaf_dev->mac_cb[i]; + ret = hns_mac_init_ex(mac_cb); + if (ret) + goto free_mac_cb; + } + + return 0; + +free_mac_cb: + dsaf_dev->mac_cb = NULL; + + return ret; +} + +void hns_mac_uninit(struct dsaf_device *dsaf_dev) +{ + cpld_led_reset(dsaf_dev->mac_cb); + dsaf_dev->mac_cb = NULL; +} + +int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb, + enum hnae_loop loop, int en) +{ + int ret; + struct mac_driver *drv = hns_mac_get_drv(mac_cb); + + if (drv->config_loopback) + ret = drv->config_loopback(drv, loop, en); + else + ret = -ENOTSUPP; + + return ret; +} + +void hns_mac_update_stats(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->update_stats(mac_ctrl_drv); +} + +void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_ethtool_stats(mac_ctrl_drv, data); +} + +void hns_mac_get_strings(struct hns_mac_cb *mac_cb, + int stringset, u8 *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_strings(stringset, data); +} + +int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + return mac_ctrl_drv->get_sset_count(stringset); +} + +int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + return mac_ctrl_drv->get_regs_count(); +} + +void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data) +{ + struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb); + + mac_ctrl_drv->get_regs(mac_ctrl_drv, data); +} + +void hns_set_led_opt(struct hns_mac_cb *mac_cb) +{ + int nic_data = 0; + int txpkts, rxpkts; + + txpkts = mac_cb->txpkt_for_led - mac_cb->hw_stats.tx_good_pkts; + rxpkts = mac_cb->rxpkt_for_led - mac_cb->hw_stats.rx_good_pkts; + if (txpkts || rxpkts) + nic_data = 1; + else + nic_data = 0; + mac_cb->txpkt_for_led = mac_cb->hw_stats.tx_good_pkts; + mac_cb->rxpkt_for_led = mac_cb->hw_stats.rx_good_pkts; + hns_cpld_set_led(mac_cb, (int)mac_cb->link, + mac_cb->speed, nic_data); +} + +int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status) +{ + if (!mac_cb || !mac_cb->cpld_vaddr) + return 0; + + return cpld_set_led_id(mac_cb, status); +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h new file mode 100644 index 000000000000..7da95a7581f9 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_MAC_H +#define _HNS_DSAF_MAC_H + +#include <linux/phy.h> +#include <linux/kernel.h> +#include <linux/if_vlan.h> +#include "hns_dsaf_main.h" + +struct dsaf_device; + +#define MAC_GMAC_SUPPORTED \ + (SUPPORTED_10baseT_Half \ + | SUPPORTED_10baseT_Full \ + | SUPPORTED_100baseT_Half \ + | SUPPORTED_100baseT_Full \ + | SUPPORTED_Autoneg) + +#define MAC_DEFAULT_MTU (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN + ETH_DATA_LEN) +#define MAC_MAX_MTU 9600 +#define MAC_MIN_MTU 68 + +#define MAC_DEFAULT_PAUSE_TIME 0xff + +#define MAC_GMAC_IDX 0 +#define MAC_XGMAC_IDX 1 + +#define ETH_STATIC_REG 1 +#define ETH_DUMP_REG 5 +/* check mac addr broadcast */ +#define MAC_IS_BROADCAST(p) ((*(p) == 0xff) && (*((p) + 1) == 0xff) && \ + (*((p) + 2) == 0xff) && (*((p) + 3) == 0xff) && \ + (*((p) + 4) == 0xff) && (*((p) + 5) == 0xff)) + +/* check mac addr is 01-00-5e-xx-xx-xx*/ +#define MAC_IS_L3_MULTICAST(p) ((*((p) + 0) == 0x01) && \ + (*((p) + 1) == 0x00) && \ + (*((p) + 2) == 0x5e)) + +/*check the mac addr is 0 in all bit*/ +#define MAC_IS_ALL_ZEROS(p) ((*(p) == 0) && (*((p) + 1) == 0) && \ + (*((p) + 2) == 0) && (*((p) + 3) == 0) && \ + (*((p) + 4) == 0) && (*((p) + 5) == 0)) + +/*check mac addr multicast*/ +#define MAC_IS_MULTICAST(p) ((*((u8 *)((p) + 0)) & 0x01) ? (1) : (0)) + +/**< Number of octets (8-bit bytes) in an ethernet address */ +#define MAC_NUM_OCTETS_PER_ADDR 6 + +struct mac_priv { + void *mac; +}; + +/* net speed */ +enum mac_speed { + MAC_SPEED_10 = 10, /**< 10 Mbps */ + MAC_SPEED_100 = 100, /**< 100 Mbps */ + MAC_SPEED_1000 = 1000, /**< 1000 Mbps = 1 Gbps */ + MAC_SPEED_10000 = 10000 /**< 10000 Mbps = 10 Gbps */ +}; + +/*mac interface keyword */ +enum mac_intf { + MAC_IF_NONE = 0x00000000, /**< interface not invalid */ + MAC_IF_MII = 0x00010000, /**< MII interface */ + MAC_IF_RMII = 0x00020000, /**< RMII interface */ + MAC_IF_SMII = 0x00030000, /**< SMII interface */ + MAC_IF_GMII = 0x00040000, /**< GMII interface */ + MAC_IF_RGMII = 0x00050000, /**< RGMII interface */ + MAC_IF_TBI = 0x00060000, /**< TBI interface */ + MAC_IF_RTBI = 0x00070000, /**< RTBI interface */ + MAC_IF_SGMII = 0x00080000, /**< SGMII interface */ + MAC_IF_XGMII = 0x00090000, /**< XGMII interface */ + MAC_IF_QSGMII = 0x000a0000 /**< QSGMII interface */ +}; + +/*mac mode */ +enum mac_mode { + /**< Invalid Ethernet mode */ + MAC_MODE_INVALID = 0, + /**< 10 Mbps MII */ + MAC_MODE_MII_10 = (MAC_IF_MII | MAC_SPEED_10), + /**< 100 Mbps MII */ + MAC_MODE_MII_100 = (MAC_IF_MII | MAC_SPEED_100), + /**< 10 Mbps RMII */ + MAC_MODE_RMII_10 = (MAC_IF_RMII | MAC_SPEED_10), + /**< 100 Mbps RMII */ + MAC_MODE_RMII_100 = (MAC_IF_RMII | MAC_SPEED_100), + /**< 10 Mbps SMII */ + MAC_MODE_SMII_10 = (MAC_IF_SMII | MAC_SPEED_10), + /**< 100 Mbps SMII */ + MAC_MODE_SMII_100 = (MAC_IF_SMII | MAC_SPEED_100), + /**< 1000 Mbps GMII */ + MAC_MODE_GMII_1000 = (MAC_IF_GMII | MAC_SPEED_1000), + /**< 10 Mbps RGMII */ + MAC_MODE_RGMII_10 = (MAC_IF_RGMII | MAC_SPEED_10), + /**< 100 Mbps RGMII */ + MAC_MODE_RGMII_100 = (MAC_IF_RGMII | MAC_SPEED_100), + /**< 1000 Mbps RGMII */ + MAC_MODE_RGMII_1000 = (MAC_IF_RGMII | MAC_SPEED_1000), + /**< 1000 Mbps TBI */ + MAC_MODE_TBI_1000 = (MAC_IF_TBI | MAC_SPEED_1000), + /**< 1000 Mbps RTBI */ + MAC_MODE_RTBI_1000 = (MAC_IF_RTBI | MAC_SPEED_1000), + /**< 10 Mbps SGMII */ + MAC_MODE_SGMII_10 = (MAC_IF_SGMII | MAC_SPEED_10), + /**< 100 Mbps SGMII */ + MAC_MODE_SGMII_100 = (MAC_IF_SGMII | MAC_SPEED_100), + /**< 1000 Mbps SGMII */ + MAC_MODE_SGMII_1000 = (MAC_IF_SGMII | MAC_SPEED_1000), + /**< 10000 Mbps XGMII */ + MAC_MODE_XGMII_10000 = (MAC_IF_XGMII | MAC_SPEED_10000), + /**< 1000 Mbps QSGMII */ + MAC_MODE_QSGMII_1000 = (MAC_IF_QSGMII | MAC_SPEED_1000) +}; + +/*mac communicate mode*/ +enum mac_commom_mode { + MAC_COMM_MODE_NONE = 0, /**< No transmit/receive communication */ + MAC_COMM_MODE_RX = 1, /**< Only receive communication */ + MAC_COMM_MODE_TX = 2, /**< Only transmit communication */ + MAC_COMM_MODE_RX_AND_TX = 3 /**< Both tx and rx communication */ +}; + +/*mac statistics */ +struct mac_statistics { + u64 stat_pkts64; /* r-10G tr-DT 64 byte frame counter */ + u64 stat_pkts65to127; /* r-10G 65 to 127 byte frame counter */ + u64 stat_pkts128to255; /* r-10G 128 to 255 byte frame counter */ + u64 stat_pkts256to511; /*r-10G 256 to 511 byte frame counter */ + u64 stat_pkts512to1023;/* r-10G 512 to 1023 byte frame counter */ + u64 stat_pkts1024to1518; /* r-10G 1024 to 1518 byte frame counter */ + u64 stat_pkts1519to1522; /* r-10G 1519 to 1522 byte good frame count*/ + /* Total number of packets that were less than 64 octets */ + /* long with a wrong CRC.*/ + u64 stat_fragments; + /* Total number of packets longer than valid maximum length octets */ + u64 stat_jabbers; + /* number of dropped packets due to internal errors of */ + /* the MAC Client. */ + u64 stat_drop_events; + /* Incremented when frames of correct length but with */ + /* CRC error are received.*/ + u64 stat_crc_align_errors; + /* Total number of packets that were less than 64 octets */ + /* long with a good CRC.*/ + u64 stat_undersize_pkts; + u64 stat_oversize_pkts; /**< T,B.D*/ + + u64 stat_rx_pause; /**< Pause MAC Control received */ + u64 stat_tx_pause; /**< Pause MAC Control sent */ + + u64 in_octets; /**< Total number of byte received. */ + u64 in_pkts; /* Total number of packets received.*/ + u64 in_mcast_pkts; /* Total number of multicast frame received */ + u64 in_bcast_pkts; /* Total number of broadcast frame received */ + /* Frames received, but discarded due to */ + /* problems within the MAC RX. */ + u64 in_discards; + u64 in_errors; /* Number of frames received with error: */ + /* - FIFO Overflow Error */ + /* - CRC Error */ + /* - Frame Too Long Error */ + /* - Alignment Error */ + u64 out_octets; /*Total number of byte sent. */ + u64 out_pkts; /**< Total number of packets sent .*/ + u64 out_mcast_pkts; /* Total number of multicast frame sent */ + u64 out_bcast_pkts; /* Total number of multicast frame sent */ + /* Frames received, but discarded due to problems within */ + /* the MAC TX N/A!.*/ + u64 out_discards; + u64 out_errors; /*Number of frames transmitted with error: */ + /* - FIFO Overflow Error */ + /* - FIFO Underflow Error */ + /* - Other */ +}; + +/*mac para struct ,mac get param from nic or dsaf when initialize*/ +struct mac_params { + char addr[MAC_NUM_OCTETS_PER_ADDR]; + void *vaddr; /*virtual address*/ + struct device *dev; + u8 mac_id; + /**< Ethernet operation mode (MAC-PHY interface and speed) */ + enum mac_mode mac_mode; +}; + +struct mac_info { + u16 speed;/* The forced speed (lower bits) in */ + /* *mbps. Please use */ + /* * ethtool_cmd_speed()/_set() to */ + /* * access it */ + u8 duplex; /* Duplex, half or full */ + u8 auto_neg; /* Enable or disable autonegotiation */ + enum hnae_loop loop_mode; + u8 tx_pause_en; + u8 tx_pause_time; + u8 rx_pause_en; + u8 pad_and_crc_en; + u8 promiscuous_en; + u8 port_en; /*port enable*/ +}; + +struct mac_entry_idx { + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 vlan_id:12; + u16 valid:1; + u16 qos:3; +}; + +struct mac_hw_stats { + u64 rx_good_pkts; /* only for xgmac */ + u64 rx_good_bytes; + u64 rx_total_pkts; /* only for xgmac */ + u64 rx_total_bytes; /* only for xgmac */ + u64 rx_bad_bytes; /* only for gmac */ + u64 rx_uc_pkts; + u64 rx_mc_pkts; + u64 rx_bc_pkts; + u64 rx_fragment_err; /* only for xgmac */ + u64 rx_undersize; /* only for xgmac */ + u64 rx_under_min; + u64 rx_minto64; /* only for gmac */ + u64 rx_64bytes; + u64 rx_65to127; + u64 rx_128to255; + u64 rx_256to511; + u64 rx_512to1023; + u64 rx_1024to1518; + u64 rx_1519tomax; + u64 rx_1519tomax_good; /* only for xgmac */ + u64 rx_oversize; + u64 rx_jabber_err; + u64 rx_fcs_err; + u64 rx_vlan_pkts; /* only for gmac */ + u64 rx_data_err; /* only for gmac */ + u64 rx_align_err; /* only for gmac */ + u64 rx_long_err; /* only for gmac */ + u64 rx_pfc_tc0; + u64 rx_pfc_tc1; /* only for xgmac */ + u64 rx_pfc_tc2; /* only for xgmac */ + u64 rx_pfc_tc3; /* only for xgmac */ + u64 rx_pfc_tc4; /* only for xgmac */ + u64 rx_pfc_tc5; /* only for xgmac */ + u64 rx_pfc_tc6; /* only for xgmac */ + u64 rx_pfc_tc7; /* only for xgmac */ + u64 rx_unknown_ctrl; + u64 rx_filter_pkts; /* only for gmac */ + u64 rx_filter_bytes; /* only for gmac */ + u64 rx_fifo_overrun_err;/* only for gmac */ + u64 rx_len_err; /* only for gmac */ + u64 rx_comma_err; /* only for gmac */ + u64 rx_symbol_err; /* only for xgmac */ + u64 tx_good_to_sw; /* only for xgmac */ + u64 tx_bad_to_sw; /* only for xgmac */ + u64 rx_1731_pkts; /* only for xgmac */ + + u64 tx_good_bytes; + u64 tx_good_pkts; /* only for xgmac */ + u64 tx_total_bytes; /* only for xgmac */ + u64 tx_total_pkts; /* only for xgmac */ + u64 tx_bad_bytes; /* only for gmac */ + u64 tx_bad_pkts; /* only for xgmac */ + u64 tx_uc_pkts; + u64 tx_mc_pkts; + u64 tx_bc_pkts; + u64 tx_undersize; /* only for xgmac */ + u64 tx_fragment_err; /* only for xgmac */ + u64 tx_under_min_pkts; /* only for gmac */ + u64 tx_64bytes; + u64 tx_65to127; + u64 tx_128to255; + u64 tx_256to511; + u64 tx_512to1023; + u64 tx_1024to1518; + u64 tx_1519tomax; + u64 tx_1519tomax_good; /* only for xgmac */ + u64 tx_oversize; /* only for xgmac */ + u64 tx_jabber_err; + u64 tx_underrun_err; /* only for gmac */ + u64 tx_vlan; /* only for gmac */ + u64 tx_crc_err; /* only for gmac */ + u64 tx_pfc_tc0; + u64 tx_pfc_tc1; /* only for xgmac */ + u64 tx_pfc_tc2; /* only for xgmac */ + u64 tx_pfc_tc3; /* only for xgmac */ + u64 tx_pfc_tc4; /* only for xgmac */ + u64 tx_pfc_tc5; /* only for xgmac */ + u64 tx_pfc_tc6; /* only for xgmac */ + u64 tx_pfc_tc7; /* only for xgmac */ + u64 tx_ctrl; /* only for xgmac */ + u64 tx_1731_pkts; /* only for xgmac */ + u64 tx_1588_pkts; /* only for xgmac */ + u64 rx_good_from_sw; /* only for xgmac */ + u64 rx_bad_from_sw; /* only for xgmac */ +}; + +struct hns_mac_cb { + struct device *dev; + struct dsaf_device *dsaf_dev; + struct mac_priv priv; + u8 __iomem *vaddr; + u8 __iomem *cpld_vaddr; + u8 __iomem *sys_ctl_vaddr; + u8 __iomem *serdes_vaddr; + struct mac_entry_idx addr_entry_idx[DSAF_MAX_VM_NUM]; + u8 sfp_prsnt; + u8 cpld_led_value; + u8 mac_id; + + u8 link; + u8 half_duplex; + u16 speed; + u16 max_speed; + u16 max_frm; + u16 tx_pause_frm_time; + u32 if_support; + u64 txpkt_for_led; + u64 rxpkt_for_led; + enum hnae_port_type mac_type; + phy_interface_t phy_if; + enum hnae_loop loop_mode; + + struct device_node *phy_node; + + struct mac_hw_stats hw_stats; +}; + +struct mac_driver { + /*init Mac when init nic or dsaf*/ + void (*mac_init)(void *mac_drv); + /*remove mac when remove nic or dsaf*/ + void (*mac_free)(void *mac_drv); + /*enable mac when enable nic or dsaf*/ + void (*mac_enable)(void *mac_drv, enum mac_commom_mode mode); + /*disable mac when disable nic or dsaf*/ + void (*mac_disable)(void *mac_drv, enum mac_commom_mode mode); + /* config mac address*/ + void (*set_mac_addr)(void *mac_drv, char *mac_addr); + /*adjust mac mode of port,include speed and duplex*/ + int (*adjust_link)(void *mac_drv, enum mac_speed speed, + u32 full_duplex); + /* config autoegotaite mode of port*/ + void (*set_an_mode)(void *mac_drv, u8 enable); + /* config loopbank mode */ + int (*config_loopback)(void *mac_drv, enum hnae_loop loop_mode, + u8 enable); + /* config mtu*/ + void (*config_max_frame_length)(void *mac_drv, u16 newval); + /*config PAD and CRC enable */ + void (*config_pad_and_crc)(void *mac_drv, u8 newval); + /* config duplex mode*/ + void (*config_half_duplex)(void *mac_drv, u8 newval); + /*config tx pause time,if pause_time is zero,disable tx pause enable*/ + void (*set_tx_auto_pause_frames)(void *mac_drv, u16 pause_time); + /*config rx pause enable*/ + void (*set_rx_ignore_pause_frames)(void *mac_drv, u32 enable); + /* config rx mode for promiscuous*/ + int (*set_promiscuous)(void *mac_drv, u8 enable); + /* get mac id */ + void (*mac_get_id)(void *mac_drv, u8 *mac_id); + void (*mac_pausefrm_cfg)(void *mac_drv, u32 rx_en, u32 tx_en); + + void (*autoneg_stat)(void *mac_drv, u32 *enable); + int (*set_pause_enable)(void *mac_drv, u32 rx_en, u32 tx_en); + void (*get_pause_enable)(void *mac_drv, u32 *rx_en, u32 *tx_en); + void (*get_link_status)(void *mac_drv, u32 *link_stat); + /* get the imporant regs*/ + void (*get_regs)(void *mac_drv, void *data); + int (*get_regs_count)(void); + /* get strings name for ethtool statistic */ + void (*get_strings)(u32 stringset, u8 *data); + /* get the number of strings*/ + int (*get_sset_count)(int stringset); + + /* get the statistic by ethtools*/ + void (*get_ethtool_stats)(void *mac_drv, u64 *data); + + /* get mac information */ + void (*get_info)(void *mac_drv, struct mac_info *mac_info); + + void (*update_stats)(void *mac_drv); + + enum mac_mode mac_mode; + u8 mac_id; + struct hns_mac_cb *mac_cb; + void __iomem *io_base; + unsigned int mac_en_flg;/*you'd better don't enable mac twice*/ + unsigned int virt_dev_num; + struct device *dev; +}; + +struct mac_stats_string { + char desc[64]; + unsigned long offset; +}; + +#define MAC_MAKE_MODE(interface, speed) (enum mac_mode)((interface) | (speed)) +#define MAC_INTERFACE_FROM_MODE(mode) (enum mac_intf)((mode) & 0xFFFF0000) +#define MAC_SPEED_FROM_MODE(mode) (enum mac_speed)((mode) & 0x0000FFFF) +#define MAC_STATS_FIELD_OFF(field) (offsetof(struct mac_hw_stats, field)) + +static inline struct mac_driver *hns_mac_get_drv( + const struct hns_mac_cb *mac_cb) +{ + return (struct mac_driver *)(mac_cb->priv.mac); +} + +void *hns_gmac_config(struct hns_mac_cb *mac_cb, + struct mac_params *mac_param); +void *hns_xgmac_config(struct hns_mac_cb *mac_cb, + struct mac_params *mac_param); + +int hns_mac_init(struct dsaf_device *dsaf_dev); +void mac_adjust_link(struct net_device *net_dev); +void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status); +int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, u32 vmid, char *addr); +int hns_mac_set_multi(struct hns_mac_cb *mac_cb, + u32 port_num, char *addr, u8 en); +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, u8 en); +void hns_mac_start(struct hns_mac_cb *mac_cb); +void hns_mac_stop(struct hns_mac_cb *mac_cb); +int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac); +void hns_mac_uninit(struct dsaf_device *dsaf_dev); +void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex); +void hns_mac_reset(struct hns_mac_cb *mac_cb); +void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg); +void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en); +int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable); +int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en); +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu); +int hns_mac_get_port_info(struct hns_mac_cb *mac_cb, + u8 *auto_neg, u16 *speed, u8 *duplex); +phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb); +int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en); +int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb, + enum hnae_loop loop, int en); +void hns_mac_update_stats(struct hns_mac_cb *mac_cb); +void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data); +void hns_mac_get_strings(struct hns_mac_cb *mac_cb, int stringset, u8 *data); +int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset); +void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data); +int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb); +void hns_set_led_opt(struct hns_mac_cb *mac_cb); +int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status); +#endif /* _HNS_DSAF_MAC_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c new file mode 100644 index 000000000000..2a98eba660c0 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -0,0 +1,2474 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/device.h> +#include <linux/vmalloc.h> + +#include "hns_dsaf_main.h" +#include "hns_dsaf_rcb.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_mac.h" + +const char *g_dsaf_mode_match[DSAF_MODE_MAX] = { + [DSAF_MODE_DISABLE_2PORT_64VM] = "2port-64vf", + [DSAF_MODE_DISABLE_6PORT_0VM] = "6port-16rss", + [DSAF_MODE_DISABLE_6PORT_16VM] = "6port-16vf", +}; + +int hns_dsaf_get_cfg(struct dsaf_device *dsaf_dev) +{ + int ret, i; + u32 desc_num; + u32 buf_size; + const char *name, *mode_str; + struct device_node *np = dsaf_dev->dev->of_node; + + if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v2")) + dsaf_dev->dsaf_ver = AE_VERSION_2; + else + dsaf_dev->dsaf_ver = AE_VERSION_1; + + ret = of_property_read_string(np, "dsa_name", &name); + if (ret) { + dev_err(dsaf_dev->dev, "get dsaf name fail, ret=%d!\n", ret); + return ret; + } + strncpy(dsaf_dev->ae_dev.name, name, AE_NAME_SIZE); + dsaf_dev->ae_dev.name[AE_NAME_SIZE - 1] = '\0'; + + ret = of_property_read_string(np, "mode", &mode_str); + if (ret) { + dev_err(dsaf_dev->dev, "get dsaf mode fail, ret=%d!\n", ret); + return ret; + } + for (i = 0; i < DSAF_MODE_MAX; i++) { + if (g_dsaf_mode_match[i] && + !strcmp(mode_str, g_dsaf_mode_match[i])) + break; + } + if (i >= DSAF_MODE_MAX || + i == DSAF_MODE_INVALID || i == DSAF_MODE_ENABLE) { + dev_err(dsaf_dev->dev, + "%s prs mode str fail!\n", dsaf_dev->ae_dev.name); + return -EINVAL; + } + dsaf_dev->dsaf_mode = (enum dsaf_mode)i; + + if (dsaf_dev->dsaf_mode > DSAF_MODE_ENABLE) + dsaf_dev->dsaf_en = HRD_DSAF_NO_DSAF_MODE; + else + dsaf_dev->dsaf_en = HRD_DSAF_MODE; + + if ((i == DSAF_MODE_ENABLE_16VM) || + (i == DSAF_MODE_DISABLE_2PORT_8VM) || + (i == DSAF_MODE_DISABLE_6PORT_2VM)) + dsaf_dev->dsaf_tc_mode = HRD_DSAF_8TC_MODE; + else + dsaf_dev->dsaf_tc_mode = HRD_DSAF_4TC_MODE; + + dsaf_dev->sc_base = of_iomap(np, 0); + if (!dsaf_dev->sc_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 0 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->sds_base = of_iomap(np, 1); + if (!dsaf_dev->sds_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 1 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->ppe_base = of_iomap(np, 2); + if (!dsaf_dev->ppe_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 2 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->io_base = of_iomap(np, 3); + if (!dsaf_dev->io_base) { + dev_err(dsaf_dev->dev, + "%s of_iomap 3 fail!\n", dsaf_dev->ae_dev.name); + ret = -ENOMEM; + goto unmap_base_addr; + } + + dsaf_dev->cpld_base = of_iomap(np, 4); + if (!dsaf_dev->cpld_base) + dev_dbg(dsaf_dev->dev, "NO CPLD ADDR"); + + ret = of_property_read_u32(np, "desc-num", &desc_num); + if (ret < 0 || desc_num < HNS_DSAF_MIN_DESC_CNT || + desc_num > HNS_DSAF_MAX_DESC_CNT) { + dev_err(dsaf_dev->dev, "get desc-num(%d) fail, ret=%d!\n", + desc_num, ret); + goto unmap_base_addr; + } + dsaf_dev->desc_num = desc_num; + + ret = of_property_read_u32(np, "buf-size", &buf_size); + if (ret < 0) { + dev_err(dsaf_dev->dev, + "get buf-size fail, ret=%d!\r\n", ret); + goto unmap_base_addr; + } + dsaf_dev->buf_size = buf_size; + + dsaf_dev->buf_size_type = hns_rcb_buf_size2type(buf_size); + if (dsaf_dev->buf_size_type < 0) { + dev_err(dsaf_dev->dev, + "buf_size(%d) is wrong!\n", buf_size); + goto unmap_base_addr; + } + + if (!dma_set_mask_and_coherent(dsaf_dev->dev, DMA_BIT_MASK(64ULL))) + dev_dbg(dsaf_dev->dev, "set mask to 64bit\n"); + else + dev_err(dsaf_dev->dev, "set mask to 64bit fail!\n"); + + return 0; + +unmap_base_addr: + if (dsaf_dev->io_base) + iounmap(dsaf_dev->io_base); + if (dsaf_dev->ppe_base) + iounmap(dsaf_dev->ppe_base); + if (dsaf_dev->sds_base) + iounmap(dsaf_dev->sds_base); + if (dsaf_dev->sc_base) + iounmap(dsaf_dev->sc_base); + if (dsaf_dev->cpld_base) + iounmap(dsaf_dev->cpld_base); + return ret; +} + +static void hns_dsaf_free_cfg(struct dsaf_device *dsaf_dev) +{ + if (dsaf_dev->io_base) + iounmap(dsaf_dev->io_base); + + if (dsaf_dev->ppe_base) + iounmap(dsaf_dev->ppe_base); + + if (dsaf_dev->sds_base) + iounmap(dsaf_dev->sds_base); + + if (dsaf_dev->sc_base) + iounmap(dsaf_dev->sc_base); + + if (dsaf_dev->cpld_base) + iounmap(dsaf_dev->cpld_base); +} + +/** + * hns_dsaf_sbm_link_sram_init_en - config dsaf_sbm_init_en + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_link_sram_init_en(struct dsaf_device *dsaf_dev) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_SBM_INIT_S, 1); +} + +/** + * hns_dsaf_reg_cnt_clr_ce - config hns_dsaf_reg_cnt_clr_ce + * @dsaf_id: dsa fabric id + * @hns_dsaf_reg_cnt_clr_ce: config value + */ +static void +hns_dsaf_reg_cnt_clr_ce(struct dsaf_device *dsaf_dev, u32 reg_cnt_clr_ce) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_DSA_REG_CNT_CLR_CE_REG, + DSAF_CNT_CLR_CE_S, reg_cnt_clr_ce); +} + +/** + * hns_ppe_qid_cfg - config ppe qid + * @dsaf_id: dsa fabric id + * @pppe_qid_cfg: value array + */ +static void +hns_dsaf_ppe_qid_cfg(struct dsaf_device *dsaf_dev, u32 qid_cfg) +{ + u32 i; + + for (i = 0; i < DSAF_COMM_CHN; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_PPE_QID_CFG_0_REG + 0x0004 * i, + DSAF_PPE_QID_CFG_M, DSAF_PPE_QID_CFG_S, + qid_cfg); + } +} + +static void hns_dsaf_mix_def_qid_cfg(struct dsaf_device *dsaf_dev) +{ + u16 max_q_per_vf, max_vfn; + u32 q_id, q_num_per_port; + u32 i; + + hns_rcb_get_queue_mode(dsaf_dev->dsaf_mode, + HNS_DSAF_COMM_SERVICE_NW_IDX, + &max_vfn, &max_q_per_vf); + q_num_per_port = max_vfn * max_q_per_vf; + + for (i = 0, q_id = 0; i < DSAF_SERVICE_NW_NUM; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_MIX_DEF_QID_0_REG + 0x0004 * i, + 0xff, 0, q_id); + q_id += q_num_per_port; + } +} + +/** + * hns_dsaf_sw_port_type_cfg - cfg sw type + * @dsaf_id: dsa fabric id + * @psw_port_type: array + */ +static void hns_dsaf_sw_port_type_cfg(struct dsaf_device *dsaf_dev, + enum dsaf_sw_port_type port_type) +{ + u32 i; + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_SW_PORT_TYPE_0_REG + 0x0004 * i, + DSAF_SW_PORT_TYPE_M, DSAF_SW_PORT_TYPE_S, + port_type); + } +} + +/** + * hns_dsaf_stp_port_type_cfg - cfg stp type + * @dsaf_id: dsa fabric id + * @pstp_port_type: array + */ +static void hns_dsaf_stp_port_type_cfg(struct dsaf_device *dsaf_dev, + enum dsaf_stp_port_type port_type) +{ + u32 i; + + for (i = 0; i < DSAF_COMM_CHN; i++) { + dsaf_set_dev_field(dsaf_dev, + DSAF_STP_PORT_TYPE_0_REG + 0x0004 * i, + DSAF_STP_PORT_TYPE_M, DSAF_STP_PORT_TYPE_S, + port_type); + } +} + +/** + * hns_dsaf_sbm_cfg - config sbm + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_cfg(struct dsaf_device *dsaf_dev) +{ + u32 o_sbm_cfg; + u32 i; + + for (i = 0; i < DSAF_SBM_NUM; i++) { + o_sbm_cfg = dsaf_read_dev(dsaf_dev, + DSAF_SBM_CFG_REG_0_REG + 0x80 * i); + dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_EN_S, 1); + dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_SHCUT_EN_S, 0); + dsaf_write_dev(dsaf_dev, + DSAF_SBM_CFG_REG_0_REG + 0x80 * i, o_sbm_cfg); + } +} + +/** + * hns_dsaf_sbm_cfg_mib_en - config sbm + * @dsaf_id: dsa fabric id + */ +static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev) +{ + u32 sbm_cfg_mib_en; + u32 i; + u32 reg; + u32 read_cnt; + + for (i = 0; i < DSAF_SBM_NUM; i++) { + reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; + dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 1); + } + + /* waitint for all sbm enable finished */ + for (i = 0; i < DSAF_SBM_NUM; i++) { + read_cnt = 0; + reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; + do { + udelay(1); + sbm_cfg_mib_en = dsaf_get_dev_bit( + dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S); + read_cnt++; + } while (sbm_cfg_mib_en == 0 && + read_cnt < DSAF_CFG_READ_CNT); + + if (sbm_cfg_mib_en == 0) { + dev_err(dsaf_dev->dev, + "sbm_cfg_mib_en fail,%s,sbm_num=%d\n", + dsaf_dev->ae_dev.name, i); + return -ENODEV; + } + } + + return 0; +} + +/** + * hns_dsaf_sbm_bp_wl_cfg - config sbm + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev) +{ + u32 o_sbm_bp_cfg0; + u32 o_sbm_bp_cfg1; + u32 o_sbm_bp_cfg2; + u32 o_sbm_bp_cfg3; + u32 reg; + u32 i; + + /* XGE */ + for (i = 0; i < DSAF_XGE_NUM; i++) { + reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg0 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S, 512); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M, + DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg0); + + reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg1 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M, + DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M, + DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg1); + + reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 104); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 128); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + + reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + + /* for no enable pfc mode */ + reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 128); + dsaf_set_field(o_sbm_bp_cfg3, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, + DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 192); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + } + + /* PPE */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 10); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 12); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + } + + /* RoCEE */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + reg = DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + DSAF_SBM_CFG2_SET_BUF_NUM_S, 2); + dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + DSAF_SBM_CFG2_RESET_BUF_NUM_S, 4); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + } +} + +/** + * hns_dsaf_voq_bp_all_thrd_cfg - voq + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_voq_bp_all_thrd_cfg(struct dsaf_device *dsaf_dev) +{ + u32 voq_bp_all_thrd; + u32 i; + + for (i = 0; i < DSAF_VOQ_NUM; i++) { + voq_bp_all_thrd = dsaf_read_dev( + dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i); + if (i < DSAF_XGE_NUM) { + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_DOWNTHRD_M, + DSAF_VOQ_BP_ALL_DOWNTHRD_S, 930); + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_UPTHRD_M, + DSAF_VOQ_BP_ALL_UPTHRD_S, 950); + } else { + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_DOWNTHRD_M, + DSAF_VOQ_BP_ALL_DOWNTHRD_S, 220); + dsaf_set_field(voq_bp_all_thrd, + DSAF_VOQ_BP_ALL_UPTHRD_M, + DSAF_VOQ_BP_ALL_UPTHRD_S, 230); + } + dsaf_write_dev( + dsaf_dev, DSAF_VOQ_BP_ALL_THRD_0_REG + 0x40 * i, + voq_bp_all_thrd); + } +} + +/** + * hns_dsaf_tbl_tcam_data_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_data: addr + */ +static void hns_dsaf_tbl_tcam_data_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_data *ptbl_tcam_data) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_LOW_0_REG, + ptbl_tcam_data->tbl_tcam_data_low); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_HIGH_0_REG, + ptbl_tcam_data->tbl_tcam_data_high); +} + +/** + * dsaf_tbl_tcam_mcast_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_mcast: addr + */ +static void hns_dsaf_tbl_tcam_mcast_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_mcast_cfg *mcast) +{ + u32 mcast_cfg4; + + mcast_cfg4 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG); + dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S, + mcast->tbl_mcast_item_vld); + dsaf_set_bit(mcast_cfg4, DSAF_TBL_MCAST_CFG4_OLD_EN_S, + mcast->tbl_mcast_old_en); + dsaf_set_field(mcast_cfg4, DSAF_TBL_MCAST_CFG4_VM128_112_M, + DSAF_TBL_MCAST_CFG4_VM128_112_S, + mcast->tbl_mcast_port_msk[4]); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, mcast_cfg4); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG, + mcast->tbl_mcast_port_msk[3]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG, + mcast->tbl_mcast_port_msk[2]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG, + mcast->tbl_mcast_port_msk[1]); + + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG, + mcast->tbl_mcast_port_msk[0]); +} + +/** + * hns_dsaf_tbl_tcam_ucast_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_tcam_ucast: addr + */ +static void hns_dsaf_tbl_tcam_ucast_cfg( + struct dsaf_device *dsaf_dev, + struct dsaf_tbl_tcam_ucast_cfg *tbl_tcam_ucast) +{ + u32 ucast_cfg1; + + ucast_cfg1 = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S, + tbl_tcam_ucast->tbl_ucast_mac_discard); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_ITEM_VLD_S, + tbl_tcam_ucast->tbl_ucast_item_vld); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OLD_EN_S, + tbl_tcam_ucast->tbl_ucast_old_en); + dsaf_set_bit(ucast_cfg1, DSAF_TBL_UCAST_CFG1_DVC_S, + tbl_tcam_ucast->tbl_ucast_dvc); + dsaf_set_field(ucast_cfg1, DSAF_TBL_UCAST_CFG1_OUT_PORT_M, + DSAF_TBL_UCAST_CFG1_OUT_PORT_S, + tbl_tcam_ucast->tbl_ucast_out_port); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_UCAST_CFG_0_REG, ucast_cfg1); +} + +/** + * hns_dsaf_tbl_line_cfg - tbl + * @dsaf_id: dsa fabric id + * @ptbl_lin: addr + */ +static void hns_dsaf_tbl_line_cfg(struct dsaf_device *dsaf_dev, + struct dsaf_tbl_line_cfg *tbl_lin) +{ + u32 tbl_line; + + tbl_line = dsaf_read_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG); + dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_MAC_DISCARD_S, + tbl_lin->tbl_line_mac_discard); + dsaf_set_bit(tbl_line, DSAF_TBL_LINE_CFG_DVC_S, + tbl_lin->tbl_line_dvc); + dsaf_set_field(tbl_line, DSAF_TBL_LINE_CFG_OUT_PORT_M, + DSAF_TBL_LINE_CFG_OUT_PORT_S, + tbl_lin->tbl_line_out_port); + dsaf_write_dev(dsaf_dev, DSAF_TBL_LIN_CFG_0_REG, tbl_line); +} + +/** + * hns_dsaf_tbl_tcam_mcast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_mcast_pul(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_line_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_line_pul(struct dsaf_device *dsaf_dev) +{ + u32 tbl_pul; + + tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul); + dsaf_set_bit(tbl_pul, DSAF_TBL_PUL_LINE_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, tbl_pul); +} + +/** + * hns_dsaf_tbl_tcam_data_mcast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_data_mcast_pul( + struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_MCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +/** + * hns_dsaf_tbl_tcam_data_ucast_pul - tbl + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_data_ucast_pul( + struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 1); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_DATA_VLD_S, 0); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_UCAST_VLD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_CFG_0_REG, DSAF_CFG_MIX_MODE_S, !!en); +} + +/** + * hns_dsaf_tbl_stat_en - tbl + * @dsaf_id: dsa fabric id + * @ptbl_stat_en: addr + */ +static void hns_dsaf_tbl_stat_en(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_ctrl; + + o_tbl_ctrl = dsaf_read_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_UC_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_MC_LKUP_NUM_EN_S, 1); + dsaf_set_bit(o_tbl_ctrl, DSAF_TBL_DFX_BC_LKUP_NUM_EN_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_DFX_CTRL_0_REG, o_tbl_ctrl); +} + +/** + * hns_dsaf_rocee_bp_en - rocee back press enable + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_rocee_bp_en(struct dsaf_device *dsaf_dev) +{ + dsaf_set_dev_bit(dsaf_dev, DSAF_XGE_CTRL_SIG_CFG_0_REG, + DSAF_FC_XGE_TX_PAUSE_S, 1); +} + +/* set msk for dsaf exception irq*/ +static void hns_dsaf_int_xge_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 mask_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_XGE_INT_MSK_0_REG + 0x4 * chnn_num, mask_set); +} + +static void hns_dsaf_int_ppe_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_PPE_INT_MSK_0_REG + 0x4 * chnn_num, msk_set); +} + +static void hns_dsaf_int_rocee_msk_set(struct dsaf_device *dsaf_dev, + u32 chnn, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, + DSAF_ROCEE_INT_MSK_0_REG + 0x4 * chnn, msk_set); +} + +static void +hns_dsaf_int_tbl_msk_set(struct dsaf_device *dsaf_dev, u32 msk_set) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_MSK_0_REG, msk_set); +} + +/* clr dsaf exception irq*/ +static void hns_dsaf_int_xge_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn_num, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_XGE_INT_SRC_0_REG + 0x4 * chnn_num, int_src); +} + +static void hns_dsaf_int_ppe_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_PPE_INT_SRC_0_REG + 0x4 * chnn, int_src); +} + +static void hns_dsaf_int_rocee_src_clr(struct dsaf_device *dsaf_dev, + u32 chnn, u32 int_src) +{ + dsaf_write_dev(dsaf_dev, + DSAF_ROCEE_INT_SRC_0_REG + 0x4 * chnn, int_src); +} + +static void hns_dsaf_int_tbl_src_clr(struct dsaf_device *dsaf_dev, + u32 int_src) +{ + dsaf_write_dev(dsaf_dev, DSAF_TBL_INT_SRC_0_REG, int_src); +} + +/** + * hns_dsaf_single_line_tbl_cfg - INT + * @dsaf_id: dsa fabric id + * @address: + * @ptbl_line: + */ +static void hns_dsaf_single_line_tbl_cfg( + struct dsaf_device *dsaf_dev, + u32 address, struct dsaf_tbl_line_cfg *ptbl_line) +{ + /*Write Addr*/ + hns_dsaf_tbl_line_addr_cfg(dsaf_dev, address); + + /*Write Line*/ + hns_dsaf_tbl_line_cfg(dsaf_dev, ptbl_line); + + /*Write Plus*/ + hns_dsaf_tbl_line_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_uc_cfg - INT + * @dsaf_id: dsa fabric id + * @address, + * @ptbl_tcam_data, + */ +static void hns_dsaf_tcam_uc_cfg( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + /*Write Tcam Data*/ + hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data); + /*Write Tcam Ucast*/ + hns_dsaf_tbl_tcam_ucast_cfg(dsaf_dev, ptbl_tcam_ucast); + /*Write Plus*/ + hns_dsaf_tbl_tcam_data_ucast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_mc_cfg - INT + * @dsaf_id: dsa fabric id + * @address, + * @ptbl_tcam_data, + * @ptbl_tcam_mcast, + */ +static void hns_dsaf_tcam_mc_cfg( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + /*Write Tcam Data*/ + hns_dsaf_tbl_tcam_data_cfg(dsaf_dev, ptbl_tcam_data); + /*Write Tcam Mcast*/ + hns_dsaf_tbl_tcam_mcast_cfg(dsaf_dev, ptbl_tcam_mcast); + /*Write Plus*/ + hns_dsaf_tbl_tcam_data_mcast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_mc_invld - INT + * @dsaf_id: dsa fabric id + * @address + */ +static void hns_dsaf_tcam_mc_invld(struct dsaf_device *dsaf_dev, u32 address) +{ + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*write tcam mcast*/ + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG, 0); + + /*Write Plus*/ + hns_dsaf_tbl_tcam_mcast_pul(dsaf_dev); +} + +/** + * hns_dsaf_tcam_uc_get - INT + * @dsaf_id: dsa fabric id + * @address + * @ptbl_tcam_data + * @ptbl_tcam_ucast + */ +static void hns_dsaf_tcam_uc_get( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_ucast_cfg *ptbl_tcam_ucast) +{ + u32 tcam_read_data0; + u32 tcam_read_data4; + + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*read tcam item puls*/ + hns_dsaf_tbl_tcam_load_pul(dsaf_dev); + + /*read tcam data*/ + ptbl_tcam_data->tbl_tcam_data_high + = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + ptbl_tcam_data->tbl_tcam_data_low + = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + + /*read tcam mcast*/ + tcam_read_data0 = dsaf_read_dev(dsaf_dev, + DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + tcam_read_data4 = dsaf_read_dev(dsaf_dev, + DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + + ptbl_tcam_ucast->tbl_ucast_item_vld + = dsaf_get_bit(tcam_read_data4, + DSAF_TBL_MCAST_CFG4_ITEM_VLD_S); + ptbl_tcam_ucast->tbl_ucast_old_en + = dsaf_get_bit(tcam_read_data4, DSAF_TBL_MCAST_CFG4_OLD_EN_S); + ptbl_tcam_ucast->tbl_ucast_mac_discard + = dsaf_get_bit(tcam_read_data0, + DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S); + ptbl_tcam_ucast->tbl_ucast_out_port + = dsaf_get_field(tcam_read_data0, + DSAF_TBL_UCAST_CFG1_OUT_PORT_M, + DSAF_TBL_UCAST_CFG1_OUT_PORT_S); + ptbl_tcam_ucast->tbl_ucast_dvc + = dsaf_get_bit(tcam_read_data0, DSAF_TBL_UCAST_CFG1_DVC_S); +} + +/** + * hns_dsaf_tcam_mc_get - INT + * @dsaf_id: dsa fabric id + * @address + * @ptbl_tcam_data + * @ptbl_tcam_ucast + */ +static void hns_dsaf_tcam_mc_get( + struct dsaf_device *dsaf_dev, u32 address, + struct dsaf_tbl_tcam_data *ptbl_tcam_data, + struct dsaf_tbl_tcam_mcast_cfg *ptbl_tcam_mcast) +{ + u32 data_tmp; + + /*Write Addr*/ + hns_dsaf_tbl_tcam_addr_cfg(dsaf_dev, address); + + /*read tcam item puls*/ + hns_dsaf_tbl_tcam_load_pul(dsaf_dev); + + /*read tcam data*/ + ptbl_tcam_data->tbl_tcam_data_high = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + ptbl_tcam_data->tbl_tcam_data_low = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + + /*read tcam mcast*/ + ptbl_tcam_mcast->tbl_mcast_port_msk[0] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[1] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[2] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG); + ptbl_tcam_mcast->tbl_mcast_port_msk[3] = + dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG); + + data_tmp = dsaf_read_dev(dsaf_dev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + ptbl_tcam_mcast->tbl_mcast_item_vld = + dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_ITEM_VLD_S); + ptbl_tcam_mcast->tbl_mcast_old_en = + dsaf_get_bit(data_tmp, DSAF_TBL_MCAST_CFG4_OLD_EN_S); + ptbl_tcam_mcast->tbl_mcast_port_msk[4] = + dsaf_get_field(data_tmp, DSAF_TBL_MCAST_CFG4_VM128_112_M, + DSAF_TBL_MCAST_CFG4_VM128_112_S); +} + +/** + * hns_dsaf_tbl_line_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_line_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + /* defaultly set all lineal mac table entry resulting discard */ + struct dsaf_tbl_line_cfg tbl_line[] = {{1, 0, 0} }; + + for (i = 0; i < DSAF_LINE_SUM; i++) + hns_dsaf_single_line_tbl_cfg(dsaf_dev, i, tbl_line); +} + +/** + * hns_dsaf_tbl_tcam_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_tcam_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + struct dsaf_tbl_tcam_data tcam_data[] = {{0, 0} }; + struct dsaf_tbl_tcam_ucast_cfg tcam_ucast[] = {{0, 0, 0, 0, 0} }; + + /*tcam tbl*/ + for (i = 0; i < DSAF_TCAM_SUM; i++) + hns_dsaf_tcam_uc_cfg(dsaf_dev, i, tcam_data, tcam_ucast); +} + +/** + * hns_dsaf_pfc_en_cfg - dsaf pfc pause cfg + * @mac_cb: mac contrl block + */ +static void hns_dsaf_pfc_en_cfg(struct dsaf_device *dsaf_dev, + int mac_id, int en) +{ + if (!en) + dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0); + else + dsaf_write_dev(dsaf_dev, DSAF_PFC_EN_0_REG + mac_id * 4, 0xff); +} + +/** + * hns_dsaf_tbl_tcam_init - INT + * @dsaf_id: dsa fabric id + * @dsaf_mode + */ +static void hns_dsaf_comm_init(struct dsaf_device *dsaf_dev) +{ + u32 i; + u32 o_dsaf_cfg; + + o_dsaf_cfg = dsaf_read_dev(dsaf_dev, DSAF_CFG_0_REG); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_EN_S, dsaf_dev->dsaf_en); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_TC_MODE_S, dsaf_dev->dsaf_tc_mode); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_CRC_EN_S, 0); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_MIX_MODE_S, 0); + dsaf_set_bit(o_dsaf_cfg, DSAF_CFG_LOCA_ADDR_EN_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_CFG_0_REG, o_dsaf_cfg); + + hns_dsaf_reg_cnt_clr_ce(dsaf_dev, 1); + hns_dsaf_stp_port_type_cfg(dsaf_dev, DSAF_STP_PORT_TYPE_FORWARD); + + /* set 22 queue per tx ppe engine, only used in switch mode */ + hns_dsaf_ppe_qid_cfg(dsaf_dev, DSAF_DEFAUTL_QUEUE_NUM_PER_PPE); + + /* set promisc def queue id */ + hns_dsaf_mix_def_qid_cfg(dsaf_dev); + + /* in non switch mode, set all port to access mode */ + hns_dsaf_sw_port_type_cfg(dsaf_dev, DSAF_SW_PORT_TYPE_NON_VLAN); + + /*set dsaf pfc to 0 for parseing rx pause*/ + for (i = 0; i < DSAF_COMM_CHN; i++) + hns_dsaf_pfc_en_cfg(dsaf_dev, i, 0); + + /*msk and clr exception irqs */ + for (i = 0; i < DSAF_COMM_CHN; i++) { + hns_dsaf_int_xge_src_clr(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_ppe_src_clr(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_rocee_src_clr(dsaf_dev, i, 0xfffffffful); + + hns_dsaf_int_xge_msk_set(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_ppe_msk_set(dsaf_dev, i, 0xfffffffful); + hns_dsaf_int_rocee_msk_set(dsaf_dev, i, 0xfffffffful); + } + hns_dsaf_int_tbl_src_clr(dsaf_dev, 0xfffffffful); + hns_dsaf_int_tbl_msk_set(dsaf_dev, 0xfffffffful); +} + +/** + * hns_dsaf_inode_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev) +{ + u32 reg; + u32 tc_cfg; + u32 i; + + if (dsaf_dev->dsaf_tc_mode == HRD_DSAF_4TC_MODE) + tc_cfg = HNS_DSAF_I4TC_CFG; + else + tc_cfg = HNS_DSAF_I8TC_CFG; + + for (i = 0; i < DSAF_INODE_NUM; i++) { + reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i; + dsaf_set_dev_field(dsaf_dev, reg, DSAF_INODE_IN_PORT_NUM_M, + DSAF_INODE_IN_PORT_NUM_S, i % DSAF_XGE_NUM); + + reg = DSAF_INODE_PRI_TC_CFG_0_REG + 0x80 * i; + dsaf_write_dev(dsaf_dev, reg, tc_cfg); + } +} + +/** + * hns_dsaf_sbm_init - INT + * @dsaf_id: dsa fabric id + */ +static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev) +{ + u32 flag; + u32 cnt = 0; + int ret; + + hns_dsaf_sbm_bp_wl_cfg(dsaf_dev); + + /* enable sbm chanel, disable sbm chanel shcut function*/ + hns_dsaf_sbm_cfg(dsaf_dev); + + /* enable sbm mib */ + ret = hns_dsaf_sbm_cfg_mib_en(dsaf_dev); + if (ret) { + dev_err(dsaf_dev->dev, + "hns_dsaf_sbm_cfg_mib_en fail,%s, ret=%d\n", + dsaf_dev->ae_dev.name, ret); + return ret; + } + + /* enable sbm initial link sram */ + hns_dsaf_sbm_link_sram_init_en(dsaf_dev); + + do { + usleep_range(200, 210);/*udelay(200);*/ + flag = dsaf_read_dev(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG); + cnt++; + } while (flag != DSAF_SRAM_INIT_FINISH_FLAG && cnt < DSAF_CFG_READ_CNT); + + if (flag != DSAF_SRAM_INIT_FINISH_FLAG) { + dev_err(dsaf_dev->dev, + "hns_dsaf_sbm_init fail %s, flag=%d, cnt=%d\n", + dsaf_dev->ae_dev.name, flag, cnt); + return -ENODEV; + } + + hns_dsaf_rocee_bp_en(dsaf_dev); + + return 0; +} + +/** + * hns_dsaf_tbl_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_tbl_init(struct dsaf_device *dsaf_dev) +{ + hns_dsaf_tbl_stat_en(dsaf_dev); + + hns_dsaf_tbl_tcam_init(dsaf_dev); + hns_dsaf_tbl_line_init(dsaf_dev); +} + +/** + * hns_dsaf_voq_init - INT + * @dsaf_id: dsa fabric id + */ +static void hns_dsaf_voq_init(struct dsaf_device *dsaf_dev) +{ + hns_dsaf_voq_bp_all_thrd_cfg(dsaf_dev); +} + +/** + * hns_dsaf_init_hw - init dsa fabric hardware + * @dsaf_dev: dsa fabric device struct pointer + */ +static int hns_dsaf_init_hw(struct dsaf_device *dsaf_dev) +{ + int ret; + + dev_dbg(dsaf_dev->dev, + "hns_dsaf_init_hw begin %s !\n", dsaf_dev->ae_dev.name); + + hns_dsaf_rst(dsaf_dev, 0); + mdelay(10); + hns_dsaf_rst(dsaf_dev, 1); + + hns_dsaf_comm_init(dsaf_dev); + + /*init XBAR_INODE*/ + hns_dsaf_inode_init(dsaf_dev); + + /*init SBM*/ + ret = hns_dsaf_sbm_init(dsaf_dev); + if (ret) + return ret; + + /*init TBL*/ + hns_dsaf_tbl_init(dsaf_dev); + + /*init VOQ*/ + hns_dsaf_voq_init(dsaf_dev); + + return 0; +} + +/** + * hns_dsaf_remove_hw - uninit dsa fabric hardware + * @dsaf_dev: dsa fabric device struct pointer + */ +static void hns_dsaf_remove_hw(struct dsaf_device *dsaf_dev) +{ + /*reset*/ + hns_dsaf_rst(dsaf_dev, 0); +} + +/** + * hns_dsaf_init - init dsa fabric + * @dsaf_dev: dsa fabric device struct pointer + * retuen 0 - success , negative --fail + */ +static int hns_dsaf_init(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + u32 i; + int ret; + + ret = hns_dsaf_init_hw(dsaf_dev); + if (ret) + return ret; + + /* malloc mem for tcam mac key(vlan+mac) */ + priv->soft_mac_tbl = vzalloc(sizeof(*priv->soft_mac_tbl) + * DSAF_TCAM_SUM); + if (!priv->soft_mac_tbl) { + ret = -ENOMEM; + goto remove_hw; + } + + /*all entry invall */ + for (i = 0; i < DSAF_TCAM_SUM; i++) + (priv->soft_mac_tbl + i)->index = DSAF_INVALID_ENTRY_IDX; + + return 0; + +remove_hw: + hns_dsaf_remove_hw(dsaf_dev); + return ret; +} + +/** + * hns_dsaf_free - free dsa fabric + * @dsaf_dev: dsa fabric device struct pointer + */ +static void hns_dsaf_free(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + + hns_dsaf_remove_hw(dsaf_dev); + + /* free all mac mem */ + vfree(priv->soft_mac_tbl); + priv->soft_mac_tbl = NULL; +} + +/** + * hns_dsaf_find_soft_mac_entry - find dsa fabric soft entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_key: mac entry struct pointer + */ +static u16 hns_dsaf_find_soft_mac_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_tbl_tcam_key *mac_key) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry; + u32 i; + + soft_mac_entry = priv->soft_mac_tbl; + for (i = 0; i < DSAF_TCAM_SUM; i++) { + /* invall tab entry */ + if ((soft_mac_entry->index != DSAF_INVALID_ENTRY_IDX) && + (soft_mac_entry->tcam_key.high.val == mac_key->high.val) && + (soft_mac_entry->tcam_key.low.val == mac_key->low.val)) + /* return find result --soft index */ + return soft_mac_entry->index; + + soft_mac_entry++; + } + return DSAF_INVALID_ENTRY_IDX; +} + +/** + * hns_dsaf_find_empty_mac_entry - search dsa fabric soft empty-entry + * @dsaf_dev: dsa fabric device struct pointer + */ +static u16 hns_dsaf_find_empty_mac_entry(struct dsaf_device *dsaf_dev) +{ + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry; + u32 i; + + soft_mac_entry = priv->soft_mac_tbl; + for (i = 0; i < DSAF_TCAM_SUM; i++) { + /* inv all entry */ + if (soft_mac_entry->index == DSAF_INVALID_ENTRY_IDX) + /* return find result --soft index */ + return i; + + soft_mac_entry++; + } + return DSAF_INVALID_ENTRY_IDX; +} + +/** + * hns_dsaf_set_mac_key - set mac key + * @dsaf_dev: dsa fabric device struct pointer + * @mac_key: tcam key pointer + * @vlan_id: vlan id + * @in_port_num: input port num + * @addr: mac addr + */ +static void hns_dsaf_set_mac_key( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_tbl_tcam_key *mac_key, u16 vlan_id, u8 in_port_num, + u8 *addr) +{ + u8 port; + + if (dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) + /*DSAF mode : in port id fixed 0*/ + port = 0; + else + /*non-dsaf mode*/ + port = in_port_num; + + mac_key->high.bits.mac_0 = addr[0]; + mac_key->high.bits.mac_1 = addr[1]; + mac_key->high.bits.mac_2 = addr[2]; + mac_key->high.bits.mac_3 = addr[3]; + mac_key->low.bits.mac_4 = addr[4]; + mac_key->low.bits.mac_5 = addr[5]; + mac_key->low.bits.vlan = vlan_id; + mac_key->low.bits.port = port; +} + +/** + * hns_dsaf_set_mac_uc_entry - set mac uc-entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: uc-mac entry + */ +int hns_dsaf_set_mac_uc_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_ucast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + + /* mac addr check */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr) || + MAC_IS_MULTICAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set_uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n", + dsaf_dev->ae_dev.name, mac_entry->addr[0], + mac_entry->addr[1], mac_entry->addr[2], + mac_entry->addr[3], mac_entry->addr[4], + mac_entry->addr[5]); + return -EINVAL; + } + + /* config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /* entry ie exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if has not inv entry,find a empty entry */ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /* has not empty,return error */ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + } + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /* config hardware entry */ + mac_data.tbl_ucast_item_vld = 1; + mac_data.tbl_ucast_mac_discard = 0; + mac_data.tbl_ucast_old_en = 0; + /* default config dvc to 0 */ + mac_data.tbl_ucast_dvc = 0; + mac_data.tbl_ucast_out_port = mac_entry->port_num; + hns_dsaf_tcam_uc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /* config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_set_mac_mc_entry - set mac mc-entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mc-mac entry + */ +int hns_dsaf_set_mac_mc_entry( + struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + + /* mac addr check */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n", + dsaf_dev->ae_dev.name, mac_entry->addr[0], + mac_entry->addr[1], mac_entry->addr[2], + mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, + mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /* entry ie exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot, find enpty entry*/ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot empty, error*/ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + + /* config hardware entry */ + memset(mac_data.tbl_mcast_port_msk, + 0, sizeof(mac_data.tbl_mcast_port_msk)); + } else { + /* config hardware entry */ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + } + mac_data.tbl_mcast_old_en = 0; + mac_data.tbl_mcast_item_vld = 1; + dsaf_set_field(mac_data.tbl_mcast_port_msk[0], + 0x3F, 0, mac_entry->port_mask[0]); + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /* config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_add_mac_mc_port - add mac mc-port + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mc-mac entry + */ +int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + int mskid; + + /*chechk mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "set_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key( + dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + memset(&mac_data, 0, sizeof(struct dsaf_tbl_tcam_mcast_cfg)); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot , find a empty*/ + entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*if hasnot empty, error*/ + dev_err(dsaf_dev->dev, + "set_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val); + return -EINVAL; + } + } else { + /*if exist, add in */ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + } + /* config hardware entry */ + if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { + mskid = mac_entry->port_num; + } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) { + mskid = mac_entry->port_num - + DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM; + } else { + dev_err(dsaf_dev->dev, + "%s,pnum(%d)error,key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_entry->port_num, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 1); + mac_data.tbl_mcast_old_en = 0; + mac_data.tbl_mcast_item_vld = 1; + + dev_dbg(dsaf_dev->dev, + "set_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + + /*config software entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = entry_index; + soft_mac_entry->tcam_key.high.val = mac_key.high.val; + soft_mac_entry->tcam_key.low.val = mac_key.low.val; + + return 0; +} + +/** + * hns_dsaf_del_mac_entry - del mac mc-port + * @dsaf_dev: dsa fabric device struct pointer + * @vlan_id: vlian id + * @in_port_num: input port num + * @addr : mac addr + */ +int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, + u8 in_port_num, u8 *addr) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(addr) || MAC_IS_BROADCAST(addr)) { + dev_err(dsaf_dev->dev, + "del_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, addr); + + /*exist ?*/ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*not exist, error */ + dev_err(dsaf_dev->dev, + "del_mac_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "del_mac_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*do del opt*/ + hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index); + + /*del soft emtry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; + + return 0; +} + +/** + * hns_dsaf_del_mac_mc_port - del mac mc- port + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + struct dsaf_drv_priv *priv = + (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); + struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; + u16 vlan_id; + u8 in_port_num; + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_drv_tbl_tcam_key tmp_mac_key; + int mskid; + const u8 empty_msk[sizeof(mac_data.tbl_mcast_port_msk)] = {0}; + + if (!(void *)mac_entry) { + dev_err(dsaf_dev->dev, + "hns_dsaf_del_mac_mc_port mac_entry is NULL\n"); + return -EINVAL; + } + + /*get key info*/ + vlan_id = mac_entry->in_vlan_id; + in_port_num = mac_entry->in_port_num; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "del_port failed, addr %02x:%02x:%02x:%02x:%02x:%02x!\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, vlan_id, in_port_num, + mac_entry->addr); + + /*check is exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*find none */ + dev_err(dsaf_dev->dev, + "find_soft_mac_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + + dev_dbg(dsaf_dev->dev, + "del_mac_mc_port, %s key(%#x:%#x) index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry*/ + hns_dsaf_tcam_mc_get( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&tmp_mac_key), &mac_data); + + /*del the port*/ + if (mac_entry->port_num < DSAF_SERVICE_NW_NUM) { + mskid = mac_entry->port_num; + } else if (mac_entry->port_num >= DSAF_BASE_INNER_PORT_NUM) { + mskid = mac_entry->port_num - + DSAF_BASE_INNER_PORT_NUM + DSAF_SERVICE_NW_NUM; + } else { + dev_err(dsaf_dev->dev, + "%s,pnum(%d)error,key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_entry->port_num, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dsaf_set_bit(mac_data.tbl_mcast_port_msk[mskid / 32], mskid % 32, 0); + + /*check non port, do del entry */ + if (!memcmp(mac_data.tbl_mcast_port_msk, empty_msk, + sizeof(mac_data.tbl_mcast_port_msk))) { + hns_dsaf_tcam_mc_invld(dsaf_dev, entry_index); + + /* del soft entry */ + soft_mac_entry += entry_index; + soft_mac_entry->index = DSAF_INVALID_ENTRY_IDX; + } else { /* not zer, just del port, updata*/ + hns_dsaf_tcam_mc_cfg( + dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)(&mac_key), &mac_data); + } + + return 0; +} + +/** + * hns_dsaf_get_mac_uc_entry - get mac uc entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_ucast_cfg mac_data; + + /* check macaddr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /*find none, error */ + dev_err(dsaf_dev->dev, + "get_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, + mac_key.high.val, mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "get_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry*/ + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + mac_entry->port_num = mac_data.tbl_ucast_out_port; + + return 0; +} + +/** + * hns_dsaf_get_mac_mc_entry - get mac mc entry + * @dsaf_dev: dsa fabric device struct pointer + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + u16 entry_index = DSAF_INVALID_ENTRY_IDX; + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_mcast_cfg mac_data; + + /*check mac addr */ + if (MAC_IS_ALL_ZEROS(mac_entry->addr) || + MAC_IS_BROADCAST(mac_entry->addr)) { + dev_err(dsaf_dev->dev, + "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n", + mac_entry->addr[0], mac_entry->addr[1], + mac_entry->addr[2], mac_entry->addr[3], + mac_entry->addr[4], mac_entry->addr[5]); + return -EINVAL; + } + + /*config key */ + hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, + mac_entry->in_port_num, mac_entry->addr); + + /*check exist? */ + entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); + if (entry_index == DSAF_INVALID_ENTRY_IDX) { + /* find none, error */ + dev_err(dsaf_dev->dev, + "get_mac_uc_entry failed, %s Mac key(%#x:%#x)\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val); + return -EINVAL; + } + dev_dbg(dsaf_dev->dev, + "get_mac_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", + dsaf_dev->ae_dev.name, mac_key.high.val, + mac_key.low.val, entry_index); + + /*read entry */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + + mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; + return 0; +} + +/** + * hns_dsaf_get_mac_entry_by_index - get mac entry by tab index + * @dsaf_dev: dsa fabric device struct pointer + * @entry_index: tab entry index + * @mac_entry: mac entry + */ +int hns_dsaf_get_mac_entry_by_index( + struct dsaf_device *dsaf_dev, + u16 entry_index, struct dsaf_drv_mac_multi_dest_entry *mac_entry) +{ + struct dsaf_drv_tbl_tcam_key mac_key; + + struct dsaf_tbl_tcam_mcast_cfg mac_data; + struct dsaf_tbl_tcam_ucast_cfg mac_uc_data; + char mac_addr[MAC_NUM_OCTETS_PER_ADDR] = {0}; + + if (entry_index >= DSAF_TCAM_SUM) { + /* find none, del error */ + dev_err(dsaf_dev->dev, "get_uc_entry failed, %s\n", + dsaf_dev->ae_dev.name); + return -EINVAL; + } + + /* mc entry, do read opt */ + hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, &mac_data); + + mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; + + /***get mac addr*/ + mac_addr[0] = mac_key.high.bits.mac_0; + mac_addr[1] = mac_key.high.bits.mac_1; + mac_addr[2] = mac_key.high.bits.mac_2; + mac_addr[3] = mac_key.high.bits.mac_3; + mac_addr[4] = mac_key.low.bits.mac_4; + mac_addr[5] = mac_key.low.bits.mac_5; + /**is mc or uc*/ + if (MAC_IS_MULTICAST((u8 *)mac_addr) || + MAC_IS_L3_MULTICAST((u8 *)mac_addr)) { + /**mc donot do*/ + } else { + /*is not mc, just uc... */ + hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, + (struct dsaf_tbl_tcam_data *)&mac_key, + &mac_uc_data); + mac_entry->port_mask[0] = (1 << mac_uc_data.tbl_ucast_out_port); + } + + return 0; +} + +static struct dsaf_device *hns_dsaf_alloc_dev(struct device *dev, + size_t sizeof_priv) +{ + struct dsaf_device *dsaf_dev; + + dsaf_dev = devm_kzalloc(dev, + sizeof(*dsaf_dev) + sizeof_priv, GFP_KERNEL); + if (unlikely(!dsaf_dev)) { + dsaf_dev = ERR_PTR(-ENOMEM); + } else { + dsaf_dev->dev = dev; + dev_set_drvdata(dev, dsaf_dev); + } + + return dsaf_dev; +} + +/** + * hns_dsaf_free_dev - free dev mem + * @dev: struct device pointer + */ +static void hns_dsaf_free_dev(struct dsaf_device *dsaf_dev) +{ + (void)dev_set_drvdata(dsaf_dev->dev, NULL); +} + +/** + * dsaf_pfc_unit_cnt - set pfc unit count + * @dsaf_id: dsa fabric id + * @pport_rate: value array + * @pdsaf_pfc_unit_cnt: value array + */ +static void hns_dsaf_pfc_unit_cnt(struct dsaf_device *dsaf_dev, int mac_id, + enum dsaf_port_rate_mode rate) +{ + u32 unit_cnt; + + switch (rate) { + case DSAF_PORT_RATE_10000: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE; + break; + case DSAF_PORT_RATE_1000: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000; + break; + case DSAF_PORT_RATE_2500: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000; + break; + default: + unit_cnt = HNS_DSAF_PFC_UNIT_CNT_FOR_XGE; + } + + dsaf_set_dev_field(dsaf_dev, + (DSAF_PFC_UNIT_CNT_0_REG + 0x4 * (u64)mac_id), + DSAF_PFC_UNINT_CNT_M, DSAF_PFC_UNINT_CNT_S, + unit_cnt); +} + +/** + * dsaf_port_work_rate_cfg - fifo + * @dsaf_id: dsa fabric id + * @xge_ge_work_mode + */ +void hns_dsaf_port_work_rate_cfg(struct dsaf_device *dsaf_dev, int mac_id, + enum dsaf_port_rate_mode rate_mode) +{ + u32 port_work_mode; + + port_work_mode = dsaf_read_dev( + dsaf_dev, DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id); + + if (rate_mode == DSAF_PORT_RATE_10000) + dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 1); + else + dsaf_set_bit(port_work_mode, DSAF_XGE_GE_WORK_MODE_S, 0); + + dsaf_write_dev(dsaf_dev, + DSAF_XGE_GE_WORK_MODE_0_REG + 0x4 * (u64)mac_id, + port_work_mode); + + hns_dsaf_pfc_unit_cnt(dsaf_dev, mac_id, rate_mode); +} + +/** + * hns_dsaf_fix_mac_mode - dsaf modify mac mode + * @mac_cb: mac contrl block + */ +void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb) +{ + enum dsaf_port_rate_mode mode; + struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; + int mac_id = mac_cb->mac_id; + + if (mac_cb->mac_type != HNAE_PORT_SERVICE) + return; + if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) + mode = DSAF_PORT_RATE_10000; + else + mode = DSAF_PORT_RATE_1000; + + hns_dsaf_port_work_rate_cfg(dsaf_dev, mac_id, mode); +} + +void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 node_num) +{ + struct dsaf_hw_stats *hw_stats + = &dsaf_dev->hw_stats[node_num]; + + hw_stats->pad_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_PAD_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->man_pkts += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pkts += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pkt_id += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_PID_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rx_pause_frame += dsaf_read_dev(dsaf_dev, + DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->release_buf_num += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_RELS_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->sbm_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SBM_DROP_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->crc_false += dsaf_read_dev(dsaf_dev, + DSAF_INODE_CRC_FALSE_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->bp_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_BP_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->rslt_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_RSLT_DISCARD_NUM_0_REG + 0x80 * (u64)node_num); + hw_stats->local_addr_false += dsaf_read_dev(dsaf_dev, + DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + 0x80 * (u64)node_num); + + hw_stats->vlan_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + 0x80 * (u64)node_num); + hw_stats->stp_drop += dsaf_read_dev(dsaf_dev, + DSAF_INODE_IN_DATA_STP_DISC_0_REG + 0x80 * (u64)node_num); + + hw_stats->tx_pkts += dsaf_read_dev(dsaf_dev, + DSAF_XOD_RCVPKT_CNT_0_REG + 0x90 * (u64)node_num); +} + +/** + *hns_dsaf_get_regs - dump dsaf regs + *@dsaf_dev: dsaf device + *@data:data for value of regs + */ +void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data) +{ + u32 i = 0; + u32 j; + u32 *p = data; + + /* dsaf common registers */ + p[0] = dsaf_read_dev(ddev, DSAF_SRAM_INIT_OVER_0_REG); + p[1] = dsaf_read_dev(ddev, DSAF_CFG_0_REG); + p[2] = dsaf_read_dev(ddev, DSAF_ECC_ERR_INVERT_0_REG); + p[3] = dsaf_read_dev(ddev, DSAF_ABNORMAL_TIMEOUT_0_REG); + p[4] = dsaf_read_dev(ddev, DSAF_FSM_TIMEOUT_0_REG); + p[5] = dsaf_read_dev(ddev, DSAF_DSA_REG_CNT_CLR_CE_REG); + p[6] = dsaf_read_dev(ddev, DSAF_DSA_SBM_INF_FIFO_THRD_REG); + p[7] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_SEL_REG); + p[8] = dsaf_read_dev(ddev, DSAF_DSA_SRAM_1BIT_ECC_CNT_REG); + + p[9] = dsaf_read_dev(ddev, DSAF_PFC_EN_0_REG + port * 4); + p[10] = dsaf_read_dev(ddev, DSAF_PFC_UNIT_CNT_0_REG + port * 4); + p[11] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4); + p[12] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4); + p[13] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4); + p[14] = dsaf_read_dev(ddev, DSAF_XGE_INT_MSK_0_REG + port * 4); + p[15] = dsaf_read_dev(ddev, DSAF_PPE_INT_MSK_0_REG + port * 4); + p[16] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_MSK_0_REG + port * 4); + p[17] = dsaf_read_dev(ddev, DSAF_XGE_INT_SRC_0_REG + port * 4); + p[18] = dsaf_read_dev(ddev, DSAF_PPE_INT_SRC_0_REG + port * 4); + p[19] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_SRC_0_REG + port * 4); + p[20] = dsaf_read_dev(ddev, DSAF_XGE_INT_STS_0_REG + port * 4); + p[21] = dsaf_read_dev(ddev, DSAF_PPE_INT_STS_0_REG + port * 4); + p[22] = dsaf_read_dev(ddev, DSAF_ROCEE_INT_STS_0_REG + port * 4); + p[23] = dsaf_read_dev(ddev, DSAF_PPE_QID_CFG_0_REG + port * 4); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) + p[24 + i] = dsaf_read_dev(ddev, + DSAF_SW_PORT_TYPE_0_REG + i * 4); + + p[32] = dsaf_read_dev(ddev, DSAF_MIX_DEF_QID_0_REG + port * 4); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) + p[33 + i] = dsaf_read_dev(ddev, + DSAF_PORT_DEF_VLAN_0_REG + i * 4); + + for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++) + p[41 + i] = dsaf_read_dev(ddev, + DSAF_VM_DEF_VLAN_0_REG + i * 4); + + /* dsaf inode registers */ + p[170] = dsaf_read_dev(ddev, DSAF_INODE_CUT_THROUGH_CFG_0_REG); + + p[171] = dsaf_read_dev(ddev, + DSAF_INODE_ECC_ERR_ADDR_0_REG + port * 0x80); + + for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[172 + i] = dsaf_read_dev(ddev, + DSAF_INODE_IN_PORT_NUM_0_REG + j * 0x80); + p[175 + i] = dsaf_read_dev(ddev, + DSAF_INODE_PRI_TC_CFG_0_REG + j * 0x80); + p[178 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BP_STATUS_0_REG + j * 0x80); + p[181 + i] = dsaf_read_dev(ddev, + DSAF_INODE_PAD_DISCARD_NUM_0_REG + j * 0x80); + p[184 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_MAN_NUM_0_REG + j * 0x80); + p[187 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_PKT_NUM_0_REG + j * 0x80); + p[190 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_PID_NUM_0_REG + j * 0x80); + p[193 + i] = dsaf_read_dev(ddev, + DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG + j * 0x80); + p[196 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_RELS_NUM_0_REG + j * 0x80); + p[199 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SBM_DROP_NUM_0_REG + j * 0x80); + p[202 + i] = dsaf_read_dev(ddev, + DSAF_INODE_CRC_FALSE_NUM_0_REG + j * 0x80); + p[205 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BP_DISCARD_NUM_0_REG + j * 0x80); + p[208 + i] = dsaf_read_dev(ddev, + DSAF_INODE_RSLT_DISCARD_NUM_0_REG + j * 0x80); + p[211 + i] = dsaf_read_dev(ddev, + DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG + j * 0x80); + p[214 + i] = dsaf_read_dev(ddev, + DSAF_INODE_VOQ_OVER_NUM_0_REG + j * 0x80); + p[217 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BD_SAVE_STATUS_0_REG + j * 4); + p[220 + i] = dsaf_read_dev(ddev, + DSAF_INODE_BD_ORDER_STATUS_0_REG + j * 4); + p[223 + i] = dsaf_read_dev(ddev, + DSAF_INODE_SW_VLAN_TAG_DISC_0_REG + j * 4); + p[224 + i] = dsaf_read_dev(ddev, + DSAF_INODE_IN_DATA_STP_DISC_0_REG + j * 4); + } + + p[227] = dsaf_read_dev(ddev, DSAF_INODE_GE_FC_EN_0_REG + port * 4); + + for (i = 0; i < DSAF_INODE_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[228 + i] = dsaf_read_dev(ddev, + DSAF_INODE_VC0_IN_PKT_NUM_0_REG + j * 4); + } + + p[231] = dsaf_read_dev(ddev, + DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4); + + /* dsaf inode registers */ + for (i = 0; i < DSAF_SBM_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[232 + i] = dsaf_read_dev(ddev, + DSAF_SBM_CFG_REG_0_REG + j * 0x80); + p[235 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + j * 0x80); + p[238 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_1_REG_0_REG + j * 0x80); + p[241 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + j * 0x80); + p[244 + i] = dsaf_read_dev(ddev, + DSAF_SBM_FREE_CNT_0_0_REG + j * 0x80); + p[245 + i] = dsaf_read_dev(ddev, + DSAF_SBM_FREE_CNT_1_0_REG + j * 0x80); + p[248 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_0_0_REG + j * 0x80); + p[251 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_1_0_REG + j * 0x80); + p[254 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_2_0_REG + j * 0x80); + p[257 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CNT_3_0_REG + j * 0x80); + p[260 + i] = dsaf_read_dev(ddev, + DSAF_SBM_INER_ST_0_REG + j * 0x80); + p[263 + i] = dsaf_read_dev(ddev, + DSAF_SBM_MIB_REQ_FAILED_TC_0_REG + j * 0x80); + p[266 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_CNT_0_REG + j * 0x80); + p[269 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_DROP_CNT_0_REG + j * 0x80); + p[272 + i] = dsaf_read_dev(ddev, + DSAF_SBM_INF_OUTPORT_CNT_0_REG + j * 0x80); + p[275 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG + j * 0x80); + p[278 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG + j * 0x80); + p[281 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG + j * 0x80); + p[284 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG + j * 0x80); + p[287 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG + j * 0x80); + p[290 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG + j * 0x80); + p[293 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG + j * 0x80); + p[296 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG + j * 0x80); + p[299 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_REQ_CNT_0_REG + j * 0x80); + p[302 + i] = dsaf_read_dev(ddev, + DSAF_SBM_LNK_RELS_CNT_0_REG + j * 0x80); + p[305 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_3_REG_0_REG + j * 0x80); + p[308 + i] = dsaf_read_dev(ddev, + DSAF_SBM_BP_CFG_4_REG_0_REG + j * 0x80); + } + + /* dsaf onode registers */ + for (i = 0; i < DSAF_XOD_NUM; i++) { + p[311 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG + j * 0x90); + p[319 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG + j * 0x90); + p[327 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG + j * 0x90); + p[335 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG + j * 0x90); + p[343 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG + j * 0x90); + p[351 + i] = dsaf_read_dev(ddev, + DSAF_XOD_ETS_TOKEN_CFG_0_REG + j * 0x90); + } + + p[359] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_0_0_REG + port * 0x90); + p[360] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_1_0_REG + port * 0x90); + p[361] = dsaf_read_dev(ddev, DSAF_XOD_PFS_CFG_2_0_REG + port * 0x90); + + for (i = 0; i < DSAF_XOD_BIG_NUM / DSAF_COMM_CHN; i++) { + j = i * DSAF_COMM_CHN + port; + p[362 + i] = dsaf_read_dev(ddev, + DSAF_XOD_GNT_L_0_REG + j * 0x90); + p[365 + i] = dsaf_read_dev(ddev, + DSAF_XOD_GNT_H_0_REG + j * 0x90); + p[368 + i] = dsaf_read_dev(ddev, + DSAF_XOD_CONNECT_STATE_0_REG + j * 0x90); + p[371 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVPKT_CNT_0_REG + j * 0x90); + p[374 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC0_CNT_0_REG + j * 0x90); + p[377 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC1_CNT_0_REG + j * 0x90); + p[380 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC2_CNT_0_REG + j * 0x90); + p[383 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVTC3_CNT_0_REG + j * 0x90); + p[386 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVVC0_CNT_0_REG + j * 0x90); + p[389 + i] = dsaf_read_dev(ddev, + DSAF_XOD_RCVVC1_CNT_0_REG + j * 0x90); + } + + p[392] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN0_CNT_0_REG + port * 0x90); + p[393] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN1_CNT_0_REG + port * 0x90); + p[394] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN2_CNT_0_REG + port * 0x90); + p[395] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN3_CNT_0_REG + port * 0x90); + p[396] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN4_CNT_0_REG + port * 0x90); + p[397] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN5_CNT_0_REG + port * 0x90); + p[398] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN6_CNT_0_REG + port * 0x90); + p[399] = dsaf_read_dev(ddev, + DSAF_XOD_XGE_RCVIN7_CNT_0_REG + port * 0x90); + p[400] = dsaf_read_dev(ddev, + DSAF_XOD_PPE_RCVIN0_CNT_0_REG + port * 0x90); + p[401] = dsaf_read_dev(ddev, + DSAF_XOD_PPE_RCVIN1_CNT_0_REG + port * 0x90); + p[402] = dsaf_read_dev(ddev, + DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG + port * 0x90); + p[403] = dsaf_read_dev(ddev, + DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG + port * 0x90); + p[404] = dsaf_read_dev(ddev, + DSAF_XOD_FIFO_STATUS_0_REG + port * 0x90); + + /* dsaf voq registers */ + for (i = 0; i < DSAF_VOQ_NUM / DSAF_COMM_CHN; i++) { + j = (i * DSAF_COMM_CHN + port) * 0x90; + p[405 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ECC_INVERT_EN_0_REG + j); + p[408 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_SRAM_PKT_NUM_0_REG + j); + p[411 + i] = dsaf_read_dev(ddev, DSAF_VOQ_IN_PKT_NUM_0_REG + j); + p[414 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_OUT_PKT_NUM_0_REG + j); + p[417 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ECC_ERR_ADDR_0_REG + j); + p[420 + i] = dsaf_read_dev(ddev, DSAF_VOQ_BP_STATUS_0_REG + j); + p[423 + i] = dsaf_read_dev(ddev, DSAF_VOQ_SPUP_IDLE_0_REG + j); + p[426 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_XGE_XOD_REQ_0_0_REG + j); + p[429 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_XGE_XOD_REQ_1_0_REG + j); + p[432 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_PPE_XOD_REQ_0_REG + j); + p[435 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_ROCEE_XOD_REQ_0_REG + j); + p[438 + i] = dsaf_read_dev(ddev, + DSAF_VOQ_BP_ALL_THRD_0_REG + j); + } + + /* dsaf tbl registers */ + p[441] = dsaf_read_dev(ddev, DSAF_TBL_CTRL_0_REG); + p[442] = dsaf_read_dev(ddev, DSAF_TBL_INT_MSK_0_REG); + p[443] = dsaf_read_dev(ddev, DSAF_TBL_INT_SRC_0_REG); + p[444] = dsaf_read_dev(ddev, DSAF_TBL_INT_STS_0_REG); + p[445] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_ADDR_0_REG); + p[446] = dsaf_read_dev(ddev, DSAF_TBL_LINE_ADDR_0_REG); + p[447] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_HIGH_0_REG); + p[448] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_LOW_0_REG); + p[449] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_4_0_REG); + p[450] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_3_0_REG); + p[451] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_2_0_REG); + p[452] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_1_0_REG); + p[453] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_MCAST_CFG_0_0_REG); + p[454] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_UCAST_CFG_0_REG); + p[455] = dsaf_read_dev(ddev, DSAF_TBL_LIN_CFG_0_REG); + p[456] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_HIGH_0_REG); + p[457] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RDATA_LOW_0_REG); + p[458] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA4_0_REG); + p[459] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA3_0_REG); + p[460] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA2_0_REG); + p[461] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA1_0_REG); + p[462] = dsaf_read_dev(ddev, DSAF_TBL_TCAM_RAM_RDATA0_0_REG); + p[463] = dsaf_read_dev(ddev, DSAF_TBL_LIN_RDATA_0_REG); + + for (i = 0; i < DSAF_SW_PORT_NUM; i++) { + j = i * 0x8; + p[464 + 2 * i] = dsaf_read_dev(ddev, + DSAF_TBL_DA0_MIS_INFO1_0_REG + j); + p[465 + 2 * i] = dsaf_read_dev(ddev, + DSAF_TBL_DA0_MIS_INFO0_0_REG + j); + } + + p[480] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO2_0_REG); + p[481] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO1_0_REG); + p[482] = dsaf_read_dev(ddev, DSAF_TBL_SA_MIS_INFO0_0_REG); + p[483] = dsaf_read_dev(ddev, DSAF_TBL_PUL_0_REG); + p[484] = dsaf_read_dev(ddev, DSAF_TBL_OLD_RSLT_0_REG); + p[485] = dsaf_read_dev(ddev, DSAF_TBL_OLD_SCAN_VAL_0_REG); + p[486] = dsaf_read_dev(ddev, DSAF_TBL_DFX_CTRL_0_REG); + p[487] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_0_REG); + p[488] = dsaf_read_dev(ddev, DSAF_TBL_DFX_STAT_2_0_REG); + p[489] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_I_0_REG); + p[490] = dsaf_read_dev(ddev, DSAF_TBL_LKUP_NUM_O_0_REG); + p[491] = dsaf_read_dev(ddev, DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG); + + /* dsaf other registers */ + p[492] = dsaf_read_dev(ddev, DSAF_INODE_FIFO_WL_0_REG + port * 0x4); + p[493] = dsaf_read_dev(ddev, DSAF_ONODE_FIFO_WL_0_REG + port * 0x4); + p[494] = dsaf_read_dev(ddev, DSAF_XGE_GE_WORK_MODE_0_REG + port * 0x4); + p[495] = dsaf_read_dev(ddev, + DSAF_XGE_APP_RX_LINK_UP_0_REG + port * 0x4); + p[496] = dsaf_read_dev(ddev, DSAF_NETPORT_CTRL_SIG_0_REG + port * 0x4); + p[497] = dsaf_read_dev(ddev, DSAF_XGE_CTRL_SIG_CFG_0_REG + port * 0x4); + + /* mark end of dsaf regs */ + for (i = 498; i < 504; i++) + p[i] = 0xdddddddd; +} + +static char *hns_dsaf_get_node_stats_strings(char *data, int node) +{ + char *buff = data; + + snprintf(buff, ETH_GSTRING_LEN, "innod%d_pad_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_manage_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pkt_id", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_rx_pause_frame", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_release_buf_num", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_sbm_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_crc_false_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_bp_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_lookup_rslt_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_local_rslt_fail_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_vlan_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "innod%d_stp_drop_pkts", node); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "onnod%d_tx_pkts", node); + buff = buff + ETH_GSTRING_LEN; + + return buff; +} + +static u64 *hns_dsaf_get_node_stats(struct dsaf_device *ddev, u64 *data, + int node_num) +{ + u64 *p = data; + struct dsaf_hw_stats *hw_stats = &ddev->hw_stats[node_num]; + + p[0] = hw_stats->pad_drop; + p[1] = hw_stats->man_pkts; + p[2] = hw_stats->rx_pkts; + p[3] = hw_stats->rx_pkt_id; + p[4] = hw_stats->rx_pause_frame; + p[5] = hw_stats->release_buf_num; + p[6] = hw_stats->sbm_drop; + p[7] = hw_stats->crc_false; + p[8] = hw_stats->bp_drop; + p[9] = hw_stats->rslt_drop; + p[10] = hw_stats->local_addr_false; + p[11] = hw_stats->vlan_drop; + p[12] = hw_stats->stp_drop; + p[13] = hw_stats->tx_pkts; + + return &p[14]; +} + +/** + *hns_dsaf_get_stats - get dsaf statistic + *@ddev: dsaf device + *@data:statistic value + *@port: port num + */ +void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port) +{ + u64 *p = data; + int node_num = port; + + /* for ge/xge node info */ + p = hns_dsaf_get_node_stats(ddev, p, node_num); + + /* for ppe node info */ + node_num = port + DSAF_PPE_INODE_BASE; + (void)hns_dsaf_get_node_stats(ddev, p, node_num); +} + +/** + *hns_dsaf_get_sset_count - get dsaf string set count + *@stringset: type of values in data + *return dsaf string name count + */ +int hns_dsaf_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return DSAF_STATIC_NUM; + + return 0; +} + +/** + *hns_dsaf_get_strings - get dsaf string set + *@stringset:srting set index + *@data:strings name value + *@port:port index + */ +void hns_dsaf_get_strings(int stringset, u8 *data, int port) +{ + char *buff = (char *)data; + int node = port; + + if (stringset != ETH_SS_STATS) + return; + + /* for ge/xge node info */ + buff = hns_dsaf_get_node_stats_strings(buff, node); + + /* for ppe node info */ + node = port + DSAF_PPE_INODE_BASE; + (void)hns_dsaf_get_node_stats_strings(buff, node); +} + +/** + *hns_dsaf_get_sset_count - get dsaf regs count + *return dsaf regs count + */ +int hns_dsaf_get_regs_count(void) +{ + return DSAF_DUMP_REGS_NUM; +} + +/** + * dsaf_probe - probo dsaf dev + * @pdev: dasf platform device + * retuen 0 - success , negative --fail + */ +static int hns_dsaf_probe(struct platform_device *pdev) +{ + struct dsaf_device *dsaf_dev; + int ret; + + dsaf_dev = hns_dsaf_alloc_dev(&pdev->dev, sizeof(struct dsaf_drv_priv)); + if (IS_ERR(dsaf_dev)) { + ret = PTR_ERR(dsaf_dev); + dev_err(&pdev->dev, + "dsaf_probe dsaf_alloc_dev failed, ret = %#x!\n", ret); + return ret; + } + + ret = hns_dsaf_get_cfg(dsaf_dev); + if (ret) + goto free_dev; + + ret = hns_dsaf_init(dsaf_dev); + if (ret) + goto free_cfg; + + ret = hns_mac_init(dsaf_dev); + if (ret) + goto uninit_dsaf; + + ret = hns_ppe_init(dsaf_dev); + if (ret) + goto uninit_mac; + + ret = hns_dsaf_ae_init(dsaf_dev); + if (ret) + goto uninit_ppe; + + return 0; + +uninit_ppe: + hns_ppe_uninit(dsaf_dev); + +uninit_mac: + hns_mac_uninit(dsaf_dev); + +uninit_dsaf: + hns_dsaf_free(dsaf_dev); + +free_cfg: + hns_dsaf_free_cfg(dsaf_dev); + +free_dev: + hns_dsaf_free_dev(dsaf_dev); + + return ret; +} + +/** + * dsaf_remove - remove dsaf dev + * @pdev: dasf platform device + */ +static int hns_dsaf_remove(struct platform_device *pdev) +{ + struct dsaf_device *dsaf_dev = dev_get_drvdata(&pdev->dev); + + hns_dsaf_ae_uninit(dsaf_dev); + + hns_ppe_uninit(dsaf_dev); + + hns_mac_uninit(dsaf_dev); + + hns_dsaf_free(dsaf_dev); + + hns_dsaf_free_cfg(dsaf_dev); + + hns_dsaf_free_dev(dsaf_dev); + + return 0; +} + +static const struct of_device_id g_dsaf_match[] = { + {.compatible = "hisilicon,hns-dsaf-v1"}, + {.compatible = "hisilicon,hns-dsaf-v2"}, + {} +}; + +static struct platform_driver g_dsaf_driver = { + .probe = hns_dsaf_probe, + .remove = hns_dsaf_remove, + .driver = { + .name = DSAF_DRV_NAME, + .of_match_table = g_dsaf_match, + }, +}; + +module_platform_driver(g_dsaf_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("HNS DSAF driver"); +MODULE_VERSION(DSAF_MOD_VERSION); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h new file mode 100644 index 000000000000..b2b93484995c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNS_DSAF_MAIN_H +#define __HNS_DSAF_MAIN_H +#include "hnae.h" + +#include "hns_dsaf_reg.h" +#include "hns_dsaf_mac.h" + +struct hns_mac_cb; + +#define DSAF_DRV_NAME "hns_dsaf" +#define DSAF_MOD_VERSION "v1.0" + +#define ENABLE (0x1) +#define DISABLE (0x0) + +#define HNS_DSAF_DEBUG_NW_REG_OFFSET (0x100000) + +#define DSAF_BASE_INNER_PORT_NUM (127) /* mac tbl qid*/ + +#define DSAF_MAX_CHIP_NUM (2) /*max 2 chips */ + +#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE (22) + +#define HNS_DSAF_MAX_DESC_CNT (1024) +#define HNS_DSAF_MIN_DESC_CNT (16) + +#define DSAF_INVALID_ENTRY_IDX (0xffff) + +#define DSAF_CFG_READ_CNT (30) +#define DSAF_SRAM_INIT_FINISH_FLAG (0xff) + +#define MAC_NUM_OCTETS_PER_ADDR 6 + +#define DSAF_DUMP_REGS_NUM 504 +#define DSAF_STATIC_NUM 28 + +#define DSAF_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset)))) + +enum hal_dsaf_mode { + HRD_DSAF_NO_DSAF_MODE = 0x0, + HRD_DSAF_MODE = 0x1, +}; + +enum hal_dsaf_tc_mode { + HRD_DSAF_4TC_MODE = 0X0, + HRD_DSAF_8TC_MODE = 0X1, +}; + +struct dsaf_vm_def_vlan { + u32 vm_def_vlan_id; + u32 vm_def_vlan_cfi; + u32 vm_def_vlan_pri; +}; + +struct dsaf_tbl_tcam_data { + u32 tbl_tcam_data_high; + u32 tbl_tcam_data_low; +}; + +#define DSAF_PORT_MSK_NUM \ + ((DSAF_TOTAL_QUEUE_NUM + DSAF_SERVICE_NW_NUM - 1) / 32 + 1) +struct dsaf_tbl_tcam_mcast_cfg { + u8 tbl_mcast_old_en; + u8 tbl_mcast_item_vld; + u32 tbl_mcast_port_msk[DSAF_PORT_MSK_NUM]; +}; + +struct dsaf_tbl_tcam_ucast_cfg { + u32 tbl_ucast_old_en; + u32 tbl_ucast_item_vld; + u32 tbl_ucast_mac_discard; + u32 tbl_ucast_dvc; + u32 tbl_ucast_out_port; +}; + +struct dsaf_tbl_line_cfg { + u32 tbl_line_mac_discard; + u32 tbl_line_dvc; + u32 tbl_line_out_port; +}; + +enum dsaf_port_rate_mode { + DSAF_PORT_RATE_1000 = 0, + DSAF_PORT_RATE_2500, + DSAF_PORT_RATE_10000 +}; + +enum dsaf_stp_port_type { + DSAF_STP_PORT_TYPE_DISCARD = 0, + DSAF_STP_PORT_TYPE_BLOCK = 1, + DSAF_STP_PORT_TYPE_LISTEN = 2, + DSAF_STP_PORT_TYPE_LEARN = 3, + DSAF_STP_PORT_TYPE_FORWARD = 4 +}; + +enum dsaf_sw_port_type { + DSAF_SW_PORT_TYPE_NON_VLAN = 0, + DSAF_SW_PORT_TYPE_ACCESS = 1, + DSAF_SW_PORT_TYPE_TRUNK = 2, +}; + +#define DSAF_SUB_BASE_SIZE (0x10000) + +/* dsaf mode define */ +enum dsaf_mode { + DSAF_MODE_INVALID = 0, /**< Invalid dsaf mode */ + DSAF_MODE_ENABLE_FIX, /**< en DSAF-mode, fixed to queue*/ + DSAF_MODE_ENABLE_0VM, /**< en DSAF-mode, support 0 VM */ + DSAF_MODE_ENABLE_8VM, /**< en DSAF-mode, support 8 VM */ + DSAF_MODE_ENABLE_16VM, /**< en DSAF-mode, support 16 VM */ + DSAF_MODE_ENABLE_32VM, /**< en DSAF-mode, support 32 VM */ + DSAF_MODE_ENABLE_128VM, /**< en DSAF-mode, support 128 VM */ + DSAF_MODE_ENABLE, /**< before is enable DSAF mode*/ + DSAF_MODE_DISABLE_FIX, /**< non-dasf, fixed to queue*/ + DSAF_MODE_DISABLE_2PORT_8VM, /**< non-dasf, 2port 8VM */ + DSAF_MODE_DISABLE_2PORT_16VM, /**< non-dasf, 2port 16VM */ + DSAF_MODE_DISABLE_2PORT_64VM, /**< non-dasf, 2port 64VM */ + DSAF_MODE_DISABLE_6PORT_0VM, /**< non-dasf, 6port 0VM */ + DSAF_MODE_DISABLE_6PORT_2VM, /**< non-dasf, 6port 2VM */ + DSAF_MODE_DISABLE_6PORT_4VM, /**< non-dasf, 6port 4VM */ + DSAF_MODE_DISABLE_6PORT_16VM, /**< non-dasf, 6port 16VM */ + DSAF_MODE_MAX /**< the last one, use as the num */ +}; + +#define DSAF_DEST_PORT_NUM 256 /* DSAF max port num */ +#define DSAF_WORD_BIT_CNT 32 /* the num bit of word */ + +/*mac entry, mc or uc entry*/ +struct dsaf_drv_mac_single_dest_entry { + /* mac addr, match the entry*/ + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 in_vlan_id; /* value of VlanId */ + + /* the vld input port num, dsaf-mode fix 0, */ + /* non-dasf is the entry whitch port vld*/ + u8 in_port_num; + + u8 port_num; /*output port num*/ + u8 rsv[6]; +}; + +/*only mc entry*/ +struct dsaf_drv_mac_multi_dest_entry { + /* mac addr, match the entry*/ + u8 addr[MAC_NUM_OCTETS_PER_ADDR]; + u16 in_vlan_id; + /* this mac addr output port,*/ + /* bit0-bit5 means Port0-Port5(1bit is vld)**/ + u32 port_mask[DSAF_DEST_PORT_NUM / DSAF_WORD_BIT_CNT]; + + /* the vld input port num, dsaf-mode fix 0,*/ + /* non-dasf is the entry whitch port vld*/ + u8 in_port_num; + u8 rsv[7]; +}; + +struct dsaf_hw_stats { + u64 pad_drop; + u64 man_pkts; + u64 rx_pkts; + u64 rx_pkt_id; + u64 rx_pause_frame; + u64 release_buf_num; + u64 sbm_drop; + u64 crc_false; + u64 bp_drop; + u64 rslt_drop; + u64 local_addr_false; + u64 vlan_drop; + u64 stp_drop; + u64 tx_pkts; +}; + +struct hnae_vf_cb { + u8 port_index; + struct hns_mac_cb *mac_cb; + struct dsaf_device *dsaf_dev; + struct hnae_handle ae_handle; /* must be the last number */ +}; + +struct dsaf_int_xge_src { + u32 xid_xge_ecc_err_int_src; + u32 xid_xge_fsm_timout_int_src; + u32 sbm_xge_lnk_fsm_timout_int_src; + u32 sbm_xge_lnk_ecc_2bit_int_src; + u32 sbm_xge_mib_req_failed_int_src; + u32 sbm_xge_mib_req_fsm_timout_int_src; + u32 sbm_xge_mib_rels_fsm_timout_int_src; + u32 sbm_xge_sram_ecc_2bit_int_src; + u32 sbm_xge_mib_buf_sum_err_int_src; + u32 sbm_xge_mib_req_extra_int_src; + u32 sbm_xge_mib_rels_extra_int_src; + u32 voq_xge_start_to_over_0_int_src; + u32 voq_xge_start_to_over_1_int_src; + u32 voq_xge_ecc_err_int_src; +}; + +struct dsaf_int_ppe_src { + u32 xid_ppe_fsm_timout_int_src; + u32 sbm_ppe_lnk_fsm_timout_int_src; + u32 sbm_ppe_lnk_ecc_2bit_int_src; + u32 sbm_ppe_mib_req_failed_int_src; + u32 sbm_ppe_mib_req_fsm_timout_int_src; + u32 sbm_ppe_mib_rels_fsm_timout_int_src; + u32 sbm_ppe_sram_ecc_2bit_int_src; + u32 sbm_ppe_mib_buf_sum_err_int_src; + u32 sbm_ppe_mib_req_extra_int_src; + u32 sbm_ppe_mib_rels_extra_int_src; + u32 voq_ppe_start_to_over_0_int_src; + u32 voq_ppe_ecc_err_int_src; + u32 xod_ppe_fifo_rd_empty_int_src; + u32 xod_ppe_fifo_wr_full_int_src; +}; + +struct dsaf_int_rocee_src { + u32 xid_rocee_fsm_timout_int_src; + u32 sbm_rocee_lnk_fsm_timout_int_src; + u32 sbm_rocee_lnk_ecc_2bit_int_src; + u32 sbm_rocee_mib_req_failed_int_src; + u32 sbm_rocee_mib_req_fsm_timout_int_src; + u32 sbm_rocee_mib_rels_fsm_timout_int_src; + u32 sbm_rocee_sram_ecc_2bit_int_src; + u32 sbm_rocee_mib_buf_sum_err_int_src; + u32 sbm_rocee_mib_req_extra_int_src; + u32 sbm_rocee_mib_rels_extra_int_src; + u32 voq_rocee_start_to_over_0_int_src; + u32 voq_rocee_ecc_err_int_src; +}; + +struct dsaf_int_tbl_src { + u32 tbl_da0_mis_src; + u32 tbl_da1_mis_src; + u32 tbl_da2_mis_src; + u32 tbl_da3_mis_src; + u32 tbl_da4_mis_src; + u32 tbl_da5_mis_src; + u32 tbl_da6_mis_src; + u32 tbl_da7_mis_src; + u32 tbl_sa_mis_src; + u32 tbl_old_sech_end_src; + u32 lram_ecc_err1_src; + u32 lram_ecc_err2_src; + u32 tram_ecc_err1_src; + u32 tram_ecc_err2_src; + u32 tbl_ucast_bcast_xge0_src; + u32 tbl_ucast_bcast_xge1_src; + u32 tbl_ucast_bcast_xge2_src; + u32 tbl_ucast_bcast_xge3_src; + u32 tbl_ucast_bcast_xge4_src; + u32 tbl_ucast_bcast_xge5_src; + u32 tbl_ucast_bcast_ppe_src; + u32 tbl_ucast_bcast_rocee_src; +}; + +struct dsaf_int_stat { + struct dsaf_int_xge_src dsaf_int_xge_stat[DSAF_COMM_CHN]; + struct dsaf_int_ppe_src dsaf_int_ppe_stat[DSAF_COMM_CHN]; + struct dsaf_int_rocee_src dsaf_int_rocee_stat[DSAF_COMM_CHN]; + struct dsaf_int_tbl_src dsaf_int_tbl_stat[1]; + +}; + +/* Dsaf device struct define ,and mac -> dsaf */ +struct dsaf_device { + struct device *dev; + struct hnae_ae_dev ae_dev; + + void *priv; + + int virq[DSAF_IRQ_NUM]; + + u8 __iomem *sc_base; + u8 __iomem *sds_base; + u8 __iomem *ppe_base; + u8 __iomem *io_base; + u8 __iomem *cpld_base; + + u32 desc_num; /* desc num per queue*/ + u32 buf_size; /* ring buffer size */ + int buf_size_type; /* ring buffer size-type */ + enum dsaf_mode dsaf_mode; /* dsaf mode */ + enum hal_dsaf_mode dsaf_en; + enum hal_dsaf_tc_mode dsaf_tc_mode; + u32 dsaf_ver; + + struct ppe_common_cb *ppe_common[DSAF_COMM_DEV_NUM]; + struct rcb_common_cb *rcb_common[DSAF_COMM_DEV_NUM]; + struct hns_mac_cb *mac_cb; + + struct dsaf_hw_stats hw_stats[DSAF_NODE_NUM]; + struct dsaf_int_stat int_stat; +}; + +static inline void *hns_dsaf_dev_priv(const struct dsaf_device *dsaf_dev) +{ + return (void *)((u8 *)dsaf_dev + sizeof(*dsaf_dev)); +} + +struct dsaf_drv_tbl_tcam_key { + union { + struct { + u8 mac_3; + u8 mac_2; + u8 mac_1; + u8 mac_0; + } bits; + + u32 val; + } high; + union { + struct { + u32 port:4; /* port id, */ + /* dsaf-mode fixed 0, non-dsaf-mode port id*/ + u32 vlan:12; /* vlan id */ + u32 mac_5:8; + u32 mac_4:8; + } bits; + + u32 val; + } low; +}; + +struct dsaf_drv_soft_mac_tbl { + struct dsaf_drv_tbl_tcam_key tcam_key; + u16 index; /*the entry's index in tcam tab*/ +}; + +struct dsaf_drv_priv { + /* soft tab Mac key, for hardware tab*/ + struct dsaf_drv_soft_mac_tbl *soft_mac_tbl; +}; + +static inline void hns_dsaf_tbl_tcam_addr_cfg(struct dsaf_device *dsaf_dev, + u32 tab_tcam_addr) +{ + dsaf_set_dev_field(dsaf_dev, DSAF_TBL_TCAM_ADDR_0_REG, + DSAF_TBL_TCAM_ADDR_M, DSAF_TBL_TCAM_ADDR_S, + tab_tcam_addr); +} + +static inline void hns_dsaf_tbl_tcam_load_pul(struct dsaf_device *dsaf_dev) +{ + u32 o_tbl_pul; + + o_tbl_pul = dsaf_read_dev(dsaf_dev, DSAF_TBL_PUL_0_REG); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 1); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); + dsaf_set_bit(o_tbl_pul, DSAF_TBL_PUL_TCAM_LOAD_S, 0); + dsaf_write_dev(dsaf_dev, DSAF_TBL_PUL_0_REG, o_tbl_pul); +} + +static inline void hns_dsaf_tbl_line_addr_cfg(struct dsaf_device *dsaf_dev, + u32 tab_line_addr) +{ + dsaf_set_dev_field(dsaf_dev, DSAF_TBL_LINE_ADDR_0_REG, + DSAF_TBL_LINE_ADDR_M, DSAF_TBL_LINE_ADDR_S, + tab_line_addr); +} + +static inline int hns_dsaf_get_comm_idx_by_port(int port) +{ + if ((port < DSAF_COMM_CHN) || (port == DSAF_MAX_PORT_NUM_PER_CHIP)) + return 0; + else + return (port - DSAF_COMM_CHN + 1); +} + +static inline struct hnae_vf_cb *hns_ae_get_vf_cb( + struct hnae_handle *handle) +{ + return container_of(handle, struct hnae_vf_cb, ae_handle); +} + +int hns_dsaf_set_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_set_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); +int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, + u8 in_port_num, u8 *addr); +int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_single_dest_entry *mac_entry); +int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); +int hns_dsaf_get_mac_entry_by_index( + struct dsaf_device *dsaf_dev, + u16 entry_index, + struct dsaf_drv_mac_multi_dest_entry *mac_entry); + +void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val); + +void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); + +void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val); + +void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb); + +int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev); +void hns_dsaf_ae_uninit(struct dsaf_device *dsaf_dev); + +void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); +void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val); +void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev, + u32 port, u32 val); + +void hns_dsaf_update_stats(struct dsaf_device *dsaf_dev, u32 inode_num); + +int hns_dsaf_get_sset_count(int stringset); +void hns_dsaf_get_stats(struct dsaf_device *ddev, u64 *data, int port); +void hns_dsaf_get_strings(int stringset, u8 *data, int port); + +void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data); +int hns_dsaf_get_regs_count(void); +void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en); + +#endif /* __HNS_DSAF_MAIN_H__ */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c new file mode 100644 index 000000000000..d611388aecef --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "hns_dsaf_misc.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_reg.h" +#include "hns_dsaf_ppe.h" + +void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, + u16 speed, int data) +{ + int speed_reg = 0; + u8 value; + + if (!mac_cb) { + pr_err("sfp_led_opt mac_dev is null!\n"); + return; + } + if (!mac_cb->cpld_vaddr) { + dev_err(mac_cb->dev, "mac_id=%d, cpld_vaddr is null !\n", + mac_cb->mac_id); + return; + } + + if (speed == MAC_SPEED_10000) + speed_reg = 1; + + value = mac_cb->cpld_led_value; + + if (link_status) { + dsaf_set_bit(value, DSAF_LED_LINK_B, link_status); + dsaf_set_field(value, DSAF_LED_SPEED_M, + DSAF_LED_SPEED_S, speed_reg); + dsaf_set_bit(value, DSAF_LED_DATA_B, data); + + if (value != mac_cb->cpld_led_value) { + dsaf_write_b(mac_cb->cpld_vaddr, value); + mac_cb->cpld_led_value = value; + } + } else { + dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE); + mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE; + } +} + +void cpld_led_reset(struct hns_mac_cb *mac_cb) +{ + if (!mac_cb || !mac_cb->cpld_vaddr) + return; + + dsaf_write_b(mac_cb->cpld_vaddr, CPLD_LED_DEFAULT_VALUE); + mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE; +} + +int cpld_set_led_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status) +{ + switch (status) { + case HNAE_LED_ACTIVE: + mac_cb->cpld_led_value = dsaf_read_b(mac_cb->cpld_vaddr); + return 2; + case HNAE_LED_ON: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_ON_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + case HNAE_LED_OFF: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_DEFAULT_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + case HNAE_LED_INACTIVE: + dsaf_set_bit(mac_cb->cpld_led_value, DSAF_LED_ANCHOR_B, + CPLD_LED_DEFAULT_VALUE); + dsaf_write_b(mac_cb->cpld_vaddr, mac_cb->cpld_led_value); + break; + default: + break; + } + + return 0; +} + +#define RESET_REQ_OR_DREQ 1 + +void hns_dsaf_rst(struct dsaf_device *dsaf_dev, u32 val) +{ + u32 xbar_reg_addr; + u32 nt_reg_addr; + + if (!val) { + xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_REQ_REG; + nt_reg_addr = DSAF_SUB_SC_NT_RESET_REQ_REG; + } else { + xbar_reg_addr = DSAF_SUB_SC_XBAR_RESET_DREQ_REG; + nt_reg_addr = DSAF_SUB_SC_NT_RESET_DREQ_REG; + } + + dsaf_write_reg(dsaf_dev->sc_base, xbar_reg_addr, + RESET_REQ_OR_DREQ); + dsaf_write_reg(dsaf_dev->sc_base, nt_reg_addr, + RESET_REQ_OR_DREQ); +} + +void hns_dsaf_xge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + if (port >= DSAF_XGE_NUM) + return; + + reg_val |= RESET_REQ_OR_DREQ; + reg_val |= 0x2082082 << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_dsaf_xge_core_srst_by_port(struct dsaf_device *dsaf_dev, + u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + if (port >= DSAF_XGE_NUM) + return; + + reg_val |= XGMAC_TRX_CORE_SRST_M << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_XGE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_XGE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val_1; + u32 reg_val_2; + + if (port >= DSAF_GE_NUM) + return; + + if (port < DSAF_SERVICE_NW_NUM) { + reg_val_1 = 0x1 << port; + reg_val_2 = 0x1041041 << port; + + if (val == 0) { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ0_REG, + reg_val_2); + } else { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ0_REG, + reg_val_2); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ1_REG, + reg_val_1); + } + } else { + reg_val_1 = 0x15540 << (port - 6); + reg_val_2 = 0x100 << (port - 6); + + if (val == 0) { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_REQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_PPE_RESET_REQ_REG, + reg_val_2); + } else { + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_GE_RESET_DREQ1_REG, + reg_val_1); + + dsaf_write_reg(dsaf_dev->sc_base, + DSAF_SUB_SC_PPE_RESET_DREQ_REG, + reg_val_2); + } + } +} + +void hns_ppe_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) +{ + u32 reg_val = 0; + u32 reg_addr; + + reg_val |= RESET_REQ_OR_DREQ << port; + + if (val == 0) + reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG; + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +void hns_ppe_com_srst(struct ppe_common_cb *ppe_common, u32 val) +{ + int comm_index = ppe_common->comm_index; + struct dsaf_device *dsaf_dev = ppe_common->dsaf_dev; + u32 reg_val; + u32 reg_addr; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + reg_val = RESET_REQ_OR_DREQ; + if (val == 0) + reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG; + + } else { + reg_val = 0x100 << (comm_index - 1); + + if (val == 0) + reg_addr = DSAF_SUB_SC_PPE_RESET_REQ_REG; + else + reg_addr = DSAF_SUB_SC_PPE_RESET_DREQ_REG; + } + + dsaf_write_reg(dsaf_dev->sc_base, reg_addr, reg_val); +} + +/** + * hns_mac_get_sds_mode - get phy ifterface form serdes mode + * @mac_cb: mac control block + * retuen phy interface + */ +phy_interface_t hns_mac_get_phy_if(struct hns_mac_cb *mac_cb) +{ + u32 hilink3_mode; + u32 hilink4_mode; + void __iomem *sys_ctl_vaddr = mac_cb->sys_ctl_vaddr; + int dev_id = mac_cb->mac_id; + phy_interface_t phy_if = PHY_INTERFACE_MODE_NA; + + hilink3_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK3_REG); + hilink4_mode = dsaf_read_reg(sys_ctl_vaddr, HNS_MAC_HILINK4_REG); + if (dev_id >= 0 && dev_id <= 3) { + if (hilink4_mode == 0) + phy_if = PHY_INTERFACE_MODE_SGMII; + else + phy_if = PHY_INTERFACE_MODE_XGMII; + } else if (dev_id >= 4 && dev_id <= 5) { + if (hilink3_mode == 0) + phy_if = PHY_INTERFACE_MODE_SGMII; + else + phy_if = PHY_INTERFACE_MODE_XGMII; + } else { + phy_if = PHY_INTERFACE_MODE_SGMII; + } + + dev_dbg(mac_cb->dev, + "hilink3_mode=%d, hilink4_mode=%d dev_id=%d, phy_if=%d\n", + hilink3_mode, hilink4_mode, dev_id, phy_if); + return phy_if; +} + +/** + * hns_mac_config_sds_loopback - set loop back for serdes + * @mac_cb: mac control block + * retuen 0 == success + */ +int hns_mac_config_sds_loopback(struct hns_mac_cb *mac_cb, u8 en) +{ + /* port 0-3 hilink4 base is serdes_vaddr + 0x00280000 + * port 4-7 hilink3 base is serdes_vaddr + 0x00200000 + */ + u8 *base_addr = (u8 *)mac_cb->serdes_vaddr + + (mac_cb->mac_id <= 3 ? 0x00280000 : 0x00200000); + const u8 lane_id[] = { + 0, /* mac 0 -> lane 0 */ + 1, /* mac 1 -> lane 1 */ + 2, /* mac 2 -> lane 2 */ + 3, /* mac 3 -> lane 3 */ + 2, /* mac 4 -> lane 2 */ + 3, /* mac 5 -> lane 3 */ + 0, /* mac 6 -> lane 0 */ + 1 /* mac 7 -> lane 1 */ + }; +#define RX_CSR(lane, reg) ((0x4080 + (reg) * 0x0002 + (lane) * 0x0200) * 2) + u64 reg_offset = RX_CSR(lane_id[mac_cb->mac_id], 0); + + int sfp_prsnt; + int ret = hns_mac_get_sfp_prsnt(mac_cb, &sfp_prsnt); + + if (!mac_cb->phy_node) { + if (ret) + pr_info("please confirm sfp is present or not\n"); + else + if (!sfp_prsnt) + pr_info("no sfp in this eth\n"); + } + + dsaf_set_reg_field(base_addr, reg_offset, 1ull << 10, 10, !!en); + + return 0; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h new file mode 100644 index 000000000000..419f07aa9734 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_MISC_H +#define _HNS_DSAF_MISC_H + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> + +#include "hns_dsaf_mac.h" + +#define CPLD_ADDR_PORT_OFFSET 0x4 + +#define HS_LED_ON 0xE +#define HS_LED_OFF 0xF + +#define CPLD_LED_ON_VALUE 1 +#define CPLD_LED_DEFAULT_VALUE 0 + +#define MAC_SFP_PORT_OFFSET 0x2 + +#define DSAF_LED_SPEED_S 0 +#define DSAF_LED_SPEED_M (0x3 << DSAF_LED_SPEED_S) + +#define DSAF_LED_LINK_B 2 +#define DSAF_LED_DATA_B 4 +#define DSAF_LED_ANCHOR_B 5 + +void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, + u16 speed, int data); +void cpld_led_reset(struct hns_mac_cb *mac_cb); +int cpld_set_led_id(struct hns_mac_cb *mac_cb, + enum hnae_led_state status); +int hns_mac_get_sfp_prsnt(struct hns_mac_cb *mac_cb, int *sfp_prsnt); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c new file mode 100644 index 000000000000..67f33f185a44 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#include "hns_dsaf_ppe.h" + +static void __iomem *hns_ppe_common_get_ioaddr( + struct ppe_common_cb *ppe_common) +{ + void __iomem *base_addr; + + int idx = ppe_common->comm_index; + + if (idx == HNS_DSAF_COMM_SERVICE_NW_IDX) + base_addr = ppe_common->dsaf_dev->ppe_base + + PPE_COMMON_REG_OFFSET; + else + base_addr = ppe_common->dsaf_dev->sds_base + + (idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + PPE_COMMON_REG_OFFSET; + + return base_addr; +} + +/** + * hns_ppe_common_get_cfg - get ppe common config + * @dsaf_dev: dasf device + * comm_index: common index + * retuen 0 - success , negative --fail + */ +int hns_ppe_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index) +{ + struct ppe_common_cb *ppe_common; + int ppe_num; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + ppe_num = HNS_PPE_SERVICE_NW_ENGINE_NUM; + else + ppe_num = HNS_PPE_DEBUG_NW_ENGINE_NUM; + + ppe_common = devm_kzalloc(dsaf_dev->dev, sizeof(*ppe_common) + + ppe_num * sizeof(struct hns_ppe_cb), GFP_KERNEL); + if (!ppe_common) + return -ENOMEM; + + ppe_common->ppe_num = ppe_num; + ppe_common->dsaf_dev = dsaf_dev; + ppe_common->comm_index = comm_index; + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + ppe_common->ppe_mode = PPE_COMMON_MODE_SERVICE; + else + ppe_common->ppe_mode = PPE_COMMON_MODE_DEBUG; + ppe_common->dev = dsaf_dev->dev; + + ppe_common->io_base = hns_ppe_common_get_ioaddr(ppe_common); + + dsaf_dev->ppe_common[comm_index] = ppe_common; + + return 0; +} + +void hns_ppe_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index) +{ + dsaf_dev->ppe_common[comm_index] = NULL; +} + +static void __iomem *hns_ppe_get_iobase(struct ppe_common_cb *ppe_common, + int ppe_idx) +{ + void __iomem *base_addr; + int common_idx = ppe_common->comm_index; + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) { + base_addr = ppe_common->dsaf_dev->ppe_base + + ppe_idx * PPE_REG_OFFSET; + + } else { + base_addr = ppe_common->dsaf_dev->sds_base + + (common_idx - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET; + } + + return base_addr; +} + +static int hns_ppe_get_port(struct ppe_common_cb *ppe_common, int idx) +{ + int port; + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) + port = idx; + else + port = HNS_PPE_SERVICE_NW_ENGINE_NUM + + ppe_common->comm_index - 1; + + return port; +} + +static void hns_ppe_get_cfg(struct ppe_common_cb *ppe_common) +{ + u32 i; + struct hns_ppe_cb *ppe_cb; + u32 ppe_num = ppe_common->ppe_num; + + for (i = 0; i < ppe_num; i++) { + ppe_cb = &ppe_common->ppe_cb[i]; + ppe_cb->dev = ppe_common->dev; + ppe_cb->next = NULL; + ppe_cb->ppe_common_cb = ppe_common; + ppe_cb->index = i; + ppe_cb->port = hns_ppe_get_port(ppe_common, i); + ppe_cb->io_base = hns_ppe_get_iobase(ppe_common, i); + ppe_cb->virq = 0; + } +} + +static void hns_ppe_cnt_clr_ce(struct hns_ppe_cb *ppe_cb) +{ + dsaf_set_dev_bit(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG, + PPE_CNT_CLR_CE_B, 1); +} + +/** + * hns_ppe_checksum_hw - set ppe checksum caculate + * @ppe_device: ppe device + * @value: value + */ +static void hns_ppe_checksum_hw(struct hns_ppe_cb *ppe_cb, u32 value) +{ + dsaf_set_dev_field(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG, + 0xfffffff, 0, value); +} + +static void hns_ppe_set_qid_mode(struct ppe_common_cb *ppe_common, + enum ppe_qid_mode qid_mdoe) +{ + dsaf_set_dev_field(ppe_common, PPE_COM_CFG_QID_MODE_REG, + PPE_CFG_QID_MODE_CF_QID_MODE_M, + PPE_CFG_QID_MODE_CF_QID_MODE_S, qid_mdoe); +} + +/** + * hns_ppe_set_qid - set ppe qid + * @ppe_common: ppe common device + * @qid: queue id + */ +static void hns_ppe_set_qid(struct ppe_common_cb *ppe_common, u32 qid) +{ + u32 qid_mod = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG); + + if (!dsaf_get_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M, + PPE_CFG_QID_MODE_DEF_QID_S)) { + dsaf_set_field(qid_mod, PPE_CFG_QID_MODE_DEF_QID_M, + PPE_CFG_QID_MODE_DEF_QID_S, qid); + dsaf_write_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG, qid_mod); + } +} + +/** + * hns_ppe_set_port_mode - set port mode + * @ppe_device: ppe device + * @mode: port mode + */ +static void hns_ppe_set_port_mode(struct hns_ppe_cb *ppe_cb, + enum ppe_port_mode mode) +{ + dsaf_write_dev(ppe_cb, PPE_CFG_XGE_MODE_REG, mode); +} + +/** + * hns_ppe_common_init_hw - init ppe common device + * @ppe_common: ppe common device + * + * Return 0 on success, negative on failure + */ +static int hns_ppe_common_init_hw(struct ppe_common_cb *ppe_common) +{ + enum ppe_qid_mode qid_mode; + enum dsaf_mode dsaf_mode = ppe_common->dsaf_dev->dsaf_mode; + + hns_ppe_com_srst(ppe_common, 0); + mdelay(100); + hns_ppe_com_srst(ppe_common, 1); + mdelay(100); + + if (ppe_common->ppe_mode == PPE_COMMON_MODE_SERVICE) { + switch (dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + case DSAF_MODE_DISABLE_FIX: + qid_mode = PPE_QID_MODE0; + hns_ppe_set_qid(ppe_common, 0); + break; + case DSAF_MODE_ENABLE_0VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + qid_mode = PPE_QID_MODE3; + break; + case DSAF_MODE_ENABLE_8VM: + case DSAF_MODE_DISABLE_2PORT_16VM: + qid_mode = PPE_QID_MODE4; + break; + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_DISABLE_6PORT_0VM: + qid_mode = PPE_QID_MODE5; + break; + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + qid_mode = PPE_QID_MODE2; + break; + case DSAF_MODE_ENABLE_128VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + qid_mode = PPE_QID_MODE1; + break; + case DSAF_MODE_DISABLE_2PORT_8VM: + qid_mode = PPE_QID_MODE7; + break; + case DSAF_MODE_DISABLE_6PORT_2VM: + qid_mode = PPE_QID_MODE6; + break; + default: + dev_err(ppe_common->dev, + "get ppe queue mode failed! dsaf_mode=%d\n", + dsaf_mode); + return -EINVAL; + } + hns_ppe_set_qid_mode(ppe_common, qid_mode); + } + + dsaf_set_dev_bit(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG, + PPE_COMMON_CNT_CLR_CE_B, 1); + + return 0; +} + +/*clr ppe exception irq*/ +static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/ + u32 vld_msk = 0; + + /*only care bit 0,1,7*/ + dsaf_set_bit(vld_msk, 0, 1); + dsaf_set_bit(vld_msk, 1, 1); + dsaf_set_bit(vld_msk, 7, 1); + + /*clr sts**/ + dsaf_write_dev(ppe_cb, PPE_RINT_REG, clr_vlue); + + /*for some reserved bits, so set 0**/ + dsaf_write_dev(ppe_cb, PPE_INTEN_REG, msk_vlue & vld_msk); +} + +/** + * ppe_init_hw - init ppe + * @ppe_device: ppe device + */ +static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb) +{ + struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb; + u32 port = ppe_cb->port; + struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev; + + hns_ppe_srst_by_port(dsaf_dev, port, 0); + mdelay(10); + hns_ppe_srst_by_port(dsaf_dev, port, 1); + + /* clr and msk except irq*/ + hns_ppe_exc_irq_en(ppe_cb, 0); + + if (ppe_common_cb->ppe_mode == PPE_COMMON_MODE_DEBUG) + hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE); + else + hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE); + hns_ppe_checksum_hw(ppe_cb, 0xffffffff); + hns_ppe_cnt_clr_ce(ppe_cb); +} + +/** + * ppe_uninit_hw - uninit ppe + * @ppe_device: ppe device + */ +static void hns_ppe_uninit_hw(struct hns_ppe_cb *ppe_cb) +{ + u32 port; + + if (ppe_cb->ppe_common_cb) { + port = ppe_cb->index; + hns_ppe_srst_by_port(ppe_cb->ppe_common_cb->dsaf_dev, port, 0); + } +} + +void hns_ppe_uninit_ex(struct ppe_common_cb *ppe_common) +{ + u32 i; + + for (i = 0; i < ppe_common->ppe_num; i++) { + hns_ppe_uninit_hw(&ppe_common->ppe_cb[i]); + memset(&ppe_common->ppe_cb[i], 0, sizeof(struct hns_ppe_cb)); + } +} + +void hns_ppe_uninit(struct dsaf_device *dsaf_dev) +{ + u32 i; + + for (i = 0; i < HNS_PPE_COM_NUM; i++) { + if (dsaf_dev->ppe_common[i]) + hns_ppe_uninit_ex(dsaf_dev->ppe_common[i]); + hns_rcb_common_free_cfg(dsaf_dev, i); + hns_ppe_common_free_cfg(dsaf_dev, i); + } +} + +/** + * hns_ppe_reset - reinit ppe/rcb hw + * @dsaf_dev: dasf device + * retuen void + */ +void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index) +{ + u32 i; + int ret; + struct ppe_common_cb *ppe_common; + + ppe_common = dsaf_dev->ppe_common[ppe_common_index]; + ret = hns_ppe_common_init_hw(ppe_common); + if (ret) + return; + + ret = hns_rcb_common_init_hw(dsaf_dev->rcb_common[ppe_common_index]); + if (ret) + return; + + for (i = 0; i < ppe_common->ppe_num; i++) + hns_ppe_init_hw(&ppe_common->ppe_cb[i]); + + hns_rcb_common_init_commit_hw(dsaf_dev->rcb_common[ppe_common_index]); +} + +void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb) +{ + struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats; + + hw_stats->rx_pkts_from_sw + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG); + hw_stats->rx_pkts + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG); + hw_stats->rx_drop_no_bd + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG); + hw_stats->rx_alloc_buf_fail + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG); + hw_stats->rx_alloc_buf_wait + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG); + hw_stats->rx_drop_no_buf + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG); + hw_stats->rx_err_fifo_full + += dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG); + + hw_stats->tx_bd_form_rcb + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG); + hw_stats->tx_pkts_from_rcb + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG); + hw_stats->tx_pkts + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG); + hw_stats->tx_err_fifo_empty + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG); + hw_stats->tx_err_checksum + += dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG); +} + +int hns_ppe_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ETH_PPE_STATIC_NUM; + return 0; +} + +int hns_ppe_get_regs_count(void) +{ + return ETH_PPE_DUMP_NUM; +} + +/** + * ppe_get_strings - get ppe srting + * @ppe_device: ppe device + * @stringset: string set type + * @data: output string + */ +void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data) +{ + char *buff = (char *)data; + int index = ppe_cb->index; + + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_sw_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_ok", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_drop_pkt_no_bd", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_fail", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_alloc_buf_wait", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_drop_no_buf", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_rx_pkt_err_fifo_full", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_bd", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_ok", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_fifo_empty", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "ppe%d_tx_pkt_err_csum_fail", index); +} + +void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data) +{ + u64 *regs_buff = data; + struct hns_ppe_hw_stats *hw_stats = &ppe_cb->hw_stats; + + regs_buff[0] = hw_stats->rx_pkts_from_sw; + regs_buff[1] = hw_stats->rx_pkts; + regs_buff[2] = hw_stats->rx_drop_no_bd; + regs_buff[3] = hw_stats->rx_alloc_buf_fail; + regs_buff[4] = hw_stats->rx_alloc_buf_wait; + regs_buff[5] = hw_stats->rx_drop_no_buf; + regs_buff[6] = hw_stats->rx_err_fifo_full; + + regs_buff[7] = hw_stats->tx_bd_form_rcb; + regs_buff[8] = hw_stats->tx_pkts_from_rcb; + regs_buff[9] = hw_stats->tx_pkts; + regs_buff[10] = hw_stats->tx_err_fifo_empty; + regs_buff[11] = hw_stats->tx_err_checksum; +} + +/** + * hns_ppe_init - init ppe device + * @dsaf_dev: dasf device + * retuen 0 - success , negative --fail + */ +int hns_ppe_init(struct dsaf_device *dsaf_dev) +{ + int i, k; + int ret; + + for (i = 0; i < HNS_PPE_COM_NUM; i++) { + ret = hns_ppe_common_get_cfg(dsaf_dev, i); + if (ret) + goto get_ppe_cfg_fail; + + ret = hns_rcb_common_get_cfg(dsaf_dev, i); + if (ret) + goto get_rcb_cfg_fail; + + hns_ppe_get_cfg(dsaf_dev->ppe_common[i]); + + hns_rcb_get_cfg(dsaf_dev->rcb_common[i]); + } + + for (i = 0; i < HNS_PPE_COM_NUM; i++) + hns_ppe_reset_common(dsaf_dev, i); + + return 0; + +get_rcb_cfg_fail: + hns_ppe_common_free_cfg(dsaf_dev, i); +get_ppe_cfg_fail: + for (k = i - 1; k >= 0; k--) { + hns_rcb_common_free_cfg(dsaf_dev, k); + hns_ppe_common_free_cfg(dsaf_dev, k); + } + return ret; +} + +void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data) +{ + struct ppe_common_cb *ppe_common = ppe_cb->ppe_common_cb; + u32 *regs = data; + u32 i; + u32 offset; + + /* ppe common registers */ + regs[0] = dsaf_read_dev(ppe_common, PPE_COM_CFG_QID_MODE_REG); + regs[1] = dsaf_read_dev(ppe_common, PPE_COM_INTEN_REG); + regs[2] = dsaf_read_dev(ppe_common, PPE_COM_RINT_REG); + regs[3] = dsaf_read_dev(ppe_common, PPE_COM_INTSTS_REG); + regs[4] = dsaf_read_dev(ppe_common, PPE_COM_COMMON_CNT_CLR_CE_REG); + + for (i = 0; i < DSAF_TOTAL_QUEUE_NUM; i++) { + offset = PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 0x4 * i; + regs[5 + i] = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM] + = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 2] + = dsaf_read_dev(ppe_common, offset); + offset = PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 0x4 * i; + regs[5 + i + DSAF_TOTAL_QUEUE_NUM * 3] + = dsaf_read_dev(ppe_common, offset); + } + + /* mark end of ppe regs */ + for (i = 521; i < 524; i++) + regs[i] = 0xeeeeeeee; + + /* ppe channel registers */ + regs[525] = dsaf_read_dev(ppe_cb, PPE_CFG_TX_FIFO_THRSLD_REG); + regs[526] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_THRSLD_REG); + regs[527] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG); + regs[528] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG); + regs[529] = dsaf_read_dev(ppe_cb, PPE_CFG_PAUSE_IDLE_CNT_REG); + regs[530] = dsaf_read_dev(ppe_cb, PPE_CFG_BUS_CTRL_REG); + regs[531] = dsaf_read_dev(ppe_cb, PPE_CFG_TNL_TO_BE_RST_REG); + regs[532] = dsaf_read_dev(ppe_cb, PPE_CURR_TNL_CAN_RST_REG); + + regs[533] = dsaf_read_dev(ppe_cb, PPE_CFG_XGE_MODE_REG); + regs[534] = dsaf_read_dev(ppe_cb, PPE_CFG_MAX_FRAME_LEN_REG); + regs[535] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_MODE_REG); + regs[536] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_VLAN_TAG_REG); + regs[537] = dsaf_read_dev(ppe_cb, PPE_CFG_TAG_GEN_REG); + regs[538] = dsaf_read_dev(ppe_cb, PPE_CFG_PARSE_TAG_REG); + regs[539] = dsaf_read_dev(ppe_cb, PPE_CFG_PRO_CHECK_EN_REG); + + regs[540] = dsaf_read_dev(ppe_cb, PPE_INTEN_REG); + regs[541] = dsaf_read_dev(ppe_cb, PPE_RINT_REG); + regs[542] = dsaf_read_dev(ppe_cb, PPE_INTSTS_REG); + regs[543] = dsaf_read_dev(ppe_cb, PPE_CFG_RX_PKT_INT_REG); + + regs[544] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME0_REG); + regs[545] = dsaf_read_dev(ppe_cb, PPE_CFG_HEAT_DECT_TIME1_REG); + + /* ppe static */ + regs[546] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_SW_PKT_CNT_REG); + regs[547] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG); + regs[548] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_NO_BUF_CNT_REG); + regs[549] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_BD_CNT_REG); + regs[550] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CNT_REG); + regs[551] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_OK_CNT_REG); + regs[552] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_EPT_CNT_REG); + regs[553] = dsaf_read_dev(ppe_cb, PPE_HIS_TX_PKT_CS_FAIL_CNT_REG); + regs[554] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_FAIL_CNT_REG); + regs[555] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_APP_BUF_WAIT_CNT_REG); + regs[556] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_FUL_CNT_REG); + regs[557] = dsaf_read_dev(ppe_cb, PPE_HIS_RX_PKT_DROP_PRT_CNT_REG); + + regs[558] = dsaf_read_dev(ppe_cb, PPE_TNL_0_5_CNT_CLR_CE_REG); + regs[559] = dsaf_read_dev(ppe_cb, PPE_CFG_AXI_DBG_REG); + regs[560] = dsaf_read_dev(ppe_cb, PPE_HIS_PRO_ERR_REG); + regs[561] = dsaf_read_dev(ppe_cb, PPE_HIS_TNL_FIFO_ERR_REG); + regs[562] = dsaf_read_dev(ppe_cb, PPE_CURR_CFF_DATA_NUM_REG); + regs[563] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_ST_REG); + regs[564] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_ST_REG); + regs[565] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO0_REG); + regs[566] = dsaf_read_dev(ppe_cb, PPE_CURR_RX_FIFO1_REG); + regs[567] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO0_REG); + regs[568] = dsaf_read_dev(ppe_cb, PPE_CURR_TX_FIFO1_REG); + regs[569] = dsaf_read_dev(ppe_cb, PPE_ECO0_REG); + regs[570] = dsaf_read_dev(ppe_cb, PPE_ECO1_REG); + regs[571] = dsaf_read_dev(ppe_cb, PPE_ECO2_REG); + + /* mark end of ppe regs */ + for (i = 572; i < 576; i++) + regs[i] = 0xeeeeeeee; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h new file mode 100644 index 000000000000..4894f9a0d39f --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_PPE_H +#define _HNS_DSAF_PPE_H + +#include <linux/platform_device.h> + +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_rcb.h" + +#define HNS_PPE_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN +#define HNS_PPE_DEBUG_NW_ENGINE_NUM 1 +#define HNS_PPE_COM_NUM DSAF_COMM_DEV_NUM + +#define PPE_COMMON_REG_OFFSET 0x70000 +#define PPE_REG_OFFSET 0x10000 + +#define ETH_PPE_DUMP_NUM 576 +#define ETH_PPE_STATIC_NUM 12 +enum ppe_qid_mode { + PPE_QID_MODE0 = 0, /* fixed queue id mode */ + PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */ + PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */ + PPE_QID_MODE3, /* switch:4TC/8TAG non switch:2Port/64VM */ + PPE_QID_MODE4, /* switch:8VM/16TAG non switch:2Port/16VM/4TC */ + PPE_QID_MODE5, /* non switch:6Port/16TAG */ + PPE_QID_MODE6, /* non switch:6Port/2VM/8TC */ + PPE_QID_MODE7, /* non switch:2Port/8VM/8TC */ +}; + +enum ppe_port_mode { + PPE_MODE_GE = 0, + PPE_MODE_XGE, +}; + +enum ppe_common_mode { + PPE_COMMON_MODE_DEBUG = 0, + PPE_COMMON_MODE_SERVICE, + PPE_COMMON_MODE_MAX +}; + +struct hns_ppe_hw_stats { + u64 rx_pkts_from_sw; + u64 rx_pkts; + u64 rx_drop_no_bd; + u64 rx_alloc_buf_fail; + u64 rx_alloc_buf_wait; + u64 rx_drop_no_buf; + u64 rx_err_fifo_full; + u64 tx_bd_form_rcb; + u64 tx_pkts_from_rcb; + u64 tx_pkts; + u64 tx_err_fifo_empty; + u64 tx_err_checksum; +}; + +struct hns_ppe_cb { + struct device *dev; + struct hns_ppe_cb *next; /* pointer to next ppe device */ + struct ppe_common_cb *ppe_common_cb; /* belong to */ + struct hns_ppe_hw_stats hw_stats; + + u8 index; /* index in a ppe common device */ + u8 port; /* port id in dsaf */ + void __iomem *io_base; + int virq; +}; + +struct ppe_common_cb { + struct device *dev; + struct dsaf_device *dsaf_dev; + void __iomem *io_base; + + enum ppe_common_mode ppe_mode; + + u8 comm_index; /*ppe_common index*/ + + u32 ppe_num; + struct hns_ppe_cb ppe_cb[0]; + +}; + +int hns_ppe_init(struct dsaf_device *dsaf_dev); + +void hns_ppe_uninit(struct dsaf_device *dsaf_dev); + +void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index); + +void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb); + +int hns_ppe_get_sset_count(int stringset); +int hns_ppe_get_regs_count(void); +void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data); + +void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data); +void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data); +#endif /* _HNS_DSAF_PPE_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c new file mode 100644 index 000000000000..4db32c62f062 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/cdev.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <asm/cacheflush.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/spinlock.h> + +#include "hns_dsaf_main.h" +#include "hns_dsaf_ppe.h" +#include "hns_dsaf_rcb.h" + +#define RCB_COMMON_REG_OFFSET 0x80000 +#define TX_RING 0 +#define RX_RING 1 + +#define RCB_RESET_WAIT_TIMES 30 +#define RCB_RESET_TRY_TIMES 10 + +/** + *hns_rcb_wait_fbd_clean - clean fbd + *@qs: ring struct pointer array + *@qnum: num of array + *@flag: tx or rx flag + */ +void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag) +{ + int i, wait_cnt; + u32 fbd_num; + + for (wait_cnt = i = 0; i < q_num; wait_cnt++) { + usleep_range(200, 300); + fbd_num = 0; + if (flag & RCB_INT_FLAG_TX) + fbd_num += dsaf_read_dev(qs[i], + RCB_RING_TX_RING_FBDNUM_REG); + if (flag & RCB_INT_FLAG_RX) + fbd_num += dsaf_read_dev(qs[i], + RCB_RING_RX_RING_FBDNUM_REG); + if (!fbd_num) + i++; + if (wait_cnt >= 10000) + break; + } + + if (i < q_num) + dev_err(qs[i]->handle->owner_dev, + "queue(%d) wait fbd(%d) clean fail!!\n", i, fbd_num); +} + +/** + *hns_rcb_reset_ring_hw - ring reset + *@q: ring struct pointer + */ +void hns_rcb_reset_ring_hw(struct hnae_queue *q) +{ + u32 wait_cnt; + u32 try_cnt = 0; + u32 could_ret; + + u32 tx_fbd_num; + + while (try_cnt++ < RCB_RESET_TRY_TIMES) { + usleep_range(100, 200); + tx_fbd_num = dsaf_read_dev(q, RCB_RING_TX_RING_FBDNUM_REG); + if (tx_fbd_num) + continue; + + dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, 0); + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1); + + msleep(20); + could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST); + + wait_cnt = 0; + while (!could_ret && (wait_cnt < RCB_RESET_WAIT_TIMES)) { + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0); + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 1); + + msleep(20); + could_ret = dsaf_read_dev(q, RCB_RING_COULD_BE_RST); + + wait_cnt++; + } + + dsaf_write_dev(q, RCB_RING_T0_BE_RST, 0); + + if (could_ret) + break; + } + + if (try_cnt >= RCB_RESET_TRY_TIMES) + dev_err(q->dev->dev, "port%d reset ring fail\n", + hns_ae_get_vf_cb(q->handle)->port_index); +} + +/** + *hns_rcb_int_ctrl_hw - rcb irq enable control + *@q: hnae queue struct pointer + *@flag:ring flag tx or rx + *@mask:mask + */ +void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask) +{ + u32 int_mask_en = !!mask; + + if (flag & RCB_INT_FLAG_TX) { + dsaf_write_dev(q, RCB_RING_INTMSK_TXWL_REG, int_mask_en); + dsaf_write_dev(q, RCB_RING_INTMSK_TX_OVERTIME_REG, + int_mask_en); + } + + if (flag & RCB_INT_FLAG_RX) { + dsaf_write_dev(q, RCB_RING_INTMSK_RXWL_REG, int_mask_en); + dsaf_write_dev(q, RCB_RING_INTMSK_RX_OVERTIME_REG, + int_mask_en); + } +} + +void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag) +{ + u32 clr = 1; + + if (flag & RCB_INT_FLAG_TX) { + dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, clr); + } + + if (flag & RCB_INT_FLAG_RX) { + dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, clr); + } +} + +/** + *hns_rcb_ring_enable_hw - enable ring + *@ring: rcb ring + */ +void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val) +{ + dsaf_write_dev(q, RCB_RING_PREFETCH_EN_REG, !!val); +} + +void hns_rcb_start(struct hnae_queue *q, u32 val) +{ + hns_rcb_ring_enable_hw(q, val); +} + +/** + *hns_rcb_common_init_commit_hw - make rcb common init completed + *@rcb_common: rcb common device + */ +void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common) +{ + wmb(); /* Sync point before breakpoint */ + dsaf_write_dev(rcb_common, RCB_COM_CFG_SYS_FSH_REG, 1); + wmb(); /* Sync point after breakpoint */ +} + +/** + *hns_rcb_ring_init - init rcb ring + *@ring_pair: ring pair control block + *@ring_type: ring type, RX_RING or TX_RING + */ +static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) +{ + struct hnae_queue *q = &ring_pair->q; + struct rcb_common_cb *rcb_common = ring_pair->rcb_common; + u32 bd_size_type = rcb_common->dsaf_dev->buf_size_type; + struct hnae_ring *ring = + (ring_type == RX_RING) ? &q->rx_ring : &q->tx_ring; + dma_addr_t dma = ring->desc_dma_addr; + + if (ring_type == RX_RING) { + dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_L_REG, + (u32)dma); + dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG, + bd_size_type); + dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG, + ring_pair->port_id_in_dsa); + dsaf_write_dev(q, RCB_RING_RX_RING_PKTLINE_REG, + ring_pair->port_id_in_dsa); + } else { + dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_L_REG, + (u32)dma); + dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG, + bd_size_type); + dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG, + ring_pair->port_id_in_dsa); + dsaf_write_dev(q, RCB_RING_TX_RING_PKTLINE_REG, + ring_pair->port_id_in_dsa); + } +} + +/** + *hns_rcb_init_hw - init rcb hardware + *@ring: rcb ring + */ +void hns_rcb_init_hw(struct ring_pair_cb *ring) +{ + hns_rcb_ring_init(ring, RX_RING); + hns_rcb_ring_init(ring, TX_RING); +} + +/** + *hns_rcb_set_port_desc_cnt - set rcb port description num + *@rcb_common: rcb_common device + *@port_idx:port index + *@desc_cnt:BD num + */ +static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common, + u32 port_idx, u32 desc_cnt) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + + dsaf_write_dev(rcb_common, RCB_CFG_BD_NUM_REG + port_idx * 4, + desc_cnt); +} + +/** + *hns_rcb_set_port_coalesced_frames - set rcb port coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port index + *@coalesced_frames:BD num for coalesced frames + */ +static int hns_rcb_set_port_coalesced_frames(struct rcb_common_cb *rcb_common, + u32 port_idx, + u32 coalesced_frames) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + if (coalesced_frames >= rcb_common->desc_num || + coalesced_frames > HNS_RCB_MAX_COALESCED_FRAMES) + return -EINVAL; + + dsaf_write_dev(rcb_common, RCB_CFG_PKTLINE_REG + port_idx * 4, + coalesced_frames); + return 0; +} + +/** + *hns_rcb_get_port_coalesced_frames - set rcb port coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port index + * return coaleseced frames value + */ +static u32 hns_rcb_get_port_coalesced_frames(struct rcb_common_cb *rcb_common, + u32 port_idx) +{ + if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) + port_idx = 0; + + return dsaf_read_dev(rcb_common, + RCB_CFG_PKTLINE_REG + port_idx * 4); +} + +/** + *hns_rcb_set_timeout - set rcb port coalesced time_out + *@rcb_common: rcb_common device + *@time_out:time for coalesced time_out + */ +static void hns_rcb_set_timeout(struct rcb_common_cb *rcb_common, + u32 timeout) +{ + dsaf_write_dev(rcb_common, RCB_CFG_OVERTIME_REG, timeout); +} + +static int hns_rcb_common_get_port_num(struct rcb_common_cb *rcb_common) +{ + if (rcb_common->comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + return HNS_RCB_SERVICE_NW_ENGINE_NUM; + else + return HNS_RCB_DEBUG_NW_ENGINE_NUM; +} + +/*clr rcb comm exception irq**/ +static void hns_rcb_comm_exc_irq_en( + struct rcb_common_cb *rcb_common, int en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0 : 0xfffffffful; + + /* clr int*/ + dsaf_write_dev(rcb_common, RCB_COM_INTSTS_ECC_ERR_REG, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_RING_STS, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_BD_RINT_STS, clr_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_RINT_TX_PKT_REG, clr_vlue); + dsaf_write_dev(rcb_common, RCB_COM_AXI_ERR_STS, clr_vlue); + + /*en msk*/ + dsaf_write_dev(rcb_common, RCB_COM_INTMASK_ECC_ERR_REG, msk_vlue); + + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_RING, msk_vlue); + + /*for tx bd neednot cacheline, so msk sf_txring_fbd_intmask (bit 1)**/ + dsaf_write_dev(rcb_common, RCB_COM_SF_CFG_INTMASK_BD, msk_vlue | 2); + + dsaf_write_dev(rcb_common, RCB_COM_INTMSK_TX_PKT_REG, msk_vlue); + dsaf_write_dev(rcb_common, RCB_COM_AXI_WR_ERR_INTMASK, msk_vlue); +} + +/** + *hns_rcb_common_init_hw - init rcb common hardware + *@rcb_common: rcb_common device + *retuen 0 - success , negative --fail + */ +int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common) +{ + u32 reg_val; + int i; + int port_num = hns_rcb_common_get_port_num(rcb_common); + + hns_rcb_comm_exc_irq_en(rcb_common, 0); + + reg_val = dsaf_read_dev(rcb_common, RCB_COM_CFG_INIT_FLAG_REG); + if (0x1 != (reg_val & 0x1)) { + dev_err(rcb_common->dsaf_dev->dev, + "RCB_COM_CFG_INIT_FLAG_REG reg = 0x%x\n", reg_val); + return -EBUSY; + } + + for (i = 0; i < port_num; i++) { + hns_rcb_set_port_desc_cnt(rcb_common, i, rcb_common->desc_num); + (void)hns_rcb_set_port_coalesced_frames( + rcb_common, i, rcb_common->coalesced_frames); + } + hns_rcb_set_timeout(rcb_common, rcb_common->timeout); + + dsaf_write_dev(rcb_common, RCB_COM_CFG_ENDIAN_REG, + HNS_RCB_COMMON_ENDIAN); + + return 0; +} + +int hns_rcb_buf_size2type(u32 buf_size) +{ + int bd_size_type; + + switch (buf_size) { + case 512: + bd_size_type = HNS_BD_SIZE_512_TYPE; + break; + case 1024: + bd_size_type = HNS_BD_SIZE_1024_TYPE; + break; + case 2048: + bd_size_type = HNS_BD_SIZE_2048_TYPE; + break; + case 4096: + bd_size_type = HNS_BD_SIZE_4096_TYPE; + break; + default: + bd_size_type = -EINVAL; + } + + return bd_size_type; +} + +static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) +{ + struct hnae_ring *ring; + struct rcb_common_cb *rcb_common; + struct ring_pair_cb *ring_pair_cb; + u32 buf_size; + u16 desc_num; + int irq_idx; + + ring_pair_cb = container_of(q, struct ring_pair_cb, q); + if (ring_type == RX_RING) { + ring = &q->rx_ring; + ring->io_base = ring_pair_cb->q.io_base; + irq_idx = HNS_RCB_IRQ_IDX_RX; + } else { + ring = &q->tx_ring; + ring->io_base = (u8 __iomem *)ring_pair_cb->q.io_base + + HNS_RCB_TX_REG_OFFSET; + irq_idx = HNS_RCB_IRQ_IDX_TX; + } + + rcb_common = ring_pair_cb->rcb_common; + buf_size = rcb_common->dsaf_dev->buf_size; + desc_num = rcb_common->dsaf_dev->desc_num; + + ring->desc = NULL; + ring->desc_cb = NULL; + + ring->irq = ring_pair_cb->virq[irq_idx]; + ring->desc_dma_addr = 0; + + ring->buf_size = buf_size; + ring->desc_num = desc_num; + ring->max_desc_num_per_pkt = HNS_RCB_RING_MAX_BD_PER_PKT; + ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE; + ring->max_pkt_size = HNS_RCB_MAX_PKT_SIZE; + ring->next_to_use = 0; + ring->next_to_clean = 0; +} + +static void hns_rcb_ring_pair_get_cfg(struct ring_pair_cb *ring_pair_cb) +{ + ring_pair_cb->q.handle = NULL; + + hns_rcb_ring_get_cfg(&ring_pair_cb->q, RX_RING); + hns_rcb_ring_get_cfg(&ring_pair_cb->q, TX_RING); +} + +static int hns_rcb_get_port(struct rcb_common_cb *rcb_common, int ring_idx) +{ + int comm_index = rcb_common->comm_index; + int port; + int q_num; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + q_num = (int)rcb_common->max_q_per_vf * rcb_common->max_vfn; + port = ring_idx / q_num; + } else { + port = HNS_RCB_SERVICE_NW_ENGINE_NUM + comm_index - 1; + } + + return port; +} + +static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common) +{ + int comm_index = rcb_common->comm_index; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + return HNS_SERVICE_RING_IRQ_IDX; + else + return HNS_DEBUG_RING_IRQ_IDX + (comm_index - 1) * 2; +} + +#define RCB_COMM_BASE_TO_RING_BASE(base, ringid)\ + ((base) + 0x10000 + HNS_RCB_REG_OFFSET * (ringid)) +/** + *hns_rcb_get_cfg - get rcb config + *@rcb_common: rcb common device + */ +void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) +{ + struct ring_pair_cb *ring_pair_cb; + u32 i; + u32 ring_num = rcb_common->ring_num; + int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common); + struct device_node *np = rcb_common->dsaf_dev->dev->of_node; + + for (i = 0; i < ring_num; i++) { + ring_pair_cb = &rcb_common->ring_pair_cb[i]; + ring_pair_cb->rcb_common = rcb_common; + ring_pair_cb->dev = rcb_common->dsaf_dev->dev; + ring_pair_cb->index = i; + ring_pair_cb->q.io_base = + RCB_COMM_BASE_TO_RING_BASE(rcb_common->io_base, i); + ring_pair_cb->port_id_in_dsa = hns_rcb_get_port(rcb_common, i); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] + = irq_of_parse_and_map(np, base_irq_idx + i * 2); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] + = irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1); + ring_pair_cb->q.phy_base = + RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i); + hns_rcb_ring_pair_get_cfg(ring_pair_cb); + } +} + +/** + *hns_rcb_get_coalesced_frames - get rcb port coalesced frames + *@rcb_common: rcb_common device + *@comm_index:port index + *return coalesced_frames + */ +u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int port) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + return hns_rcb_get_port_coalesced_frames(rcb_comm, port); +} + +/** + *hns_rcb_get_coalesce_usecs - get rcb port coalesced time_out + *@rcb_common: rcb_common device + *@comm_index:port index + *return time_out + */ +u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index) +{ + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + return rcb_comm->timeout; +} + +/** + *hns_rcb_set_coalesce_usecs - set rcb port coalesced time_out + *@rcb_common: rcb_common device + *@comm_index: comm :index + *@etx_usecs:tx time for coalesced time_out + *@rx_usecs:rx time for coalesced time_out + */ +void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev, + int port, u32 timeout) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + + if (rcb_comm->timeout == timeout) + return; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + dev_err(dsaf_dev->dev, + "error: not support coalesce_usecs setting!\n"); + return; + } + rcb_comm->timeout = timeout; + hns_rcb_set_timeout(rcb_comm, rcb_comm->timeout); +} + +/** + *hns_rcb_set_coalesced_frames - set rcb coalesced frames + *@rcb_common: rcb_common device + *@tx_frames:tx BD num for coalesced frames + *@rx_frames:rx BD num for coalesced frames + *Return 0 on success, negative on failure + */ +int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev, + int port, u32 coalesced_frames) +{ + int comm_index = hns_dsaf_get_comm_idx_by_port(port); + struct rcb_common_cb *rcb_comm = dsaf_dev->rcb_common[comm_index]; + u32 coalesced_reg_val; + int ret; + + coalesced_reg_val = hns_rcb_get_port_coalesced_frames(rcb_comm, port); + + if (coalesced_reg_val == coalesced_frames) + return 0; + + if (coalesced_frames >= HNS_RCB_MIN_COALESCED_FRAMES) { + ret = hns_rcb_set_port_coalesced_frames(rcb_comm, port, + coalesced_frames); + return ret; + } else { + return -EINVAL; + } +} + +/** + *hns_rcb_get_queue_mode - get max VM number and max ring number per VM + * accordding to dsaf mode + *@dsaf_mode: dsaf mode + *@max_vfn : max vfn number + *@max_q_per_vf:max ring number per vm + */ +void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, + u16 *max_vfn, u16 *max_q_per_vf) +{ + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + switch (dsaf_mode) { + case DSAF_MODE_DISABLE_6PORT_0VM: + *max_vfn = 1; + *max_q_per_vf = 16; + break; + case DSAF_MODE_DISABLE_FIX: + *max_vfn = 1; + *max_q_per_vf = 1; + break; + case DSAF_MODE_DISABLE_2PORT_64VM: + *max_vfn = 64; + *max_q_per_vf = 1; + break; + case DSAF_MODE_DISABLE_6PORT_16VM: + *max_vfn = 16; + *max_q_per_vf = 1; + break; + default: + *max_vfn = 1; + *max_q_per_vf = 16; + break; + } + } else { + *max_vfn = 1; + *max_q_per_vf = 1; + } +} + +int hns_rcb_get_ring_num(struct dsaf_device *dsaf_dev, int comm_index) +{ + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + switch (dsaf_dev->dsaf_mode) { + case DSAF_MODE_ENABLE_FIX: + return 1; + + case DSAF_MODE_DISABLE_FIX: + return 6; + + case DSAF_MODE_ENABLE_0VM: + return 32; + + case DSAF_MODE_DISABLE_6PORT_0VM: + case DSAF_MODE_ENABLE_16VM: + case DSAF_MODE_DISABLE_6PORT_2VM: + case DSAF_MODE_DISABLE_6PORT_16VM: + case DSAF_MODE_DISABLE_6PORT_4VM: + case DSAF_MODE_ENABLE_8VM: + return 96; + + case DSAF_MODE_DISABLE_2PORT_16VM: + case DSAF_MODE_DISABLE_2PORT_8VM: + case DSAF_MODE_ENABLE_32VM: + case DSAF_MODE_DISABLE_2PORT_64VM: + case DSAF_MODE_ENABLE_128VM: + return 128; + + default: + dev_warn(dsaf_dev->dev, + "get ring num fail,use default!dsaf_mode=%d\n", + dsaf_dev->dsaf_mode); + return 128; + } + } else { + return 1; + } +} + +void __iomem *hns_rcb_common_get_vaddr(struct dsaf_device *dsaf_dev, + int comm_index) +{ + void __iomem *base_addr; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) + base_addr = dsaf_dev->ppe_base + RCB_COMMON_REG_OFFSET; + else + base_addr = dsaf_dev->sds_base + + (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + RCB_COMMON_REG_OFFSET; + + return base_addr; +} + +static phys_addr_t hns_rcb_common_get_paddr(struct dsaf_device *dsaf_dev, + int comm_index) +{ + struct device_node *np = dsaf_dev->dev->of_node; + phys_addr_t phy_addr; + const __be32 *tmp_addr; + u64 addr_offset = 0; + u64 size = 0; + int index = 0; + + if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) { + index = 2; + addr_offset = RCB_COMMON_REG_OFFSET; + } else { + index = 1; + addr_offset = (comm_index - 1) * HNS_DSAF_DEBUG_NW_REG_OFFSET + + RCB_COMMON_REG_OFFSET; + } + tmp_addr = of_get_address(np, index, &size, NULL); + phy_addr = of_translate_address(np, tmp_addr); + return phy_addr + addr_offset; +} + +int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, + int comm_index) +{ + struct rcb_common_cb *rcb_common; + enum dsaf_mode dsaf_mode = dsaf_dev->dsaf_mode; + u16 max_vfn; + u16 max_q_per_vf; + int ring_num = hns_rcb_get_ring_num(dsaf_dev, comm_index); + + rcb_common = + devm_kzalloc(dsaf_dev->dev, sizeof(*rcb_common) + + ring_num * sizeof(struct ring_pair_cb), GFP_KERNEL); + if (!rcb_common) { + dev_err(dsaf_dev->dev, "rcb common devm_kzalloc fail!\n"); + return -ENOMEM; + } + rcb_common->comm_index = comm_index; + rcb_common->ring_num = ring_num; + rcb_common->dsaf_dev = dsaf_dev; + + rcb_common->desc_num = dsaf_dev->desc_num; + rcb_common->coalesced_frames = HNS_RCB_DEF_COALESCED_FRAMES; + rcb_common->timeout = HNS_RCB_MAX_TIME_OUT; + + hns_rcb_get_queue_mode(dsaf_mode, comm_index, &max_vfn, &max_q_per_vf); + rcb_common->max_vfn = max_vfn; + rcb_common->max_q_per_vf = max_q_per_vf; + + rcb_common->io_base = hns_rcb_common_get_vaddr(dsaf_dev, comm_index); + rcb_common->phy_base = hns_rcb_common_get_paddr(dsaf_dev, comm_index); + + dsaf_dev->rcb_common[comm_index] = rcb_common; + return 0; +} + +void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, + u32 comm_index) +{ + dsaf_dev->rcb_common[comm_index] = NULL; +} + +void hns_rcb_update_stats(struct hnae_queue *queue) +{ + struct ring_pair_cb *ring = + container_of(queue, struct ring_pair_cb, q); + struct dsaf_device *dsaf_dev = ring->rcb_common->dsaf_dev; + struct ppe_common_cb *ppe_common + = dsaf_dev->ppe_common[ring->rcb_common->comm_index]; + struct hns_ring_hw_stats *hw_stats = &ring->hw_stats; + + hw_stats->rx_pkts += dsaf_read_dev(queue, + RCB_RING_RX_RING_PKTNUM_RECORD_REG); + dsaf_write_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG, 0x1); + + hw_stats->ppe_rx_ok_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG + 4 * ring->index); + hw_stats->ppe_rx_drop_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG + 4 * ring->index); + + hw_stats->tx_pkts += dsaf_read_dev(queue, + RCB_RING_TX_RING_PKTNUM_RECORD_REG); + dsaf_write_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG, 0x1); + + hw_stats->ppe_tx_ok_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG + 4 * ring->index); + hw_stats->ppe_tx_drop_pkts += dsaf_read_dev(ppe_common, + PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG + 4 * ring->index); +} + +/** + *hns_rcb_get_stats - get rcb statistic + *@ring: rcb ring + *@data:statistic value + */ +void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data) +{ + u64 *regs_buff = data; + struct ring_pair_cb *ring = + container_of(queue, struct ring_pair_cb, q); + struct hns_ring_hw_stats *hw_stats = &ring->hw_stats; + + regs_buff[0] = hw_stats->tx_pkts; + regs_buff[1] = hw_stats->ppe_tx_ok_pkts; + regs_buff[2] = hw_stats->ppe_tx_drop_pkts; + regs_buff[3] = + dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG); + + regs_buff[4] = queue->tx_ring.stats.tx_pkts; + regs_buff[5] = queue->tx_ring.stats.tx_bytes; + regs_buff[6] = queue->tx_ring.stats.tx_err_cnt; + regs_buff[7] = queue->tx_ring.stats.io_err_cnt; + regs_buff[8] = queue->tx_ring.stats.sw_err_cnt; + regs_buff[9] = queue->tx_ring.stats.seg_pkt_cnt; + regs_buff[10] = queue->tx_ring.stats.restart_queue; + regs_buff[11] = queue->tx_ring.stats.tx_busy; + + regs_buff[12] = hw_stats->rx_pkts; + regs_buff[13] = hw_stats->ppe_rx_ok_pkts; + regs_buff[14] = hw_stats->ppe_rx_drop_pkts; + regs_buff[15] = + dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG); + + regs_buff[16] = queue->rx_ring.stats.rx_pkts; + regs_buff[17] = queue->rx_ring.stats.rx_bytes; + regs_buff[18] = queue->rx_ring.stats.rx_err_cnt; + regs_buff[19] = queue->rx_ring.stats.io_err_cnt; + regs_buff[20] = queue->rx_ring.stats.sw_err_cnt; + regs_buff[21] = queue->rx_ring.stats.seg_pkt_cnt; + regs_buff[22] = queue->rx_ring.stats.reuse_pg_cnt; + regs_buff[23] = queue->rx_ring.stats.err_pkt_len; + regs_buff[24] = queue->rx_ring.stats.non_vld_descs; + regs_buff[25] = queue->rx_ring.stats.err_bd_num; + regs_buff[26] = queue->rx_ring.stats.l2_err; + regs_buff[27] = queue->rx_ring.stats.l3l4_csum_err; +} + +/** + *hns_rcb_get_ring_sset_count - rcb string set count + *@stringset:ethtool cmd + *return rcb ring string set count + */ +int hns_rcb_get_ring_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return HNS_RING_STATIC_REG_NUM; + + return 0; +} + +/** + *hns_rcb_get_common_regs_count - rcb common regs count + *return regs count + */ +int hns_rcb_get_common_regs_count(void) +{ + return HNS_RCB_COMMON_DUMP_REG_NUM; +} + +/** + *rcb_get_sset_count - rcb ring regs count + *return regs count + */ +int hns_rcb_get_ring_regs_count(void) +{ + return HNS_RCB_RING_DUMP_REG_NUM; +} + +/** + *hns_rcb_get_strings - get rcb string set + *@stringset:string set index + *@data:strings name value + *@index:queue index + */ +void hns_rcb_get_strings(int stringset, u8 *data, int index) +{ + char *buff = (char *)data; + + if (stringset != ETH_SS_STATS) + return; + + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_rcb_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_tx_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_ppe_drop_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_fbd_num", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_bytes", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_err_cnt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_io_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_sw_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_seg_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_restart_queue", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_ring%d_tx_busy", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_rcb_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_ppe_drop_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_fbd_num", index); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_pkt_num", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bytes", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_err_cnt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_io_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_sw_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_seg_pkt", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_reuse_pg", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_len_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_non_vld_desc_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_bd_num_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l2_err", index); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_ring%d_l3l4csum_err", index); +} + +void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_com, void *data) +{ + u32 *regs = data; + u32 i = 0; + + /*rcb common registers */ + regs[0] = dsaf_read_dev(rcb_com, RCB_COM_CFG_ENDIAN_REG); + regs[1] = dsaf_read_dev(rcb_com, RCB_COM_CFG_SYS_FSH_REG); + regs[2] = dsaf_read_dev(rcb_com, RCB_COM_CFG_INIT_FLAG_REG); + + regs[3] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_REG); + regs[4] = dsaf_read_dev(rcb_com, RCB_COM_CFG_RINVLD_REG); + regs[5] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FNA_REG); + regs[6] = dsaf_read_dev(rcb_com, RCB_COM_CFG_FA_REG); + regs[7] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PKT_TC_BP_REG); + regs[8] = dsaf_read_dev(rcb_com, RCB_COM_CFG_PPE_TNL_CLKEN_REG); + + regs[9] = dsaf_read_dev(rcb_com, RCB_COM_INTMSK_TX_PKT_REG); + regs[10] = dsaf_read_dev(rcb_com, RCB_COM_RINT_TX_PKT_REG); + regs[11] = dsaf_read_dev(rcb_com, RCB_COM_INTMASK_ECC_ERR_REG); + regs[12] = dsaf_read_dev(rcb_com, RCB_COM_INTSTS_ECC_ERR_REG); + regs[13] = dsaf_read_dev(rcb_com, RCB_COM_EBD_SRAM_ERR_REG); + regs[14] = dsaf_read_dev(rcb_com, RCB_COM_RXRING_ERR_REG); + regs[15] = dsaf_read_dev(rcb_com, RCB_COM_TXRING_ERR_REG); + regs[16] = dsaf_read_dev(rcb_com, RCB_COM_TX_FBD_ERR_REG); + regs[17] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK_EN_REG); + regs[18] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK0_REG); + regs[19] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK1_REG); + regs[20] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK2_REG); + regs[21] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK3_REG); + regs[22] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK4_REG); + regs[23] = dsaf_read_dev(rcb_com, RCB_SRAM_ECC_CHK5_REG); + regs[24] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR0_REG); + regs[25] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR3_REG); + regs[26] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR4_REG); + regs[27] = dsaf_read_dev(rcb_com, RCB_ECC_ERR_ADDR5_REG); + + regs[28] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_RING); + regs[29] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING_STS); + regs[30] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_RING); + regs[31] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_INTMASK_BD); + regs[32] = dsaf_read_dev(rcb_com, RCB_COM_SF_CFG_BD_RINT_STS); + regs[33] = dsaf_read_dev(rcb_com, RCB_COM_RCB_RD_BD_BUSY); + regs[34] = dsaf_read_dev(rcb_com, RCB_COM_RCB_FBD_CRT_EN); + regs[35] = dsaf_read_dev(rcb_com, RCB_COM_AXI_WR_ERR_INTMASK); + regs[36] = dsaf_read_dev(rcb_com, RCB_COM_AXI_ERR_STS); + regs[37] = dsaf_read_dev(rcb_com, RCB_COM_CHK_TX_FBD_NUM_REG); + + /* rcb common entry registers */ + for (i = 0; i < 16; i++) { /* total 16 model registers */ + regs[38 + i] + = dsaf_read_dev(rcb_com, RCB_CFG_BD_NUM_REG + 4 * i); + regs[54 + i] + = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_REG + 4 * i); + } + + regs[70] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_REG); + regs[71] = dsaf_read_dev(rcb_com, RCB_CFG_PKTLINE_INT_NUM_REG); + regs[72] = dsaf_read_dev(rcb_com, RCB_CFG_OVERTIME_INT_NUM_REG); + + /* mark end of rcb common regs */ + for (i = 73; i < 80; i++) + regs[i] = 0xcccccccc; +} + +void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data) +{ + u32 *regs = data; + struct ring_pair_cb *ring_pair + = container_of(queue, struct ring_pair_cb, q); + u32 i = 0; + + /*rcb ring registers */ + regs[0] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_L_REG); + regs[1] = dsaf_read_dev(queue, RCB_RING_RX_RING_BASEADDR_H_REG); + regs[2] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_NUM_REG); + regs[3] = dsaf_read_dev(queue, RCB_RING_RX_RING_BD_LEN_REG); + regs[4] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTLINE_REG); + regs[5] = dsaf_read_dev(queue, RCB_RING_RX_RING_TAIL_REG); + regs[6] = dsaf_read_dev(queue, RCB_RING_RX_RING_HEAD_REG); + regs[7] = dsaf_read_dev(queue, RCB_RING_RX_RING_FBDNUM_REG); + regs[8] = dsaf_read_dev(queue, RCB_RING_RX_RING_PKTNUM_RECORD_REG); + + regs[9] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_L_REG); + regs[10] = dsaf_read_dev(queue, RCB_RING_TX_RING_BASEADDR_H_REG); + regs[11] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_NUM_REG); + regs[12] = dsaf_read_dev(queue, RCB_RING_TX_RING_BD_LEN_REG); + regs[13] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTLINE_REG); + regs[15] = dsaf_read_dev(queue, RCB_RING_TX_RING_TAIL_REG); + regs[16] = dsaf_read_dev(queue, RCB_RING_TX_RING_HEAD_REG); + regs[17] = dsaf_read_dev(queue, RCB_RING_TX_RING_FBDNUM_REG); + regs[18] = dsaf_read_dev(queue, RCB_RING_TX_RING_OFFSET_REG); + regs[19] = dsaf_read_dev(queue, RCB_RING_TX_RING_PKTNUM_RECORD_REG); + + regs[20] = dsaf_read_dev(queue, RCB_RING_PREFETCH_EN_REG); + regs[21] = dsaf_read_dev(queue, RCB_RING_CFG_VF_NUM_REG); + regs[22] = dsaf_read_dev(queue, RCB_RING_ASID_REG); + regs[23] = dsaf_read_dev(queue, RCB_RING_RX_VM_REG); + regs[24] = dsaf_read_dev(queue, RCB_RING_T0_BE_RST); + regs[25] = dsaf_read_dev(queue, RCB_RING_COULD_BE_RST); + regs[26] = dsaf_read_dev(queue, RCB_RING_WRR_WEIGHT_REG); + + regs[27] = dsaf_read_dev(queue, RCB_RING_INTMSK_RXWL_REG); + regs[28] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_RING_REG); + regs[29] = dsaf_read_dev(queue, RCB_RING_INTMSK_TXWL_REG); + regs[30] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_RING_REG); + regs[31] = dsaf_read_dev(queue, RCB_RING_INTMSK_RX_OVERTIME_REG); + regs[32] = dsaf_read_dev(queue, RCB_RING_INTSTS_RX_OVERTIME_REG); + regs[33] = dsaf_read_dev(queue, RCB_RING_INTMSK_TX_OVERTIME_REG); + regs[34] = dsaf_read_dev(queue, RCB_RING_INTSTS_TX_OVERTIME_REG); + + /* mark end of ring regs */ + for (i = 35; i < 40; i++) + regs[i] = 0xcccccc00 + ring_pair->index; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h new file mode 100644 index 000000000000..3a2afe2dd8bb --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_DSAF_RCB_H +#define _HNS_DSAF_RCB_H + +#include <linux/netdevice.h> +#include <linux/platform_device.h> + +#include "hnae.h" +#include "hns_dsaf_main.h" + +struct rcb_common_cb; + +#define HNS_RCB_IRQ_NUM_PER_QUEUE 2 +#define HNS_RCB_IRQ_IDX_TX 0 +#define HNS_RCB_IRQ_IDX_RX 1 +#define HNS_RCB_TX_REG_OFFSET 0x40 + +#define HNS_RCB_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN +#define HNS_RCB_DEBUG_NW_ENGINE_NUM 1 +#define HNS_RCB_RING_MAX_BD_PER_PKT 3 +#define HNS_RCB_MAX_PKT_SIZE MAC_MAX_MTU + +#define HNS_RCB_RING_MAX_PENDING_BD 1024 +#define HNS_RCB_RING_MIN_PENDING_BD 16 + +#define HNS_RCB_REG_OFFSET 0x10000 + +#define HNS_RCB_MAX_COALESCED_FRAMES 1023 +#define HNS_RCB_MIN_COALESCED_FRAMES 1 +#define HNS_RCB_DEF_COALESCED_FRAMES 50 +#define HNS_RCB_MAX_TIME_OUT 0x500 + +#define HNS_RCB_COMMON_ENDIAN 1 + +#define HNS_BD_SIZE_512_TYPE 0 +#define HNS_BD_SIZE_1024_TYPE 1 +#define HNS_BD_SIZE_2048_TYPE 2 +#define HNS_BD_SIZE_4096_TYPE 3 + +#define HNS_RCB_COMMON_DUMP_REG_NUM 80 +#define HNS_RCB_RING_DUMP_REG_NUM 40 +#define HNS_RING_STATIC_REG_NUM 28 + +#define HNS_DUMP_REG_NUM 500 +#define HNS_STATIC_REG_NUM 12 + +enum rcb_int_flag { + RCB_INT_FLAG_TX = 0x1, + RCB_INT_FLAG_RX = (0x1 << 1), + RCB_INT_FLAG_MAX = (0x1 << 2), /*must be the last element */ +}; + +struct hns_ring_hw_stats { + u64 tx_pkts; + u64 ppe_tx_ok_pkts; + u64 ppe_tx_drop_pkts; + u64 rx_pkts; + u64 ppe_rx_ok_pkts; + u64 ppe_rx_drop_pkts; +}; + +struct ring_pair_cb { + struct rcb_common_cb *rcb_common; /* ring belongs to */ + struct device *dev; /*device for DMA mapping */ + struct hnae_queue q; + + u16 index; /* global index in a rcb common device */ + u16 buf_size; + + int virq[HNS_RCB_IRQ_NUM_PER_QUEUE]; + + u8 port_id_in_dsa; + u8 used_by_vf; + + struct hns_ring_hw_stats hw_stats; +}; + +struct rcb_common_cb { + u8 __iomem *io_base; + phys_addr_t phy_base; + struct dsaf_device *dsaf_dev; + u16 max_vfn; + u16 max_q_per_vf; + + u8 comm_index; + u32 ring_num; + u32 coalesced_frames; /* frames threshold of rx interrupt */ + u32 timeout; /* time threshold of rx interrupt */ + u32 desc_num; /* desc num per queue*/ + + struct ring_pair_cb ring_pair_cb[0]; +}; + +int hns_rcb_buf_size2type(u32 buf_size); + +int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index); +void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index); +int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common); +void hns_rcb_start(struct hnae_queue *q, u32 val); +void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common); +void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common); +void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, + u16 *max_vfn, u16 *max_q_per_vf); + +void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val); +void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag); +void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 enable); +void hns_rcb_init_hw(struct ring_pair_cb *ring); +void hns_rcb_reset_ring_hw(struct hnae_queue *q); +void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag); + +u32 hns_rcb_get_coalesced_frames(struct dsaf_device *dsaf_dev, int comm_index); +u32 hns_rcb_get_coalesce_usecs(struct dsaf_device *dsaf_dev, int comm_index); +void hns_rcb_set_coalesce_usecs(struct dsaf_device *dsaf_dev, + int comm_index, u32 timeout); +int hns_rcb_set_coalesced_frames(struct dsaf_device *dsaf_dev, + int comm_index, u32 coalesce_frames); +void hns_rcb_update_stats(struct hnae_queue *queue); + +void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data); + +void hns_rcb_get_common_regs(struct rcb_common_cb *rcb_common, void *data); + +int hns_rcb_get_ring_sset_count(int stringset); +int hns_rcb_get_common_regs_count(void); +int hns_rcb_get_ring_regs_count(void); + +void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data); + +void hns_rcb_get_strings(int stringset, u8 *data, int index); +#endif /* _HNS_DSAF_RCB_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h new file mode 100644 index 000000000000..b475e1bf2e6f --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -0,0 +1,972 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _DSAF_REG_H_ +#define _DSAF_REG_H_ + +#define HNS_GE_FIFO_ERR_INTNUM 8 +#define HNS_XGE_ERR_INTNUM 6 +#define HNS_RCB_COMM_ERR_INTNUM 12 +#define HNS_PPE_TNL_ERR_INTNUM 8 +#define HNS_DSAF_EVENT_INTNUM 21 +#define HNS_DEBUG_RING_INTNUM 4 +#define HNS_SERVICE_RING_INTNUM 256 + +#define HNS_DEBUG_RING_IRQ_IDX (HNS_GE_FIFO_ERR_INTNUM + HNS_XGE_ERR_INTNUM +\ + HNS_RCB_COMM_ERR_INTNUM + HNS_PPE_TNL_ERR_INTNUM +\ + HNS_DSAF_EVENT_INTNUM) +#define HNS_SERVICE_RING_IRQ_IDX (HNS_DEBUG_RING_IRQ_IDX +\ + HNS_DEBUG_RING_INTNUM) + +#define DSAF_IRQ_NUM 18 + +#define DSAF_MAX_PORT_NUM_PER_CHIP 8 +#define DSAF_SERVICE_PORT_NUM_PER_DSAF 6 +#define DSAF_MAX_VM_NUM 128 + +#define DSAF_COMM_DEV_NUM 3 +#define DSAF_PPE_INODE_BASE 6 +#define HNS_DSAF_COMM_SERVICE_NW_IDX 0 +#define DSAF_DEBUG_NW_NUM 2 +#define DSAF_SERVICE_NW_NUM 6 +#define DSAF_COMM_CHN DSAF_SERVICE_NW_NUM +#define DSAF_GE_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) +#define DSAF_PORT_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) +#define DSAF_XGE_NUM DSAF_SERVICE_NW_NUM +#define DSAF_NODE_NUM 18 +#define DSAF_XOD_BIG_NUM DSAF_NODE_NUM +#define DSAF_SBM_NUM DSAF_NODE_NUM +#define DSAF_VOQ_NUM DSAF_NODE_NUM +#define DSAF_INODE_NUM DSAF_NODE_NUM +#define DSAF_XOD_NUM 8 +#define DSAF_TBL_NUM 8 +#define DSAF_SW_PORT_NUM 8 +#define DSAF_TOTAL_QUEUE_NUM 129 + +#define DSAF_TCAM_SUM 512 +#define DSAF_LINE_SUM (2048 * 14) + +#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C +#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190 +#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194 +#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300 +#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304 +#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308 +#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C +#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310 +#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314 +#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318 +#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C +#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320 +#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354 +#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00 +#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04 +#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08 +#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C +#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10 +#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14 +#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18 +#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C +#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20 +#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24 +#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48 +#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C +#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060 +#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300 +#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300 +#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304 +#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308 +#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C +#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310 +#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314 +#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328 +#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00 +#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04 +#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08 +#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C +#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10 +#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44 + +/*serdes offset**/ +#define HNS_MAC_HILINK3_REG DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG +#define HNS_MAC_HILINK4_REG DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG +#define HNS_MAC_LANE0_CTLEDFE_REG 0x000BFFCCULL +#define HNS_MAC_LANE1_CTLEDFE_REG 0x000BFFBCULL +#define HNS_MAC_LANE2_CTLEDFE_REG 0x000BFFACULL +#define HNS_MAC_LANE3_CTLEDFE_REG 0x000BFF9CULL +#define HNS_MAC_LANE0_STATE_REG 0x000BFFD4ULL +#define HNS_MAC_LANE1_STATE_REG 0x000BFFC4ULL +#define HNS_MAC_LANE2_STATE_REG 0x000BFFB4ULL +#define HNS_MAC_LANE3_STATE_REG 0x000BFFA4ULL + +#define HILINK_RESET_TIMOUT 10000 + +#define DSAF_SRAM_INIT_OVER_0_REG 0x0 +#define DSAF_CFG_0_REG 0x4 +#define DSAF_ECC_ERR_INVERT_0_REG 0x8 +#define DSAF_ABNORMAL_TIMEOUT_0_REG 0x1C +#define DSAF_FSM_TIMEOUT_0_REG 0x20 +#define DSAF_DSA_REG_CNT_CLR_CE_REG 0x2C +#define DSAF_DSA_SBM_INF_FIFO_THRD_REG 0x30 +#define DSAF_DSA_SRAM_1BIT_ECC_SEL_REG 0x34 +#define DSAF_DSA_SRAM_1BIT_ECC_CNT_REG 0x38 +#define DSAF_PFC_EN_0_REG 0x50 +#define DSAF_PFC_UNIT_CNT_0_REG 0x70 +#define DSAF_XGE_INT_MSK_0_REG 0x100 +#define DSAF_PPE_INT_MSK_0_REG 0x120 +#define DSAF_ROCEE_INT_MSK_0_REG 0x140 +#define DSAF_XGE_INT_SRC_0_REG 0x160 +#define DSAF_PPE_INT_SRC_0_REG 0x180 +#define DSAF_ROCEE_INT_SRC_0_REG 0x1A0 +#define DSAF_XGE_INT_STS_0_REG 0x1C0 +#define DSAF_PPE_INT_STS_0_REG 0x1E0 +#define DSAF_ROCEE_INT_STS_0_REG 0x200 +#define DSAF_PPE_QID_CFG_0_REG 0x300 +#define DSAF_SW_PORT_TYPE_0_REG 0x320 +#define DSAF_STP_PORT_TYPE_0_REG 0x340 +#define DSAF_MIX_DEF_QID_0_REG 0x360 +#define DSAF_PORT_DEF_VLAN_0_REG 0x380 +#define DSAF_VM_DEF_VLAN_0_REG 0x400 + +#define DSAF_INODE_CUT_THROUGH_CFG_0_REG 0x1000 +#define DSAF_INODE_ECC_INVERT_EN_0_REG 0x1008 +#define DSAF_INODE_ECC_ERR_ADDR_0_REG 0x100C +#define DSAF_INODE_IN_PORT_NUM_0_REG 0x1018 +#define DSAF_INODE_PRI_TC_CFG_0_REG 0x101C +#define DSAF_INODE_BP_STATUS_0_REG 0x1020 +#define DSAF_INODE_PAD_DISCARD_NUM_0_REG 0x1028 +#define DSAF_INODE_FINAL_IN_MAN_NUM_0_REG 0x102C +#define DSAF_INODE_FINAL_IN_PKT_NUM_0_REG 0x1030 +#define DSAF_INODE_SBM_PID_NUM_0_REG 0x1038 +#define DSAF_INODE_FINAL_IN_PAUSE_NUM_0_REG 0x103C +#define DSAF_INODE_SBM_RELS_NUM_0_REG 0x104C +#define DSAF_INODE_SBM_DROP_NUM_0_REG 0x1050 +#define DSAF_INODE_CRC_FALSE_NUM_0_REG 0x1054 +#define DSAF_INODE_BP_DISCARD_NUM_0_REG 0x1058 +#define DSAF_INODE_RSLT_DISCARD_NUM_0_REG 0x105C +#define DSAF_INODE_LOCAL_ADDR_FALSE_NUM_0_REG 0x1060 +#define DSAF_INODE_VOQ_OVER_NUM_0_REG 0x1068 +#define DSAF_INODE_BD_SAVE_STATUS_0_REG 0x1900 +#define DSAF_INODE_BD_ORDER_STATUS_0_REG 0x1950 +#define DSAF_INODE_SW_VLAN_TAG_DISC_0_REG 0x1A00 +#define DSAF_INODE_IN_DATA_STP_DISC_0_REG 0x1A50 +#define DSAF_INODE_GE_FC_EN_0_REG 0x1B00 +#define DSAF_INODE_VC0_IN_PKT_NUM_0_REG 0x1B50 +#define DSAF_INODE_VC1_IN_PKT_NUM_0_REG 0x1C00 + +#define DSAF_SBM_CFG_REG_0_REG 0x2000 +#define DSAF_SBM_BP_CFG_0_XGE_REG_0_REG 0x2004 +#define DSAF_SBM_BP_CFG_0_PPE_REG_0_REG 0x2304 +#define DSAF_SBM_BP_CFG_0_ROCEE_REG_0_REG 0x2604 +#define DSAF_SBM_BP_CFG_1_REG_0_REG 0x2008 +#define DSAF_SBM_BP_CFG_2_XGE_REG_0_REG 0x200C +#define DSAF_SBM_BP_CFG_2_PPE_REG_0_REG 0x230C +#define DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x260C +#define DSAF_SBM_FREE_CNT_0_0_REG 0x2010 +#define DSAF_SBM_FREE_CNT_1_0_REG 0x2014 +#define DSAF_SBM_BP_CNT_0_0_REG 0x2018 +#define DSAF_SBM_BP_CNT_1_0_REG 0x201C +#define DSAF_SBM_BP_CNT_2_0_REG 0x2020 +#define DSAF_SBM_BP_CNT_3_0_REG 0x2024 +#define DSAF_SBM_INER_ST_0_REG 0x2028 +#define DSAF_SBM_MIB_REQ_FAILED_TC_0_REG 0x202C +#define DSAF_SBM_LNK_INPORT_CNT_0_REG 0x2030 +#define DSAF_SBM_LNK_DROP_CNT_0_REG 0x2034 +#define DSAF_SBM_INF_OUTPORT_CNT_0_REG 0x2038 +#define DSAF_SBM_LNK_INPORT_TC0_CNT_0_REG 0x203C +#define DSAF_SBM_LNK_INPORT_TC1_CNT_0_REG 0x2040 +#define DSAF_SBM_LNK_INPORT_TC2_CNT_0_REG 0x2044 +#define DSAF_SBM_LNK_INPORT_TC3_CNT_0_REG 0x2048 +#define DSAF_SBM_LNK_INPORT_TC4_CNT_0_REG 0x204C +#define DSAF_SBM_LNK_INPORT_TC5_CNT_0_REG 0x2050 +#define DSAF_SBM_LNK_INPORT_TC6_CNT_0_REG 0x2054 +#define DSAF_SBM_LNK_INPORT_TC7_CNT_0_REG 0x2058 +#define DSAF_SBM_LNK_REQ_CNT_0_REG 0x205C +#define DSAF_SBM_LNK_RELS_CNT_0_REG 0x2060 +#define DSAF_SBM_BP_CFG_3_REG_0_REG 0x2068 +#define DSAF_SBM_BP_CFG_4_REG_0_REG 0x206C + +#define DSAF_XOD_ETS_TSA_TC0_TC3_CFG_0_REG 0x3000 +#define DSAF_XOD_ETS_TSA_TC4_TC7_CFG_0_REG 0x3004 +#define DSAF_XOD_ETS_BW_TC0_TC3_CFG_0_REG 0x3008 +#define DSAF_XOD_ETS_BW_TC4_TC7_CFG_0_REG 0x300C +#define DSAF_XOD_ETS_BW_OFFSET_CFG_0_REG 0x3010 +#define DSAF_XOD_ETS_TOKEN_CFG_0_REG 0x3014 +#define DSAF_XOD_PFS_CFG_0_0_REG 0x3018 +#define DSAF_XOD_PFS_CFG_1_0_REG 0x301C +#define DSAF_XOD_PFS_CFG_2_0_REG 0x3020 +#define DSAF_XOD_GNT_L_0_REG 0x3024 +#define DSAF_XOD_GNT_H_0_REG 0x3028 +#define DSAF_XOD_CONNECT_STATE_0_REG 0x302C +#define DSAF_XOD_RCVPKT_CNT_0_REG 0x3030 +#define DSAF_XOD_RCVTC0_CNT_0_REG 0x3034 +#define DSAF_XOD_RCVTC1_CNT_0_REG 0x3038 +#define DSAF_XOD_RCVTC2_CNT_0_REG 0x303C +#define DSAF_XOD_RCVTC3_CNT_0_REG 0x3040 +#define DSAF_XOD_RCVVC0_CNT_0_REG 0x3044 +#define DSAF_XOD_RCVVC1_CNT_0_REG 0x3048 +#define DSAF_XOD_XGE_RCVIN0_CNT_0_REG 0x304C +#define DSAF_XOD_XGE_RCVIN1_CNT_0_REG 0x3050 +#define DSAF_XOD_XGE_RCVIN2_CNT_0_REG 0x3054 +#define DSAF_XOD_XGE_RCVIN3_CNT_0_REG 0x3058 +#define DSAF_XOD_XGE_RCVIN4_CNT_0_REG 0x305C +#define DSAF_XOD_XGE_RCVIN5_CNT_0_REG 0x3060 +#define DSAF_XOD_XGE_RCVIN6_CNT_0_REG 0x3064 +#define DSAF_XOD_XGE_RCVIN7_CNT_0_REG 0x3068 +#define DSAF_XOD_PPE_RCVIN0_CNT_0_REG 0x306C +#define DSAF_XOD_PPE_RCVIN1_CNT_0_REG 0x3070 +#define DSAF_XOD_ROCEE_RCVIN0_CNT_0_REG 0x3074 +#define DSAF_XOD_ROCEE_RCVIN1_CNT_0_REG 0x3078 +#define DSAF_XOD_FIFO_STATUS_0_REG 0x307C + +#define DSAF_VOQ_ECC_INVERT_EN_0_REG 0x4004 +#define DSAF_VOQ_SRAM_PKT_NUM_0_REG 0x4008 +#define DSAF_VOQ_IN_PKT_NUM_0_REG 0x400C +#define DSAF_VOQ_OUT_PKT_NUM_0_REG 0x4010 +#define DSAF_VOQ_ECC_ERR_ADDR_0_REG 0x4014 +#define DSAF_VOQ_BP_STATUS_0_REG 0x4018 +#define DSAF_VOQ_SPUP_IDLE_0_REG 0x401C +#define DSAF_VOQ_XGE_XOD_REQ_0_0_REG 0x4024 +#define DSAF_VOQ_XGE_XOD_REQ_1_0_REG 0x4028 +#define DSAF_VOQ_PPE_XOD_REQ_0_REG 0x402C +#define DSAF_VOQ_ROCEE_XOD_REQ_0_REG 0x4030 +#define DSAF_VOQ_BP_ALL_THRD_0_REG 0x4034 + +#define DSAF_TBL_CTRL_0_REG 0x5000 +#define DSAF_TBL_INT_MSK_0_REG 0x5004 +#define DSAF_TBL_INT_SRC_0_REG 0x5008 +#define DSAF_TBL_INT_STS_0_REG 0x5100 +#define DSAF_TBL_TCAM_ADDR_0_REG 0x500C +#define DSAF_TBL_LINE_ADDR_0_REG 0x5010 +#define DSAF_TBL_TCAM_HIGH_0_REG 0x5014 +#define DSAF_TBL_TCAM_LOW_0_REG 0x5018 +#define DSAF_TBL_TCAM_MCAST_CFG_4_0_REG 0x501C +#define DSAF_TBL_TCAM_MCAST_CFG_3_0_REG 0x5020 +#define DSAF_TBL_TCAM_MCAST_CFG_2_0_REG 0x5024 +#define DSAF_TBL_TCAM_MCAST_CFG_1_0_REG 0x5028 +#define DSAF_TBL_TCAM_MCAST_CFG_0_0_REG 0x502C +#define DSAF_TBL_TCAM_UCAST_CFG_0_REG 0x5030 +#define DSAF_TBL_LIN_CFG_0_REG 0x5034 +#define DSAF_TBL_TCAM_RDATA_HIGH_0_REG 0x5038 +#define DSAF_TBL_TCAM_RDATA_LOW_0_REG 0x503C +#define DSAF_TBL_TCAM_RAM_RDATA4_0_REG 0x5040 +#define DSAF_TBL_TCAM_RAM_RDATA3_0_REG 0x5044 +#define DSAF_TBL_TCAM_RAM_RDATA2_0_REG 0x5048 +#define DSAF_TBL_TCAM_RAM_RDATA1_0_REG 0x504C +#define DSAF_TBL_TCAM_RAM_RDATA0_0_REG 0x5050 +#define DSAF_TBL_LIN_RDATA_0_REG 0x5054 +#define DSAF_TBL_DA0_MIS_INFO1_0_REG 0x5058 +#define DSAF_TBL_DA0_MIS_INFO0_0_REG 0x505C +#define DSAF_TBL_SA_MIS_INFO2_0_REG 0x5104 +#define DSAF_TBL_SA_MIS_INFO1_0_REG 0x5098 +#define DSAF_TBL_SA_MIS_INFO0_0_REG 0x509C +#define DSAF_TBL_PUL_0_REG 0x50A0 +#define DSAF_TBL_OLD_RSLT_0_REG 0x50A4 +#define DSAF_TBL_OLD_SCAN_VAL_0_REG 0x50A8 +#define DSAF_TBL_DFX_CTRL_0_REG 0x50AC +#define DSAF_TBL_DFX_STAT_0_REG 0x50B0 +#define DSAF_TBL_DFX_STAT_2_0_REG 0x5108 +#define DSAF_TBL_LKUP_NUM_I_0_REG 0x50C0 +#define DSAF_TBL_LKUP_NUM_O_0_REG 0x50E0 +#define DSAF_TBL_UCAST_BCAST_MIS_INFO_0_0_REG 0x510C + +#define DSAF_INODE_FIFO_WL_0_REG 0x6000 +#define DSAF_ONODE_FIFO_WL_0_REG 0x6020 +#define DSAF_XGE_GE_WORK_MODE_0_REG 0x6040 +#define DSAF_XGE_APP_RX_LINK_UP_0_REG 0x6080 +#define DSAF_NETPORT_CTRL_SIG_0_REG 0x60A0 +#define DSAF_XGE_CTRL_SIG_CFG_0_REG 0x60C0 + +#define PPE_COM_CFG_QID_MODE_REG 0x0 +#define PPE_COM_INTEN_REG 0x110 +#define PPE_COM_RINT_REG 0x114 +#define PPE_COM_INTSTS_REG 0x118 +#define PPE_COM_COMMON_CNT_CLR_CE_REG 0x1120 +#define PPE_COM_HIS_RX_PKT_QID_DROP_CNT_REG 0x300 +#define PPE_COM_HIS_RX_PKT_QID_OK_CNT_REG 0x600 +#define PPE_COM_HIS_TX_PKT_QID_ERR_CNT_REG 0x900 +#define PPE_COM_HIS_TX_PKT_QID_OK_CNT_REG 0xC00 +#define PPE_COM_COMMON_CNT_CLR_CE_REG 0x1120 + +#define PPE_CFG_TX_FIFO_THRSLD_REG 0x0 +#define PPE_CFG_RX_FIFO_THRSLD_REG 0x4 +#define PPE_CFG_RX_FIFO_PAUSE_THRSLD_REG 0x8 +#define PPE_CFG_RX_FIFO_SW_BP_THRSLD_REG 0xC +#define PPE_CFG_PAUSE_IDLE_CNT_REG 0x10 +#define PPE_CFG_BUS_CTRL_REG 0x40 +#define PPE_CFG_TNL_TO_BE_RST_REG 0x48 +#define PPE_CURR_TNL_CAN_RST_REG 0x4C +#define PPE_CFG_XGE_MODE_REG 0x80 +#define PPE_CFG_MAX_FRAME_LEN_REG 0x84 +#define PPE_CFG_RX_PKT_MODE_REG 0x88 +#define PPE_CFG_RX_VLAN_TAG_REG 0x8C +#define PPE_CFG_TAG_GEN_REG 0x90 +#define PPE_CFG_PARSE_TAG_REG 0x94 +#define PPE_CFG_PRO_CHECK_EN_REG 0x98 +#define PPE_INTEN_REG 0x100 +#define PPE_RINT_REG 0x104 +#define PPE_INTSTS_REG 0x108 +#define PPE_CFG_RX_PKT_INT_REG 0x140 +#define PPE_CFG_HEAT_DECT_TIME0_REG 0x144 +#define PPE_CFG_HEAT_DECT_TIME1_REG 0x148 +#define PPE_HIS_RX_SW_PKT_CNT_REG 0x200 +#define PPE_HIS_RX_WR_BD_OK_PKT_CNT_REG 0x204 +#define PPE_HIS_RX_PKT_NO_BUF_CNT_REG 0x208 +#define PPE_HIS_TX_BD_CNT_REG 0x20C +#define PPE_HIS_TX_PKT_CNT_REG 0x210 +#define PPE_HIS_TX_PKT_OK_CNT_REG 0x214 +#define PPE_HIS_TX_PKT_EPT_CNT_REG 0x218 +#define PPE_HIS_TX_PKT_CS_FAIL_CNT_REG 0x21C +#define PPE_HIS_RX_APP_BUF_FAIL_CNT_REG 0x220 +#define PPE_HIS_RX_APP_BUF_WAIT_CNT_REG 0x224 +#define PPE_HIS_RX_PKT_DROP_FUL_CNT_REG 0x228 +#define PPE_HIS_RX_PKT_DROP_PRT_CNT_REG 0x22C +#define PPE_TNL_0_5_CNT_CLR_CE_REG 0x300 +#define PPE_CFG_AXI_DBG_REG 0x304 +#define PPE_HIS_PRO_ERR_REG 0x308 +#define PPE_HIS_TNL_FIFO_ERR_REG 0x30C +#define PPE_CURR_CFF_DATA_NUM_REG 0x310 +#define PPE_CURR_RX_ST_REG 0x314 +#define PPE_CURR_TX_ST_REG 0x318 +#define PPE_CURR_RX_FIFO0_REG 0x31C +#define PPE_CURR_RX_FIFO1_REG 0x320 +#define PPE_CURR_TX_FIFO0_REG 0x324 +#define PPE_CURR_TX_FIFO1_REG 0x328 +#define PPE_ECO0_REG 0x32C +#define PPE_ECO1_REG 0x330 +#define PPE_ECO2_REG 0x334 + +#define RCB_COM_CFG_ENDIAN_REG 0x0 +#define RCB_COM_CFG_SYS_FSH_REG 0xC +#define RCB_COM_CFG_INIT_FLAG_REG 0x10 +#define RCB_COM_CFG_PKT_REG 0x30 +#define RCB_COM_CFG_RINVLD_REG 0x34 +#define RCB_COM_CFG_FNA_REG 0x38 +#define RCB_COM_CFG_FA_REG 0x3C +#define RCB_COM_CFG_PKT_TC_BP_REG 0x40 +#define RCB_COM_CFG_PPE_TNL_CLKEN_REG 0x44 + +#define RCB_COM_INTMSK_TX_PKT_REG 0x3A0 +#define RCB_COM_RINT_TX_PKT_REG 0x3A8 +#define RCB_COM_INTMASK_ECC_ERR_REG 0x400 +#define RCB_COM_INTSTS_ECC_ERR_REG 0x408 +#define RCB_COM_EBD_SRAM_ERR_REG 0x410 +#define RCB_COM_RXRING_ERR_REG 0x41C +#define RCB_COM_TXRING_ERR_REG 0x420 +#define RCB_COM_TX_FBD_ERR_REG 0x424 +#define RCB_SRAM_ECC_CHK_EN_REG 0x428 +#define RCB_SRAM_ECC_CHK0_REG 0x42C +#define RCB_SRAM_ECC_CHK1_REG 0x430 +#define RCB_SRAM_ECC_CHK2_REG 0x434 +#define RCB_SRAM_ECC_CHK3_REG 0x438 +#define RCB_SRAM_ECC_CHK4_REG 0x43c +#define RCB_SRAM_ECC_CHK5_REG 0x440 +#define RCB_ECC_ERR_ADDR0_REG 0x450 +#define RCB_ECC_ERR_ADDR3_REG 0x45C +#define RCB_ECC_ERR_ADDR4_REG 0x460 +#define RCB_ECC_ERR_ADDR5_REG 0x464 + +#define RCB_COM_SF_CFG_INTMASK_RING 0x480 +#define RCB_COM_SF_CFG_RING_STS 0x484 +#define RCB_COM_SF_CFG_RING 0x488 +#define RCB_COM_SF_CFG_INTMASK_BD 0x48C +#define RCB_COM_SF_CFG_BD_RINT_STS 0x470 +#define RCB_COM_RCB_RD_BD_BUSY 0x490 +#define RCB_COM_RCB_FBD_CRT_EN 0x494 +#define RCB_COM_AXI_WR_ERR_INTMASK 0x498 +#define RCB_COM_AXI_ERR_STS 0x49C +#define RCB_COM_CHK_TX_FBD_NUM_REG 0x4a0 + +#define RCB_CFG_BD_NUM_REG 0x9000 +#define RCB_CFG_PKTLINE_REG 0x9050 + +#define RCB_CFG_OVERTIME_REG 0x9300 +#define RCB_CFG_PKTLINE_INT_NUM_REG 0x9304 +#define RCB_CFG_OVERTIME_INT_NUM_REG 0x9308 + +#define RCB_RING_RX_RING_BASEADDR_L_REG 0x00000 +#define RCB_RING_RX_RING_BASEADDR_H_REG 0x00004 +#define RCB_RING_RX_RING_BD_NUM_REG 0x00008 +#define RCB_RING_RX_RING_BD_LEN_REG 0x0000C +#define RCB_RING_RX_RING_PKTLINE_REG 0x00010 +#define RCB_RING_RX_RING_TAIL_REG 0x00018 +#define RCB_RING_RX_RING_HEAD_REG 0x0001C +#define RCB_RING_RX_RING_FBDNUM_REG 0x00020 +#define RCB_RING_RX_RING_PKTNUM_RECORD_REG 0x0002C + +#define RCB_RING_TX_RING_BASEADDR_L_REG 0x00040 +#define RCB_RING_TX_RING_BASEADDR_H_REG 0x00044 +#define RCB_RING_TX_RING_BD_NUM_REG 0x00048 +#define RCB_RING_TX_RING_BD_LEN_REG 0x0004C +#define RCB_RING_TX_RING_PKTLINE_REG 0x00050 +#define RCB_RING_TX_RING_TAIL_REG 0x00058 +#define RCB_RING_TX_RING_HEAD_REG 0x0005C +#define RCB_RING_TX_RING_FBDNUM_REG 0x00060 +#define RCB_RING_TX_RING_OFFSET_REG 0x00064 +#define RCB_RING_TX_RING_PKTNUM_RECORD_REG 0x0006C + +#define RCB_RING_PREFETCH_EN_REG 0x0007C +#define RCB_RING_CFG_VF_NUM_REG 0x00080 +#define RCB_RING_ASID_REG 0x0008C +#define RCB_RING_RX_VM_REG 0x00090 +#define RCB_RING_T0_BE_RST 0x00094 +#define RCB_RING_COULD_BE_RST 0x00098 +#define RCB_RING_WRR_WEIGHT_REG 0x0009c + +#define RCB_RING_INTMSK_RXWL_REG 0x000A0 +#define RCB_RING_INTSTS_RX_RING_REG 0x000A4 +#define RCB_RING_INTMSK_TXWL_REG 0x000AC +#define RCB_RING_INTSTS_TX_RING_REG 0x000B0 +#define RCB_RING_INTMSK_RX_OVERTIME_REG 0x000B8 +#define RCB_RING_INTSTS_RX_OVERTIME_REG 0x000BC +#define RCB_RING_INTMSK_TX_OVERTIME_REG 0x000C4 +#define RCB_RING_INTSTS_TX_OVERTIME_REG 0x000C8 + +#define GMAC_DUPLEX_TYPE_REG 0x0008UL +#define GMAC_FD_FC_TYPE_REG 0x000CUL +#define GMAC_FC_TX_TIMER_REG 0x001CUL +#define GMAC_FD_FC_ADDR_LOW_REG 0x0020UL +#define GMAC_FD_FC_ADDR_HIGH_REG 0x0024UL +#define GMAC_IPG_TX_TIMER_REG 0x0030UL +#define GMAC_PAUSE_THR_REG 0x0038UL +#define GMAC_MAX_FRM_SIZE_REG 0x003CUL +#define GMAC_PORT_MODE_REG 0x0040UL +#define GMAC_PORT_EN_REG 0x0044UL +#define GMAC_PAUSE_EN_REG 0x0048UL +#define GMAC_SHORT_RUNTS_THR_REG 0x0050UL +#define GMAC_AN_NEG_STATE_REG 0x0058UL +#define GMAC_TX_LOCAL_PAGE_REG 0x005CUL +#define GMAC_TRANSMIT_CONTROL_REG 0x0060UL +#define GMAC_REC_FILT_CONTROL_REG 0x0064UL +#define GMAC_PTP_CONFIG_REG 0x0074UL + +#define GMAC_RX_OCTETS_TOTAL_OK_REG 0x0080UL +#define GMAC_RX_OCTETS_BAD_REG 0x0084UL +#define GMAC_RX_UC_PKTS_REG 0x0088UL +#define GMAC_RX_MC_PKTS_REG 0x008CUL +#define GMAC_RX_BC_PKTS_REG 0x0090UL +#define GMAC_RX_PKTS_64OCTETS_REG 0x0094UL +#define GMAC_RX_PKTS_65TO127OCTETS_REG 0x0098UL +#define GMAC_RX_PKTS_128TO255OCTETS_REG 0x009CUL +#define GMAC_RX_PKTS_255TO511OCTETS_REG 0x00A0UL +#define GMAC_RX_PKTS_512TO1023OCTETS_REG 0x00A4UL +#define GMAC_RX_PKTS_1024TO1518OCTETS_REG 0x00A8UL +#define GMAC_RX_PKTS_1519TOMAXOCTETS_REG 0x00ACUL +#define GMAC_RX_FCS_ERRORS_REG 0x00B0UL +#define GMAC_RX_TAGGED_REG 0x00B4UL +#define GMAC_RX_DATA_ERR_REG 0x00B8UL +#define GMAC_RX_ALIGN_ERRORS_REG 0x00BCUL +#define GMAC_RX_LONG_ERRORS_REG 0x00C0UL +#define GMAC_RX_JABBER_ERRORS_REG 0x00C4UL +#define GMAC_RX_PAUSE_MACCTRL_FRAM_REG 0x00C8UL +#define GMAC_RX_UNKNOWN_MACCTRL_FRAM_REG 0x00CCUL +#define GMAC_RX_VERY_LONG_ERR_CNT_REG 0x00D0UL +#define GMAC_RX_RUNT_ERR_CNT_REG 0x00D4UL +#define GMAC_RX_SHORT_ERR_CNT_REG 0x00D8UL +#define GMAC_RX_FILT_PKT_CNT_REG 0x00E8UL +#define GMAC_RX_OCTETS_TOTAL_FILT_REG 0x00ECUL +#define GMAC_OCTETS_TRANSMITTED_OK_REG 0x0100UL +#define GMAC_OCTETS_TRANSMITTED_BAD_REG 0x0104UL +#define GMAC_TX_UC_PKTS_REG 0x0108UL +#define GMAC_TX_MC_PKTS_REG 0x010CUL +#define GMAC_TX_BC_PKTS_REG 0x0110UL +#define GMAC_TX_PKTS_64OCTETS_REG 0x0114UL +#define GMAC_TX_PKTS_65TO127OCTETS_REG 0x0118UL +#define GMAC_TX_PKTS_128TO255OCTETS_REG 0x011CUL +#define GMAC_TX_PKTS_255TO511OCTETS_REG 0x0120UL +#define GMAC_TX_PKTS_512TO1023OCTETS_REG 0x0124UL +#define GMAC_TX_PKTS_1024TO1518OCTETS_REG 0x0128UL +#define GMAC_TX_PKTS_1519TOMAXOCTETS_REG 0x012CUL +#define GMAC_TX_EXCESSIVE_LENGTH_DROP_REG 0x014CUL +#define GMAC_TX_UNDERRUN_REG 0x0150UL +#define GMAC_TX_TAGGED_REG 0x0154UL +#define GMAC_TX_CRC_ERROR_REG 0x0158UL +#define GMAC_TX_PAUSE_FRAMES_REG 0x015CUL +#define GAMC_RX_MAX_FRAME 0x0170UL +#define GMAC_LINE_LOOP_BACK_REG 0x01A8UL +#define GMAC_CF_CRC_STRIP_REG 0x01B0UL +#define GMAC_MODE_CHANGE_EN_REG 0x01B4UL +#define GMAC_SIXTEEN_BIT_CNTR_REG 0x01CCUL +#define GMAC_LD_LINK_COUNTER_REG 0x01D0UL +#define GMAC_LOOP_REG 0x01DCUL +#define GMAC_RECV_CONTROL_REG 0x01E0UL +#define GMAC_VLAN_CODE_REG 0x01E8UL +#define GMAC_RX_OVERRUN_CNT_REG 0x01ECUL +#define GMAC_RX_LENGTHFIELD_ERR_CNT_REG 0x01F4UL +#define GMAC_RX_FAIL_COMMA_CNT_REG 0x01F8UL +#define GMAC_STATION_ADDR_LOW_0_REG 0x0200UL +#define GMAC_STATION_ADDR_HIGH_0_REG 0x0204UL +#define GMAC_STATION_ADDR_LOW_1_REG 0x0208UL +#define GMAC_STATION_ADDR_HIGH_1_REG 0x020CUL +#define GMAC_STATION_ADDR_LOW_2_REG 0x0210UL +#define GMAC_STATION_ADDR_HIGH_2_REG 0x0214UL +#define GMAC_STATION_ADDR_LOW_3_REG 0x0218UL +#define GMAC_STATION_ADDR_HIGH_3_REG 0x021CUL +#define GMAC_STATION_ADDR_LOW_4_REG 0x0220UL +#define GMAC_STATION_ADDR_HIGH_4_REG 0x0224UL +#define GMAC_STATION_ADDR_LOW_5_REG 0x0228UL +#define GMAC_STATION_ADDR_HIGH_5_REG 0x022CUL +#define GMAC_STATION_ADDR_LOW_MSK_0_REG 0x0230UL +#define GMAC_STATION_ADDR_HIGH_MSK_0_REG 0x0234UL +#define GMAC_STATION_ADDR_LOW_MSK_1_REG 0x0238UL +#define GMAC_STATION_ADDR_HIGH_MSK_1_REG 0x023CUL +#define GMAC_MAC_SKIP_LEN_REG 0x0240UL +#define GMAC_TX_LOOP_PKT_PRI_REG 0x0378UL + +#define XGMAC_INT_STATUS_REG 0x0 +#define XGMAC_INT_ENABLE_REG 0x4 +#define XGMAC_INT_SET_REG 0x8 +#define XGMAC_IERR_U_INFO_REG 0xC +#define XGMAC_OVF_INFO_REG 0x10 +#define XGMAC_OVF_CNT_REG 0x14 +#define XGMAC_PORT_MODE_REG 0x40 +#define XGMAC_CLK_ENABLE_REG 0x44 +#define XGMAC_RESET_REG 0x48 +#define XGMAC_LINK_CONTROL_REG 0x50 +#define XGMAC_LINK_STATUS_REG 0x54 +#define XGMAC_SPARE_REG 0xC0 +#define XGMAC_SPARE_CNT_REG 0xC4 + +#define XGMAC_MAC_ENABLE_REG 0x100 +#define XGMAC_MAC_CONTROL_REG 0x104 +#define XGMAC_MAC_IPG_REG 0x120 +#define XGMAC_MAC_MSG_CRC_EN_REG 0x124 +#define XGMAC_MAC_MSG_IMG_REG 0x128 +#define XGMAC_MAC_MSG_FC_CFG_REG 0x12C +#define XGMAC_MAC_MSG_TC_CFG_REG 0x130 +#define XGMAC_MAC_PAD_SIZE_REG 0x134 +#define XGMAC_MAC_MIN_PKT_SIZE_REG 0x138 +#define XGMAC_MAC_MAX_PKT_SIZE_REG 0x13C +#define XGMAC_MAC_PAUSE_CTRL_REG 0x160 +#define XGMAC_MAC_PAUSE_TIME_REG 0x164 +#define XGMAC_MAC_PAUSE_GAP_REG 0x168 +#define XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG 0x16C +#define XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG 0x170 +#define XGMAC_MAC_PAUSE_PEER_MAC_H_REG 0x174 +#define XGMAC_MAC_PAUSE_PEER_MAC_L_REG 0x178 +#define XGMAC_MAC_PFC_PRI_EN_REG 0x17C +#define XGMAC_MAC_1588_CTRL_REG 0x180 +#define XGMAC_MAC_1588_TX_PORT_DLY_REG 0x184 +#define XGMAC_MAC_1588_RX_PORT_DLY_REG 0x188 +#define XGMAC_MAC_1588_ASYM_DLY_REG 0x18C +#define XGMAC_MAC_1588_ADJUST_CFG_REG 0x190 +#define XGMAC_MAC_Y1731_ETH_TYPE_REG 0x194 +#define XGMAC_MAC_MIB_CONTROL_REG 0x198 +#define XGMAC_MAC_WAN_RATE_ADJUST_REG 0x19C +#define XGMAC_MAC_TX_ERR_MARK_REG 0x1A0 +#define XGMAC_MAC_TX_LF_RF_CONTROL_REG 0x1A4 +#define XGMAC_MAC_RX_LF_RF_STATUS_REG 0x1A8 +#define XGMAC_MAC_TX_RUNT_PKT_CNT_REG 0x1C0 +#define XGMAC_MAC_RX_RUNT_PKT_CNT_REG 0x1C4 +#define XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG 0x1C8 +#define XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG 0x1CC +#define XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG 0x1D0 +#define XGMAC_MAC_RX_ERR_MSG_CNT_REG 0x1D4 +#define XGMAC_MAC_RX_ERR_EFD_CNT_REG 0x1D8 +#define XGMAC_MAC_ERR_INFO_REG 0x1DC +#define XGMAC_MAC_DBG_INFO_REG 0x1E0 + +#define XGMAC_PCS_BASER_SYNC_THD_REG 0x330 +#define XGMAC_PCS_STATUS1_REG 0x404 +#define XGMAC_PCS_BASER_STATUS1_REG 0x410 +#define XGMAC_PCS_BASER_STATUS2_REG 0x414 +#define XGMAC_PCS_BASER_SEEDA_0_REG 0x420 +#define XGMAC_PCS_BASER_SEEDA_1_REG 0x424 +#define XGMAC_PCS_BASER_SEEDB_0_REG 0x428 +#define XGMAC_PCS_BASER_SEEDB_1_REG 0x42C +#define XGMAC_PCS_BASER_TEST_CONTROL_REG 0x430 +#define XGMAC_PCS_BASER_TEST_ERR_CNT_REG 0x434 +#define XGMAC_PCS_DBG_INFO_REG 0x4C0 +#define XGMAC_PCS_DBG_INFO1_REG 0x4C4 +#define XGMAC_PCS_DBG_INFO2_REG 0x4C8 +#define XGMAC_PCS_DBG_INFO3_REG 0x4CC + +#define XGMAC_PMA_ENABLE_REG 0x700 +#define XGMAC_PMA_CONTROL_REG 0x704 +#define XGMAC_PMA_SIGNAL_STATUS_REG 0x708 +#define XGMAC_PMA_DBG_INFO_REG 0x70C +#define XGMAC_PMA_FEC_ABILITY_REG 0x740 +#define XGMAC_PMA_FEC_CONTROL_REG 0x744 +#define XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG 0x750 +#define XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG 0x760 + +#define XGMAC_TX_PKTS_FRAGMENT 0x0000 +#define XGMAC_TX_PKTS_UNDERSIZE 0x0008 +#define XGMAC_TX_PKTS_UNDERMIN 0x0010 +#define XGMAC_TX_PKTS_64OCTETS 0x0018 +#define XGMAC_TX_PKTS_65TO127OCTETS 0x0020 +#define XGMAC_TX_PKTS_128TO255OCTETS 0x0028 +#define XGMAC_TX_PKTS_256TO511OCTETS 0x0030 +#define XGMAC_TX_PKTS_512TO1023OCTETS 0x0038 +#define XGMAC_TX_PKTS_1024TO1518OCTETS 0x0040 +#define XGMAC_TX_PKTS_1519TOMAXOCTETS 0x0048 +#define XGMAC_TX_PKTS_1519TOMAXOCTETSOK 0x0050 +#define XGMAC_TX_PKTS_OVERSIZE 0x0058 +#define XGMAC_TX_PKTS_JABBER 0x0060 +#define XGMAC_TX_GOODPKTS 0x0068 +#define XGMAC_TX_GOODOCTETS 0x0070 +#define XGMAC_TX_TOTAL_PKTS 0x0078 +#define XGMAC_TX_TOTALOCTETS 0x0080 +#define XGMAC_TX_UNICASTPKTS 0x0088 +#define XGMAC_TX_MULTICASTPKTS 0x0090 +#define XGMAC_TX_BROADCASTPKTS 0x0098 +#define XGMAC_TX_PRI0PAUSEPKTS 0x00a0 +#define XGMAC_TX_PRI1PAUSEPKTS 0x00a8 +#define XGMAC_TX_PRI2PAUSEPKTS 0x00b0 +#define XGMAC_TX_PRI3PAUSEPKTS 0x00b8 +#define XGMAC_TX_PRI4PAUSEPKTS 0x00c0 +#define XGMAC_TX_PRI5PAUSEPKTS 0x00c8 +#define XGMAC_TX_PRI6PAUSEPKTS 0x00d0 +#define XGMAC_TX_PRI7PAUSEPKTS 0x00d8 +#define XGMAC_TX_MACCTRLPKTS 0x00e0 +#define XGMAC_TX_1731PKTS 0x00e8 +#define XGMAC_TX_1588PKTS 0x00f0 +#define XGMAC_RX_FROMAPPGOODPKTS 0x00f8 +#define XGMAC_RX_FROMAPPBADPKTS 0x0100 +#define XGMAC_TX_ERRALLPKTS 0x0108 + +#define XGMAC_RX_PKTS_FRAGMENT 0x0110 +#define XGMAC_RX_PKTSUNDERSIZE 0x0118 +#define XGMAC_RX_PKTS_UNDERMIN 0x0120 +#define XGMAC_RX_PKTS_64OCTETS 0x0128 +#define XGMAC_RX_PKTS_65TO127OCTETS 0x0130 +#define XGMAC_RX_PKTS_128TO255OCTETS 0x0138 +#define XGMAC_RX_PKTS_256TO511OCTETS 0x0140 +#define XGMAC_RX_PKTS_512TO1023OCTETS 0x0148 +#define XGMAC_RX_PKTS_1024TO1518OCTETS 0x0150 +#define XGMAC_RX_PKTS_1519TOMAXOCTETS 0x0158 +#define XGMAC_RX_PKTS_1519TOMAXOCTETSOK 0x0160 +#define XGMAC_RX_PKTS_OVERSIZE 0x0168 +#define XGMAC_RX_PKTS_JABBER 0x0170 +#define XGMAC_RX_GOODPKTS 0x0178 +#define XGMAC_RX_GOODOCTETS 0x0180 +#define XGMAC_RX_TOTAL_PKTS 0x0188 +#define XGMAC_RX_TOTALOCTETS 0x0190 +#define XGMAC_RX_UNICASTPKTS 0x0198 +#define XGMAC_RX_MULTICASTPKTS 0x01a0 +#define XGMAC_RX_BROADCASTPKTS 0x01a8 +#define XGMAC_RX_PRI0PAUSEPKTS 0x01b0 +#define XGMAC_RX_PRI1PAUSEPKTS 0x01b8 +#define XGMAC_RX_PRI2PAUSEPKTS 0x01c0 +#define XGMAC_RX_PRI3PAUSEPKTS 0x01c8 +#define XGMAC_RX_PRI4PAUSEPKTS 0x01d0 +#define XGMAC_RX_PRI5PAUSEPKTS 0x01d8 +#define XGMAC_RX_PRI6PAUSEPKTS 0x01e0 +#define XGMAC_RX_PRI7PAUSEPKTS 0x01e8 +#define XGMAC_RX_MACCTRLPKTS 0x01f0 +#define XGMAC_TX_SENDAPPGOODPKTS 0x01f8 +#define XGMAC_TX_SENDAPPBADPKTS 0x0200 +#define XGMAC_RX_1731PKTS 0x0208 +#define XGMAC_RX_SYMBOLERRPKTS 0x0210 +#define XGMAC_RX_FCSERRPKTS 0x0218 + +#define XGMAC_TRX_CORE_SRST_M 0x2080 + +#define DSAF_CFG_EN_S 0 +#define DSAF_CFG_TC_MODE_S 1 +#define DSAF_CFG_CRC_EN_S 2 +#define DSAF_CFG_SBM_INIT_S 3 +#define DSAF_CFG_MIX_MODE_S 4 +#define DSAF_CFG_STP_MODE_S 5 +#define DSAF_CFG_LOCA_ADDR_EN_S 6 + +#define DSAF_CNT_CLR_CE_S 0 +#define DSAF_SNAP_EN_S 1 + +#define HNS_DSAF_PFC_UNIT_CNT_FOR_XGE 41 +#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_1000 410 +#define HNS_DSAF_PFC_UNIT_CNT_FOR_GE_2500 103 + +#define DSAF_PFC_UNINT_CNT_M ((1ULL << 9) - 1) +#define DSAF_PFC_UNINT_CNT_S 0 + +#define DSAF_PPE_QID_CFG_M 0xFF +#define DSAF_PPE_QID_CFG_S 0 + +#define DSAF_SW_PORT_TYPE_M 3 +#define DSAF_SW_PORT_TYPE_S 0 + +#define DSAF_STP_PORT_TYPE_M 7 +#define DSAF_STP_PORT_TYPE_S 0 + +#define DSAF_INODE_IN_PORT_NUM_M 7 +#define DSAF_INODE_IN_PORT_NUM_S 0 + +#define HNS_DSAF_I4TC_CFG 0x18688688 +#define HNS_DSAF_I8TC_CFG 0x18FAC688 + +#define DSAF_SBM_CFG_SHCUT_EN_S 0 +#define DSAF_SBM_CFG_EN_S 1 +#define DSAF_SBM_CFG_MIB_EN_S 2 +#define DSAF_SBM_CFG_ECC_INVERT_EN_S 3 + +#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S 0 +#define DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S 10 +#define DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10) +#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S 20 +#define DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M (((1ULL << 11) - 1) << 20) + +#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S 0 +#define DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S 10 +#define DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 10) + +#define DSAF_SBM_CFG2_SET_BUF_NUM_S 0 +#define DSAF_SBM_CFG2_SET_BUF_NUM_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG2_RESET_BUF_NUM_S 10 +#define DSAF_SBM_CFG2_RESET_BUF_NUM_M (((1ULL << 10) - 1) << 10) + +#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S 0 +#define DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 0) +#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 10 +#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 10) + +#define DSAF_TBL_TCAM_ADDR_S 0 +#define DSAF_TBL_TCAM_ADDR_M ((1ULL << 9) - 1) + +#define DSAF_TBL_LINE_ADDR_S 0 +#define DSAF_TBL_LINE_ADDR_M ((1ULL << 15) - 1) + +#define DSAF_TBL_MCAST_CFG4_VM128_112_S 0 +#define DSAF_TBL_MCAST_CFG4_VM128_112_M (((1ULL << 7) - 1) << 0) +#define DSAF_TBL_MCAST_CFG4_ITEM_VLD_S 7 +#define DSAF_TBL_MCAST_CFG4_OLD_EN_S 8 + +#define DSAF_TBL_MCAST_CFG0_XGE5_0_S 0 +#define DSAF_TBL_MCAST_CFG0_XGE5_0_M (((1ULL << 6) - 1) << 0) +#define DSAF_TBL_MCAST_CFG0_VM25_0_S 6 +#define DSAF_TBL_MCAST_CFG0_VM25_0_M (((1ULL << 26) - 1) << 6) + +#define DSAF_TBL_UCAST_CFG1_OUT_PORT_S 0 +#define DSAF_TBL_UCAST_CFG1_OUT_PORT_M (((1ULL << 8) - 1) << 0) +#define DSAF_TBL_UCAST_CFG1_DVC_S 8 +#define DSAF_TBL_UCAST_CFG1_MAC_DISCARD_S 9 +#define DSAF_TBL_UCAST_CFG1_ITEM_VLD_S 10 +#define DSAF_TBL_UCAST_CFG1_OLD_EN_S 11 + +#define DSAF_TBL_LINE_CFG_OUT_PORT_S 0 +#define DSAF_TBL_LINE_CFG_OUT_PORT_M (((1ULL << 8) - 1) << 0) +#define DSAF_TBL_LINE_CFG_DVC_S 8 +#define DSAF_TBL_LINE_CFG_MAC_DISCARD_S 9 + +#define DSAF_TBL_PUL_OLD_RSLT_RE_S 0 +#define DSAF_TBL_PUL_MCAST_VLD_S 1 +#define DSAF_TBL_PUL_TCAM_DATA_VLD_S 2 +#define DSAF_TBL_PUL_UCAST_VLD_S 3 +#define DSAF_TBL_PUL_LINE_VLD_S 4 +#define DSAF_TBL_PUL_TCAM_LOAD_S 5 +#define DSAF_TBL_PUL_LINE_LOAD_S 6 + +#define DSAF_TBL_DFX_LINE_LKUP_NUM_EN_S 0 +#define DSAF_TBL_DFX_UC_LKUP_NUM_EN_S 1 +#define DSAF_TBL_DFX_MC_LKUP_NUM_EN_S 2 +#define DSAF_TBL_DFX_BC_LKUP_NUM_EN_S 3 +#define DSAF_TBL_DFX_RAM_ERR_INJECT_EN_S 4 + +#define DSAF_VOQ_BP_ALL_DOWNTHRD_S 0 +#define DSAF_VOQ_BP_ALL_DOWNTHRD_M (((1ULL << 10) - 1) << 0) +#define DSAF_VOQ_BP_ALL_UPTHRD_S 10 +#define DSAF_VOQ_BP_ALL_UPTHRD_M (((1ULL << 10) - 1) << 10) + +#define DSAF_XGE_GE_WORK_MODE_S 0 +#define DSAF_XGE_GE_LOOPBACK_S 1 + +#define DSAF_FC_XGE_TX_PAUSE_S 0 +#define DSAF_REGS_XGE_CNT_CAR_S 1 + +#define PPE_CFG_QID_MODE_DEF_QID_S 0 +#define PPE_CFG_QID_MODE_DEF_QID_M (0xff << PPE_CFG_QID_MODE_DEF_QID_S) + +#define PPE_CFG_QID_MODE_CF_QID_MODE_S 8 +#define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S) + +#define PPE_CNT_CLR_CE_B 0 +#define PPE_CNT_CLR_SNAP_EN_B 1 + +#define PPE_COMMON_CNT_CLR_CE_B 0 +#define PPE_COMMON_CNT_CLR_SNAP_EN_B 1 + +#define GMAC_DUPLEX_TYPE_B 0 + +#define GMAC_FC_TX_TIMER_S 0 +#define GMAC_FC_TX_TIMER_M 0xffff + +#define GMAC_MAX_FRM_SIZE_S 0 +#define GMAC_MAX_FRM_SIZE_M 0xffff + +#define GMAC_PORT_MODE_S 0 +#define GMAC_PORT_MODE_M 0xf + +#define GMAC_RGMII_1000M_DELAY_B 4 +#define GMAC_MII_TX_EDGE_SEL_B 5 +#define GMAC_FIFO_ERR_AUTO_RST_B 6 +#define GMAC_DBG_CLK_LOS_MSK_B 7 + +#define GMAC_PORT_RX_EN_B 1 +#define GMAC_PORT_TX_EN_B 2 + +#define GMAC_PAUSE_EN_RX_FDFC_B 0 +#define GMAC_PAUSE_EN_TX_FDFC_B 1 +#define GMAC_PAUSE_EN_TX_HDFC_B 2 + +#define GMAC_SHORT_RUNTS_THR_S 0 +#define GMAC_SHORT_RUNTS_THR_M 0x1f + +#define GMAC_AN_NEG_STAT_FD_B 5 +#define GMAC_AN_NEG_STAT_HD_B 6 +#define GMAC_AN_NEG_STAT_RF1_DUPLIEX_B 12 +#define GMAC_AN_NEG_STAT_RF2_B 13 + +#define GMAC_AN_NEG_STAT_NP_LNK_OK_B 15 +#define GMAC_AN_NEG_STAT_RX_SYNC_OK_B 20 +#define GMAC_AN_NEG_STAT_AN_DONE_B 21 + +#define GMAC_AN_NEG_STAT_PS_S 7 +#define GMAC_AN_NEG_STAT_PS_M (0x3 << GMAC_AN_NEG_STAT_PS_S) + +#define GMAC_AN_NEG_STAT_SPEED_S 10 +#define GMAC_AN_NEG_STAT_SPEED_M (0x3 << GMAC_AN_NEG_STAT_SPEED_S) + +#define GMAC_TX_AN_EN_B 5 +#define GMAC_TX_CRC_ADD_B 6 +#define GMAC_TX_PAD_EN_B 7 + +#define GMAC_LINE_LOOPBACK_B 0 + +#define GMAC_LP_REG_CF_EXT_DRV_LP_B 1 +#define GMAC_LP_REG_CF2MI_LP_EN_B 2 + +#define GMAC_MODE_CHANGE_EB_B 0 + +#define GMAC_RECV_CTRL_STRIP_PAD_EN_B 3 +#define GMAC_RECV_CTRL_RUNT_PKT_EN_B 4 + +#define GMAC_TX_LOOP_PKT_HIG_PRI_B 0 +#define GMAC_TX_LOOP_PKT_EN_B 1 + +#define XGMAC_PORT_MODE_TX_S 0x0 +#define XGMAC_PORT_MODE_TX_M (0x3 << XGMAC_PORT_MODE_TX_S) +#define XGMAC_PORT_MODE_TX_40G_B 0x3 +#define XGMAC_PORT_MODE_RX_S 0x4 +#define XGMAC_PORT_MODE_RX_M (0x3 << XGMAC_PORT_MODE_RX_S) +#define XGMAC_PORT_MODE_RX_40G_B 0x7 + +#define XGMAC_ENABLE_TX_B 0 +#define XGMAC_ENABLE_RX_B 1 + +#define XGMAC_CTL_TX_FCS_B 0 +#define XGMAC_CTL_TX_PAD_B 1 +#define XGMAC_CTL_TX_PREAMBLE_TRANS_B 3 +#define XGMAC_CTL_TX_UNDER_MIN_ERR_B 4 +#define XGMAC_CTL_TX_TRUNCATE_B 5 +#define XGMAC_CTL_TX_1588_B 8 +#define XGMAC_CTL_TX_1731_B 9 +#define XGMAC_CTL_TX_PFC_B 10 +#define XGMAC_CTL_RX_FCS_B 16 +#define XGMAC_CTL_RX_FCS_STRIP_B 17 +#define XGMAC_CTL_RX_PREAMBLE_TRANS_B 19 +#define XGMAC_CTL_RX_UNDER_MIN_ERR_B 20 +#define XGMAC_CTL_RX_TRUNCATE_B 21 +#define XGMAC_CTL_RX_1588_B 24 +#define XGMAC_CTL_RX_1731_B 25 +#define XGMAC_CTL_RX_PFC_B 26 + +#define XGMAC_PMA_FEC_CTL_TX_B 0 +#define XGMAC_PMA_FEC_CTL_RX_B 1 +#define XGMAC_PMA_FEC_CTL_ERR_EN 2 +#define XGMAC_PMA_FEC_CTL_ERR_SH 3 + +#define XGMAC_PAUSE_CTL_TX_B 0 +#define XGMAC_PAUSE_CTL_RX_B 1 +#define XGMAC_PAUSE_CTL_RSP_MODE_B 2 +#define XGMAC_PAUSE_CTL_TX_XOFF_B 3 + +static inline void dsaf_write_reg(void *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = ACCESS_ONCE(base); + + writel(value, reg_addr + reg); +} + +#define dsaf_write_dev(a, reg, value) \ + dsaf_write_reg((a)->io_base, (reg), (value)) + +static inline u32 dsaf_read_reg(u8 *base, u32 reg) +{ + u8 __iomem *reg_addr = ACCESS_ONCE(base); + + return readl(reg_addr + reg); +} + +#define dsaf_read_dev(a, reg) \ + dsaf_read_reg((a)->io_base, (reg)) + +#define dsaf_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= (((val) << (shift)) & (mask)); \ + } while (0) + +#define dsaf_set_bit(origin, shift, val) \ + dsaf_set_field((origin), (1ull << (shift)), (shift), (val)) + +static inline void dsaf_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, + u32 val) +{ + u32 origin = dsaf_read_reg(base, reg); + + dsaf_set_field(origin, mask, shift, val); + dsaf_write_reg(base, reg, origin); +} + +#define dsaf_set_dev_field(dev, reg, mask, shift, val) \ + dsaf_set_reg_field((dev)->io_base, (reg), (mask), (shift), (val)) + +#define dsaf_set_dev_bit(dev, reg, bit, val) \ + dsaf_set_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit), (val)) + +#define dsaf_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define dsaf_get_bit(origin, shift) \ + dsaf_get_field((origin), (1ull << (shift)), (shift)) + +static inline u32 dsaf_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) +{ + u32 origin; + + origin = dsaf_read_reg(base, reg); + return dsaf_get_field(origin, mask, shift); +} + +#define dsaf_get_dev_field(dev, reg, mask, shift) \ + dsaf_get_reg_field((dev)->io_base, (reg), (mask), (shift)) + +#define dsaf_get_dev_bit(dev, reg, bit) \ + dsaf_get_reg_field((dev)->io_base, (reg), (1ull << (bit)), (bit)) + +#define dsaf_write_b(addr, data)\ + writeb((data), (__iomem unsigned char *)(addr)) +#define dsaf_read_b(addr)\ + readb((__iomem unsigned char *)(addr)) + +#define hns_mac_reg_read64(drv, offset) \ + readq((__iomem void *)(((u8 *)(drv)->io_base + 0xc00 + (offset)))) + +#endif /* _DSAF_REG_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c new file mode 100644 index 000000000000..802d55457f19 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c @@ -0,0 +1,837 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <asm-generic/io-64-nonatomic-hi-lo.h> +#include <linux/of_mdio.h> +#include "hns_dsaf_main.h" +#include "hns_dsaf_mac.h" +#include "hns_dsaf_xgmac.h" +#include "hns_dsaf_reg.h" + +static const struct mac_stats_string g_xgmac_stats_string[] = { + {"xgmac_tx_bad_pkts_minto64", MAC_STATS_FIELD_OFF(tx_fragment_err)}, + {"xgmac_tx_good_pkts_minto64", MAC_STATS_FIELD_OFF(tx_undersize)}, + {"xgmac_tx_total_pkts_minto64", MAC_STATS_FIELD_OFF(tx_under_min_pkts)}, + {"xgmac_tx_pkts_64", MAC_STATS_FIELD_OFF(tx_64bytes)}, + {"xgmac_tx_pkts_65to127", MAC_STATS_FIELD_OFF(tx_65to127)}, + {"xgmac_tx_pkts_128to255", MAC_STATS_FIELD_OFF(tx_128to255)}, + {"xgmac_tx_pkts_256to511", MAC_STATS_FIELD_OFF(tx_256to511)}, + {"xgmac_tx_pkts_512to1023", MAC_STATS_FIELD_OFF(tx_512to1023)}, + {"xgmac_tx_pkts_1024to1518", MAC_STATS_FIELD_OFF(tx_1024to1518)}, + {"xgmac_tx_pkts_1519tomax", MAC_STATS_FIELD_OFF(tx_1519tomax)}, + {"xgmac_tx_good_pkts_1519tomax", + MAC_STATS_FIELD_OFF(tx_1519tomax_good)}, + {"xgmac_tx_good_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_oversize)}, + {"xgmac_tx_bad_pkts_untralmax", MAC_STATS_FIELD_OFF(tx_jabber_err)}, + {"xgmac_tx_good_pkts_all", MAC_STATS_FIELD_OFF(tx_good_pkts)}, + {"xgmac_tx_good_byte_all", MAC_STATS_FIELD_OFF(tx_good_bytes)}, + {"xgmac_tx_total_pkt", MAC_STATS_FIELD_OFF(tx_total_pkts)}, + {"xgmac_tx_total_byt", MAC_STATS_FIELD_OFF(tx_total_bytes)}, + {"xgmac_tx_uc_pkt", MAC_STATS_FIELD_OFF(tx_uc_pkts)}, + {"xgmac_tx_mc_pkt", MAC_STATS_FIELD_OFF(tx_mc_pkts)}, + {"xgmac_tx_bc_pkt", MAC_STATS_FIELD_OFF(tx_bc_pkts)}, + {"xgmac_tx_pause_frame_num", MAC_STATS_FIELD_OFF(tx_pfc_tc0)}, + {"xgmac_tx_pfc_per_1pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc1)}, + {"xgmac_tx_pfc_per_2pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc2)}, + {"xgmac_tx_pfc_per_3pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc3)}, + {"xgmac_tx_pfc_per_4pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc4)}, + {"xgmac_tx_pfc_per_5pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc5)}, + {"xgmac_tx_pfc_per_6pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc6)}, + {"xgmac_tx_pfc_per_7pause_framer", MAC_STATS_FIELD_OFF(tx_pfc_tc7)}, + {"xgmac_tx_mac_ctrol_frame", MAC_STATS_FIELD_OFF(tx_ctrl)}, + {"xgmac_tx_1731_pkts", MAC_STATS_FIELD_OFF(tx_1731_pkts)}, + {"xgmac_tx_1588_pkts", MAC_STATS_FIELD_OFF(tx_1588_pkts)}, + {"xgmac_rx_good_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_good_from_sw)}, + {"xgmac_rx_bad_pkt_from_dsaf", MAC_STATS_FIELD_OFF(rx_bad_from_sw)}, + {"xgmac_tx_bad_pkt_64tomax", MAC_STATS_FIELD_OFF(tx_bad_pkts)}, + + {"xgmac_rx_bad_pkts_minto64", MAC_STATS_FIELD_OFF(rx_fragment_err)}, + {"xgmac_rx_good_pkts_minto64", MAC_STATS_FIELD_OFF(rx_undersize)}, + {"xgmac_rx_total_pkts_minto64", MAC_STATS_FIELD_OFF(rx_under_min)}, + {"xgmac_rx_pkt_64", MAC_STATS_FIELD_OFF(rx_64bytes)}, + {"xgmac_rx_pkt_65to127", MAC_STATS_FIELD_OFF(rx_65to127)}, + {"xgmac_rx_pkt_128to255", MAC_STATS_FIELD_OFF(rx_128to255)}, + {"xgmac_rx_pkt_256to511", MAC_STATS_FIELD_OFF(rx_256to511)}, + {"xgmac_rx_pkt_512to1023", MAC_STATS_FIELD_OFF(rx_512to1023)}, + {"xgmac_rx_pkt_1024to1518", MAC_STATS_FIELD_OFF(rx_1024to1518)}, + {"xgmac_rx_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax)}, + {"xgmac_rx_good_pkt_1519tomax", MAC_STATS_FIELD_OFF(rx_1519tomax_good)}, + {"xgmac_rx_good_pkt_untramax", MAC_STATS_FIELD_OFF(rx_oversize)}, + {"xgmac_rx_bad_pkt_untramax", MAC_STATS_FIELD_OFF(rx_jabber_err)}, + {"xgmac_rx_good_pkt", MAC_STATS_FIELD_OFF(rx_good_pkts)}, + {"xgmac_rx_good_byt", MAC_STATS_FIELD_OFF(rx_good_bytes)}, + {"xgmac_rx_pkt", MAC_STATS_FIELD_OFF(rx_total_pkts)}, + {"xgmac_rx_byt", MAC_STATS_FIELD_OFF(rx_total_bytes)}, + {"xgmac_rx_uc_pkt", MAC_STATS_FIELD_OFF(rx_uc_pkts)}, + {"xgmac_rx_mc_pkt", MAC_STATS_FIELD_OFF(rx_mc_pkts)}, + {"xgmac_rx_bc_pkt", MAC_STATS_FIELD_OFF(rx_bc_pkts)}, + {"xgmac_rx_pause_frame_num", MAC_STATS_FIELD_OFF(rx_pfc_tc0)}, + {"xgmac_rx_pfc_per_1pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc1)}, + {"xgmac_rx_pfc_per_2pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc2)}, + {"xgmac_rx_pfc_per_3pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc3)}, + {"xgmac_rx_pfc_per_4pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc4)}, + {"xgmac_rx_pfc_per_5pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc5)}, + {"xgmac_rx_pfc_per_6pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc6)}, + {"xgmac_rx_pfc_per_7pause_frame", MAC_STATS_FIELD_OFF(rx_pfc_tc7)}, + {"xgmac_rx_mac_control", MAC_STATS_FIELD_OFF(rx_unknown_ctrl)}, + {"xgmac_tx_good_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_good_to_sw)}, + {"xgmac_tx_bad_pkt_todsaf", MAC_STATS_FIELD_OFF(tx_bad_to_sw)}, + {"xgmac_rx_1731_pkt", MAC_STATS_FIELD_OFF(rx_1731_pkts)}, + {"xgmac_rx_symbol_err_pkt", MAC_STATS_FIELD_OFF(rx_symbol_err)}, + {"xgmac_rx_fcs_pkt", MAC_STATS_FIELD_OFF(rx_fcs_err)} +}; + +/** + *hns_xgmac_tx_enable - xgmac port tx enable + *@drv: mac driver + *@value: value of enable + */ +static void hns_xgmac_tx_enable(struct mac_driver *drv, u32 value) +{ + dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_TX_B, !!value); +} + +/** + *hns_xgmac_rx_enable - xgmac port rx enable + *@drv: mac driver + *@value: value of enable + */ +static void hns_xgmac_rx_enable(struct mac_driver *drv, u32 value) +{ + dsaf_set_dev_bit(drv, XGMAC_MAC_ENABLE_REG, XGMAC_ENABLE_RX_B, !!value); +} + +/** + *hns_xgmac_enable - enable xgmac port + *@drv: mac driver + *@mode: mode of mac port + */ +static void hns_xgmac_enable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 1); + mdelay(10); + + /*enable XGE rX/tX */ + if (mode == MAC_COMM_MODE_TX) { + hns_xgmac_tx_enable(drv, 1); + } else if (mode == MAC_COMM_MODE_RX) { + hns_xgmac_rx_enable(drv, 1); + } else if (mode == MAC_COMM_MODE_RX_AND_TX) { + hns_xgmac_tx_enable(drv, 1); + hns_xgmac_rx_enable(drv, 1); + } else { + dev_err(drv->dev, "error mac mode:%d\n", mode); + } +} + +/** + *hns_xgmac_disable - disable xgmac port + *@mac_drv: mac driver + *@mode: mode of mac port + */ +static void hns_xgmac_disable(void *mac_drv, enum mac_commom_mode mode) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + if (mode == MAC_COMM_MODE_TX) { + hns_xgmac_tx_enable(drv, 0); + } else if (mode == MAC_COMM_MODE_RX) { + hns_xgmac_rx_enable(drv, 0); + } else if (mode == MAC_COMM_MODE_RX_AND_TX) { + hns_xgmac_tx_enable(drv, 0); + hns_xgmac_rx_enable(drv, 0); + } + + mdelay(10); + hns_dsaf_xge_core_srst_by_port(dsaf_dev, port, 0); +} + +/** + *hns_xgmac_pma_fec_enable - xgmac PMA FEC enable + *@drv: mac driver + *@tx_value: tx value + *@rx_value: rx value + *return status + */ +static void hns_xgmac_pma_fec_enable(struct mac_driver *drv, u32 tx_value, + u32 rx_value) +{ + u32 origin = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG); + + dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_TX_B, !!tx_value); + dsaf_set_bit(origin, XGMAC_PMA_FEC_CTL_RX_B, !!rx_value); + dsaf_write_dev(drv, XGMAC_PMA_FEC_CONTROL_REG, origin); +} + +/* clr exc irq for xge*/ +static void hns_xgmac_exc_irq_en(struct mac_driver *drv, u32 en) +{ + u32 clr_vlue = 0xfffffffful; + u32 msk_vlue = en ? 0xfffffffful : 0; /*1 is en, 0 is dis*/ + + dsaf_write_dev(drv, XGMAC_INT_STATUS_REG, clr_vlue); + dsaf_write_dev(drv, XGMAC_INT_ENABLE_REG, msk_vlue); +} + +/** + *hns_xgmac_init - initialize XGE + *@mac_drv: mac driver + */ +static void hns_xgmac_init(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + u32 port = drv->mac_id; + + hns_dsaf_xge_srst_by_port(dsaf_dev, port, 0); + mdelay(100); + hns_dsaf_xge_srst_by_port(dsaf_dev, port, 1); + + mdelay(100); + hns_xgmac_exc_irq_en(drv, 0); + + hns_xgmac_pma_fec_enable(drv, 0x0, 0x0); + + hns_xgmac_disable(mac_drv, MAC_COMM_MODE_RX_AND_TX); +} + +/** + *hns_xgmac_config_pad_and_crc - set xgmac pad and crc enable the same time + *@mac_drv: mac driver + *@newval:enable of pad and crc + */ +static void hns_xgmac_config_pad_and_crc(void *mac_drv, u8 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 origin = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + + dsaf_set_bit(origin, XGMAC_CTL_TX_PAD_B, !!newval); + dsaf_set_bit(origin, XGMAC_CTL_TX_FCS_B, !!newval); + dsaf_set_bit(origin, XGMAC_CTL_RX_FCS_B, !!newval); + dsaf_write_dev(drv, XGMAC_MAC_CONTROL_REG, origin); +} + +/** + *hns_xgmac_pausefrm_cfg - set pause param about xgmac + *@mac_drv: mac driver + *@newval:enable of pad and crc + */ +static void hns_xgmac_pausefrm_cfg(void *mac_drv, u32 rx_en, u32 tx_en) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 origin = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + + dsaf_set_bit(origin, XGMAC_PAUSE_CTL_TX_B, !!tx_en); + dsaf_set_bit(origin, XGMAC_PAUSE_CTL_RX_B, !!rx_en); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG, origin); +} + +static void hns_xgmac_set_pausefrm_mac_addr(void *mac_drv, char *mac_addr) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + u32 high_val = mac_addr[1] | (mac_addr[0] << 8); + u32 low_val = mac_addr[5] | (mac_addr[4] << 8) + | (mac_addr[3] << 16) | (mac_addr[2] << 24); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG, low_val); + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG, high_val); +} + +/** + *hns_xgmac_set_rx_ignore_pause_frames - set rx pause param about xgmac + *@mac_drv: mac driver + *@enable:enable rx pause param + */ +static void hns_xgmac_set_rx_ignore_pause_frames(void *mac_drv, u32 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG, + XGMAC_PAUSE_CTL_RX_B, !!enable); +} + +/** + *hns_xgmac_set_tx_auto_pause_frames - set tx pause param about xgmac + *@mac_drv: mac driver + *@enable:enable tx pause param + */ +static void hns_xgmac_set_tx_auto_pause_frames(void *mac_drv, u16 enable) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_set_dev_bit(drv, XGMAC_MAC_PAUSE_CTRL_REG, + XGMAC_PAUSE_CTL_TX_B, !!enable); + + /*if enable is not zero ,set tx pause time */ + if (enable) + dsaf_write_dev(drv, XGMAC_MAC_PAUSE_TIME_REG, enable); +} + +/** + *hns_xgmac_get_id - get xgmac port id + *@mac_drv: mac driver + *@newval:xgmac max frame length + */ +static void hns_xgmac_get_id(void *mac_drv, u8 *mac_id) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *mac_id = drv->mac_id; +} + +/** + *hns_xgmac_config_max_frame_length - set xgmac max frame length + *@mac_drv: mac driver + *@newval:xgmac max frame length + */ +static void hns_xgmac_config_max_frame_length(void *mac_drv, u16 newval) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + dsaf_write_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG, newval); +} + +void hns_xgmac_update_stats(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = &drv->mac_cb->hw_stats; + + /* TX */ + hw_stats->tx_fragment_err + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT); + hw_stats->tx_undersize + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE); + hw_stats->tx_under_min_pkts + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN); + hw_stats->tx_64bytes = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS); + hw_stats->tx_65to127 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS); + hw_stats->tx_128to255 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS); + hw_stats->tx_256to511 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS); + hw_stats->tx_512to1023 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS); + hw_stats->tx_1024to1518 + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS); + hw_stats->tx_1519tomax + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS); + hw_stats->tx_1519tomax_good + = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK); + hw_stats->tx_oversize = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE); + hw_stats->tx_jabber_err = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER); + hw_stats->tx_good_pkts = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS); + hw_stats->tx_good_bytes = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS); + hw_stats->tx_total_pkts = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS); + hw_stats->tx_total_bytes + = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS); + hw_stats->tx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS); + hw_stats->tx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS); + hw_stats->tx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS); + hw_stats->tx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS); + hw_stats->tx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS); + hw_stats->tx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS); + hw_stats->tx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS); + hw_stats->tx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS); + hw_stats->tx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS); + hw_stats->tx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS); + hw_stats->tx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS); + hw_stats->tx_ctrl = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS); + hw_stats->tx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS); + hw_stats->tx_1588_pkts = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS); + hw_stats->rx_good_from_sw + = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS); + hw_stats->rx_bad_from_sw + = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS); + hw_stats->tx_bad_pkts = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS); + + /* RX */ + hw_stats->rx_fragment_err + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT); + hw_stats->rx_undersize + = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE); + hw_stats->rx_under_min + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN); + hw_stats->rx_64bytes = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS); + hw_stats->rx_65to127 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS); + hw_stats->rx_128to255 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS); + hw_stats->rx_256to511 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS); + hw_stats->rx_512to1023 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS); + hw_stats->rx_1024to1518 + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS); + hw_stats->rx_1519tomax + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS); + hw_stats->rx_1519tomax_good + = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK); + hw_stats->rx_oversize = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE); + hw_stats->rx_jabber_err = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER); + hw_stats->rx_good_pkts = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS); + hw_stats->rx_good_bytes = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS); + hw_stats->rx_total_pkts = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS); + hw_stats->rx_total_bytes + = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS); + hw_stats->rx_uc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS); + hw_stats->rx_mc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS); + hw_stats->rx_bc_pkts = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS); + hw_stats->rx_pfc_tc0 = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS); + hw_stats->rx_pfc_tc1 = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS); + hw_stats->rx_pfc_tc2 = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS); + hw_stats->rx_pfc_tc3 = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS); + hw_stats->rx_pfc_tc4 = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS); + hw_stats->rx_pfc_tc5 = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS); + hw_stats->rx_pfc_tc6 = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS); + hw_stats->rx_pfc_tc7 = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS); + + hw_stats->rx_unknown_ctrl + = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS); + hw_stats->tx_good_to_sw + = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS); + hw_stats->tx_bad_to_sw + = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS); + hw_stats->rx_1731_pkts = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS); + hw_stats->rx_symbol_err + = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS); + hw_stats->rx_fcs_err = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS); +} + +/** + *hns_xgmac_free - free xgmac driver + *@mac_drv: mac driver + */ +static void hns_xgmac_free(void *mac_drv) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct dsaf_device *dsaf_dev + = (struct dsaf_device *)dev_get_drvdata(drv->dev); + + u32 mac_id = drv->mac_id; + + hns_dsaf_xge_srst_by_port(dsaf_dev, mac_id, 0); +} + +/** + *hns_xgmac_get_info - get xgmac information + *@mac_drv: mac driver + *@mac_info:mac information + */ +static void hns_xgmac_get_info(void *mac_drv, struct mac_info *mac_info) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 pause_time, pause_ctrl, port_mode, ctrl_val; + + ctrl_val = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + mac_info->pad_and_crc_en = dsaf_get_bit(ctrl_val, XGMAC_CTL_TX_PAD_B); + mac_info->auto_neg = 0; + + pause_time = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG); + mac_info->tx_pause_time = pause_time; + + port_mode = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG); + mac_info->port_en = dsaf_get_field(port_mode, XGMAC_PORT_MODE_TX_M, + XGMAC_PORT_MODE_TX_S) && + dsaf_get_field(port_mode, XGMAC_PORT_MODE_RX_M, + XGMAC_PORT_MODE_RX_S); + mac_info->duplex = 1; + mac_info->speed = MAC_SPEED_10000; + + pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + mac_info->rx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B); + mac_info->tx_pause_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B); +} + +/** + *hns_xgmac_get_pausefrm_cfg - get xgmac pause param + *@mac_drv: mac driver + *@rx_en:xgmac rx pause enable + *@tx_en:xgmac tx pause enable + */ +static void hns_xgmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_en, u32 *tx_en) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 pause_ctrl; + + pause_ctrl = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + *rx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_RX_B); + *tx_en = dsaf_get_bit(pause_ctrl, XGMAC_PAUSE_CTL_TX_B); +} + +/** + *hns_xgmac_get_link_status - get xgmac link status + *@mac_drv: mac driver + *@link_stat: xgmac link stat + */ +static void hns_xgmac_get_link_status(void *mac_drv, u32 *link_stat) +{ + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + *link_stat = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG); +} + +/** + *hns_xgmac_get_regs - dump xgmac regs + *@mac_drv: mac driver + *@cmd:ethtool cmd + *@data:data for value of regs + */ +static void hns_xgmac_get_regs(void *mac_drv, void *data) +{ + u32 i = 0; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + u32 *regs = data; + u64 qtmp; + + /* base config registers */ + regs[0] = dsaf_read_dev(drv, XGMAC_INT_STATUS_REG); + regs[1] = dsaf_read_dev(drv, XGMAC_INT_ENABLE_REG); + regs[2] = dsaf_read_dev(drv, XGMAC_INT_SET_REG); + regs[3] = dsaf_read_dev(drv, XGMAC_IERR_U_INFO_REG); + regs[4] = dsaf_read_dev(drv, XGMAC_OVF_INFO_REG); + regs[5] = dsaf_read_dev(drv, XGMAC_OVF_CNT_REG); + regs[6] = dsaf_read_dev(drv, XGMAC_PORT_MODE_REG); + regs[7] = dsaf_read_dev(drv, XGMAC_CLK_ENABLE_REG); + regs[8] = dsaf_read_dev(drv, XGMAC_RESET_REG); + regs[9] = dsaf_read_dev(drv, XGMAC_LINK_CONTROL_REG); + regs[10] = dsaf_read_dev(drv, XGMAC_LINK_STATUS_REG); + + regs[11] = dsaf_read_dev(drv, XGMAC_SPARE_REG); + regs[12] = dsaf_read_dev(drv, XGMAC_SPARE_CNT_REG); + regs[13] = dsaf_read_dev(drv, XGMAC_MAC_ENABLE_REG); + regs[14] = dsaf_read_dev(drv, XGMAC_MAC_CONTROL_REG); + regs[15] = dsaf_read_dev(drv, XGMAC_MAC_IPG_REG); + regs[16] = dsaf_read_dev(drv, XGMAC_MAC_MSG_CRC_EN_REG); + regs[17] = dsaf_read_dev(drv, XGMAC_MAC_MSG_IMG_REG); + regs[18] = dsaf_read_dev(drv, XGMAC_MAC_MSG_FC_CFG_REG); + regs[19] = dsaf_read_dev(drv, XGMAC_MAC_MSG_TC_CFG_REG); + regs[20] = dsaf_read_dev(drv, XGMAC_MAC_PAD_SIZE_REG); + regs[21] = dsaf_read_dev(drv, XGMAC_MAC_MIN_PKT_SIZE_REG); + regs[22] = dsaf_read_dev(drv, XGMAC_MAC_MAX_PKT_SIZE_REG); + regs[23] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_CTRL_REG); + regs[24] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_TIME_REG); + regs[25] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_GAP_REG); + regs[26] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_H_REG); + regs[27] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_LOCAL_MAC_L_REG); + regs[28] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_H_REG); + regs[29] = dsaf_read_dev(drv, XGMAC_MAC_PAUSE_PEER_MAC_L_REG); + regs[30] = dsaf_read_dev(drv, XGMAC_MAC_PFC_PRI_EN_REG); + regs[31] = dsaf_read_dev(drv, XGMAC_MAC_1588_CTRL_REG); + regs[32] = dsaf_read_dev(drv, XGMAC_MAC_1588_TX_PORT_DLY_REG); + regs[33] = dsaf_read_dev(drv, XGMAC_MAC_1588_RX_PORT_DLY_REG); + regs[34] = dsaf_read_dev(drv, XGMAC_MAC_1588_ASYM_DLY_REG); + regs[35] = dsaf_read_dev(drv, XGMAC_MAC_1588_ADJUST_CFG_REG); + + regs[36] = dsaf_read_dev(drv, XGMAC_MAC_Y1731_ETH_TYPE_REG); + regs[37] = dsaf_read_dev(drv, XGMAC_MAC_MIB_CONTROL_REG); + regs[38] = dsaf_read_dev(drv, XGMAC_MAC_WAN_RATE_ADJUST_REG); + regs[39] = dsaf_read_dev(drv, XGMAC_MAC_TX_ERR_MARK_REG); + regs[40] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_CONTROL_REG); + regs[41] = dsaf_read_dev(drv, XGMAC_MAC_RX_LF_RF_STATUS_REG); + regs[42] = dsaf_read_dev(drv, XGMAC_MAC_TX_RUNT_PKT_CNT_REG); + regs[43] = dsaf_read_dev(drv, XGMAC_MAC_RX_RUNT_PKT_CNT_REG); + regs[44] = dsaf_read_dev(drv, XGMAC_MAC_RX_PREAM_ERR_PKT_CNT_REG); + regs[45] = dsaf_read_dev(drv, XGMAC_MAC_TX_LF_RF_TERM_PKT_CNT_REG); + regs[46] = dsaf_read_dev(drv, XGMAC_MAC_TX_SN_MISMATCH_PKT_CNT_REG); + regs[47] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_MSG_CNT_REG); + regs[48] = dsaf_read_dev(drv, XGMAC_MAC_RX_ERR_EFD_CNT_REG); + regs[49] = dsaf_read_dev(drv, XGMAC_MAC_ERR_INFO_REG); + regs[50] = dsaf_read_dev(drv, XGMAC_MAC_DBG_INFO_REG); + + regs[51] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SYNC_THD_REG); + regs[52] = dsaf_read_dev(drv, XGMAC_PCS_STATUS1_REG); + regs[53] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS1_REG); + regs[54] = dsaf_read_dev(drv, XGMAC_PCS_BASER_STATUS2_REG); + regs[55] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_0_REG); + regs[56] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDA_1_REG); + regs[57] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_0_REG); + regs[58] = dsaf_read_dev(drv, XGMAC_PCS_BASER_SEEDB_1_REG); + regs[59] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_CONTROL_REG); + regs[60] = dsaf_read_dev(drv, XGMAC_PCS_BASER_TEST_ERR_CNT_REG); + regs[61] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO_REG); + regs[62] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO1_REG); + regs[63] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO2_REG); + regs[64] = dsaf_read_dev(drv, XGMAC_PCS_DBG_INFO3_REG); + + regs[65] = dsaf_read_dev(drv, XGMAC_PMA_ENABLE_REG); + regs[66] = dsaf_read_dev(drv, XGMAC_PMA_CONTROL_REG); + regs[67] = dsaf_read_dev(drv, XGMAC_PMA_SIGNAL_STATUS_REG); + regs[68] = dsaf_read_dev(drv, XGMAC_PMA_DBG_INFO_REG); + regs[69] = dsaf_read_dev(drv, XGMAC_PMA_FEC_ABILITY_REG); + regs[70] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CONTROL_REG); + regs[71] = dsaf_read_dev(drv, XGMAC_PMA_FEC_CORR_BLOCK_CNT__REG); + regs[72] = dsaf_read_dev(drv, XGMAC_PMA_FEC_UNCORR_BLOCK_CNT__REG); + + /* status registers */ +#define hns_xgmac_cpy_q(p, q) \ + do {\ + *(p) = (u32)(q);\ + *((p) + 1) = (u32)((q) >> 32);\ + } while (0) + + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_FRAGMENT); + hns_xgmac_cpy_q(®s[73], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERSIZE); + hns_xgmac_cpy_q(®s[75], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_UNDERMIN); + hns_xgmac_cpy_q(®s[77], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_64OCTETS); + hns_xgmac_cpy_q(®s[79], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_65TO127OCTETS); + hns_xgmac_cpy_q(®s[81], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_128TO255OCTETS); + hns_xgmac_cpy_q(®s[83], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_256TO511OCTETS); + hns_xgmac_cpy_q(®s[85], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_512TO1023OCTETS); + hns_xgmac_cpy_q(®s[87], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1024TO1518OCTETS); + hns_xgmac_cpy_q(®s[89], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETS); + hns_xgmac_cpy_q(®s[91], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_1519TOMAXOCTETSOK); + hns_xgmac_cpy_q(®s[93], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_OVERSIZE); + hns_xgmac_cpy_q(®s[95], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PKTS_JABBER); + hns_xgmac_cpy_q(®s[97], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODPKTS); + hns_xgmac_cpy_q(®s[99], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_GOODOCTETS); + hns_xgmac_cpy_q(®s[101], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTAL_PKTS); + hns_xgmac_cpy_q(®s[103], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_TOTALOCTETS); + hns_xgmac_cpy_q(®s[105], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_UNICASTPKTS); + hns_xgmac_cpy_q(®s[107], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MULTICASTPKTS); + hns_xgmac_cpy_q(®s[109], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_BROADCASTPKTS); + hns_xgmac_cpy_q(®s[111], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI0PAUSEPKTS); + hns_xgmac_cpy_q(®s[113], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI1PAUSEPKTS); + hns_xgmac_cpy_q(®s[115], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI2PAUSEPKTS); + hns_xgmac_cpy_q(®s[117], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI3PAUSEPKTS); + hns_xgmac_cpy_q(®s[119], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI4PAUSEPKTS); + hns_xgmac_cpy_q(®s[121], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI5PAUSEPKTS); + hns_xgmac_cpy_q(®s[123], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI6PAUSEPKTS); + hns_xgmac_cpy_q(®s[125], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_PRI7PAUSEPKTS); + hns_xgmac_cpy_q(®s[127], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_MACCTRLPKTS); + hns_xgmac_cpy_q(®s[129], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1731PKTS); + hns_xgmac_cpy_q(®s[131], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_1588PKTS); + hns_xgmac_cpy_q(®s[133], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPGOODPKTS); + hns_xgmac_cpy_q(®s[135], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FROMAPPBADPKTS); + hns_xgmac_cpy_q(®s[137], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_ERRALLPKTS); + hns_xgmac_cpy_q(®s[139], qtmp); + + /* RX */ + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_FRAGMENT); + hns_xgmac_cpy_q(®s[141], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTSUNDERSIZE); + hns_xgmac_cpy_q(®s[143], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_UNDERMIN); + hns_xgmac_cpy_q(®s[145], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_64OCTETS); + hns_xgmac_cpy_q(®s[147], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_65TO127OCTETS); + hns_xgmac_cpy_q(®s[149], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_128TO255OCTETS); + hns_xgmac_cpy_q(®s[151], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_256TO511OCTETS); + hns_xgmac_cpy_q(®s[153], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_512TO1023OCTETS); + hns_xgmac_cpy_q(®s[155], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1024TO1518OCTETS); + hns_xgmac_cpy_q(®s[157], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETS); + hns_xgmac_cpy_q(®s[159], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_1519TOMAXOCTETSOK); + hns_xgmac_cpy_q(®s[161], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_OVERSIZE); + hns_xgmac_cpy_q(®s[163], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PKTS_JABBER); + hns_xgmac_cpy_q(®s[165], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODPKTS); + hns_xgmac_cpy_q(®s[167], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_GOODOCTETS); + hns_xgmac_cpy_q(®s[169], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTAL_PKTS); + hns_xgmac_cpy_q(®s[171], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_TOTALOCTETS); + hns_xgmac_cpy_q(®s[173], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_UNICASTPKTS); + hns_xgmac_cpy_q(®s[175], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MULTICASTPKTS); + hns_xgmac_cpy_q(®s[177], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_BROADCASTPKTS); + hns_xgmac_cpy_q(®s[179], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI0PAUSEPKTS); + hns_xgmac_cpy_q(®s[181], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI1PAUSEPKTS); + hns_xgmac_cpy_q(®s[183], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI2PAUSEPKTS); + hns_xgmac_cpy_q(®s[185], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI3PAUSEPKTS); + hns_xgmac_cpy_q(®s[187], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI4PAUSEPKTS); + hns_xgmac_cpy_q(®s[189], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI5PAUSEPKTS); + hns_xgmac_cpy_q(®s[191], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI6PAUSEPKTS); + hns_xgmac_cpy_q(®s[193], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_PRI7PAUSEPKTS); + hns_xgmac_cpy_q(®s[195], qtmp); + + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_MACCTRLPKTS); + hns_xgmac_cpy_q(®s[197], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPGOODPKTS); + hns_xgmac_cpy_q(®s[199], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_TX_SENDAPPBADPKTS); + hns_xgmac_cpy_q(®s[201], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_1731PKTS); + hns_xgmac_cpy_q(®s[203], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_SYMBOLERRPKTS); + hns_xgmac_cpy_q(®s[205], qtmp); + qtmp = hns_mac_reg_read64(drv, XGMAC_RX_FCSERRPKTS); + hns_xgmac_cpy_q(®s[207], qtmp); + + /* mark end of mac regs */ + for (i = 208; i < 214; i++) + regs[i] = 0xaaaaaaaa; +} + +/** + *hns_xgmac_get_stats - get xgmac statistic + *@mac_drv: mac driver + *@data:data for value of stats regs + */ +static void hns_xgmac_get_stats(void *mac_drv, u64 *data) +{ + u32 i; + u64 *buf = data; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + struct mac_hw_stats *hw_stats = NULL; + + hw_stats = &drv->mac_cb->hw_stats; + + for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) { + buf[i] = DSAF_STATS_READ(hw_stats, + g_xgmac_stats_string[i].offset); + } +} + +/** + *hns_xgmac_get_strings - get xgmac strings name + *@stringset: type of values in data + *@data:data for value of string name + */ +static void hns_xgmac_get_strings(u32 stringset, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(g_xgmac_stats_string); i++) { + snprintf(buff, ETH_GSTRING_LEN, g_xgmac_stats_string[i].desc); + buff = buff + ETH_GSTRING_LEN; + } +} + +/** + *hns_xgmac_get_sset_count - get xgmac string set count + *@stringset: type of values in data + *return xgmac string set count + */ +static int hns_xgmac_get_sset_count(int stringset) +{ + if (stringset == ETH_SS_STATS) + return ARRAY_SIZE(g_xgmac_stats_string); + + return 0; +} + +/** + *hns_xgmac_get_regs_count - get xgmac regs count + *return xgmac regs count + */ +static int hns_xgmac_get_regs_count(void) +{ + return ETH_XGMAC_DUMP_NUM; +} + +void *hns_xgmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) +{ + struct mac_driver *mac_drv; + + mac_drv = devm_kzalloc(mac_cb->dev, sizeof(*mac_drv), GFP_KERNEL); + if (!mac_drv) + return NULL; + + mac_drv->mac_init = hns_xgmac_init; + mac_drv->mac_enable = hns_xgmac_enable; + mac_drv->mac_disable = hns_xgmac_disable; + + mac_drv->mac_id = mac_param->mac_id; + mac_drv->mac_mode = mac_param->mac_mode; + mac_drv->io_base = mac_param->vaddr; + mac_drv->dev = mac_param->dev; + mac_drv->mac_cb = mac_cb; + + mac_drv->set_mac_addr = hns_xgmac_set_pausefrm_mac_addr; + mac_drv->set_an_mode = NULL; + mac_drv->config_loopback = NULL; + mac_drv->config_pad_and_crc = hns_xgmac_config_pad_and_crc; + mac_drv->config_half_duplex = NULL; + mac_drv->set_rx_ignore_pause_frames = + hns_xgmac_set_rx_ignore_pause_frames; + mac_drv->mac_get_id = hns_xgmac_get_id; + mac_drv->mac_free = hns_xgmac_free; + mac_drv->adjust_link = NULL; + mac_drv->set_tx_auto_pause_frames = hns_xgmac_set_tx_auto_pause_frames; + mac_drv->config_max_frame_length = hns_xgmac_config_max_frame_length; + mac_drv->mac_pausefrm_cfg = hns_xgmac_pausefrm_cfg; + mac_drv->autoneg_stat = NULL; + mac_drv->get_info = hns_xgmac_get_info; + mac_drv->get_pause_enable = hns_xgmac_get_pausefrm_cfg; + mac_drv->get_link_status = hns_xgmac_get_link_status; + mac_drv->get_regs = hns_xgmac_get_regs; + mac_drv->get_ethtool_stats = hns_xgmac_get_stats; + mac_drv->get_sset_count = hns_xgmac_get_sset_count; + mac_drv->get_regs_count = hns_xgmac_get_regs_count; + mac_drv->get_strings = hns_xgmac_get_strings; + mac_drv->update_stats = hns_xgmac_update_stats; + + return (void *)mac_drv; +} diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h new file mode 100644 index 000000000000..139f7297c7b4 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _HNS_XGMAC_H +#define _HNS_XGMAC_H + +#define ETH_XGMAC_DUMP_NUM (214) + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c new file mode 100644 index 000000000000..302d3ae8e9e5 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -0,0 +1,1643 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/cpumask.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/skbuff.h> + +#include "hnae.h" +#include "hns_enet.h" + +#define NIC_MAX_Q_PER_VF 16 +#define HNS_NIC_TX_TIMEOUT (5 * HZ) + +#define SERVICE_TIMER_HZ (1 * HZ) + +#define NIC_TX_CLEAN_MAX_NUM 256 +#define NIC_RX_CLEAN_MAX_NUM 64 + +#define RCB_IRQ_NOT_INITED 0 +#define RCB_IRQ_INITED 1 + +static void fill_desc(struct hnae_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + int buf_num, enum hns_desc_type type) +{ + struct hnae_desc *desc = &ring->desc[ring->next_to_use]; + struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; + struct sk_buff *skb; + __be16 protocol; + u32 ip_offset; + u32 asid_bufnum_pid = 0; + u32 flag_ipoffset = 0; + + desc_cb->priv = priv; + desc_cb->length = size; + desc_cb->dma = dma; + desc_cb->type = type; + + desc->addr = cpu_to_le64(dma); + desc->tx.send_size = cpu_to_le16((u16)size); + + /*config bd buffer end */ + flag_ipoffset |= 1 << HNS_TXD_VLD_B; + + asid_bufnum_pid |= buf_num << HNS_TXD_BUFNUM_S; + + if (type == DESC_TYPE_SKB) { + skb = (struct sk_buff *)priv; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + protocol = skb->protocol; + ip_offset = ETH_HLEN; + + /*if it is a SW VLAN check the next protocol*/ + if (protocol == htons(ETH_P_8021Q)) { + ip_offset += VLAN_HLEN; + protocol = vlan_get_protocol(skb); + skb->protocol = protocol; + } + + if (skb->protocol == htons(ETH_P_IP)) { + flag_ipoffset |= 1 << HNS_TXD_L3CS_B; + /* check for tcp/udp header */ + flag_ipoffset |= 1 << HNS_TXD_L4CS_B; + + } else if (skb->protocol == htons(ETH_P_IPV6)) { + /* ipv6 has not l3 cs, check for L4 header */ + flag_ipoffset |= 1 << HNS_TXD_L4CS_B; + } + + flag_ipoffset |= ip_offset << HNS_TXD_IPOFFSET_S; + } + } + + flag_ipoffset |= frag_end << HNS_TXD_FE_B; + + desc->tx.asid_bufnum_pid = cpu_to_le16(asid_bufnum_pid); + desc->tx.flag_ipoffset = cpu_to_le32(flag_ipoffset); + + ring_ptr_move_fw(ring, next_to_use); +} + +static void unfill_desc(struct hnae_ring *ring) +{ + ring_ptr_move_bw(ring, next_to_use); +} + +int hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct device *dev = priv->dev; + struct hnae_ring *ring = ring_data->ring; + struct netdev_queue *dev_queue; + struct skb_frag_struct *frag; + int buf_num; + dma_addr_t dma; + int size, next_to_use; + int i, j; + struct sk_buff *new_skb; + + assert(ring->max_desc_num_per_pkt <= ring->desc_num); + + /* no. of segments (plus a header) */ + buf_num = skb_shinfo(skb)->nr_frags + 1; + + if (unlikely(buf_num > ring->max_desc_num_per_pkt)) { + if (ring_space(ring) < 1) { + ring->stats.tx_busy++; + goto out_net_tx_busy; + } + + new_skb = skb_copy(skb, GFP_ATOMIC); + if (!new_skb) { + ring->stats.sw_err_cnt++; + netdev_err(ndev, "no memory to xmit!\n"); + goto out_err_tx_ok; + } + + dev_kfree_skb_any(skb); + skb = new_skb; + buf_num = 1; + assert(skb_shinfo(skb)->nr_frags == 1); + } else if (buf_num > ring_space(ring)) { + ring->stats.tx_busy++; + goto out_net_tx_busy; + } + next_to_use = ring->next_to_use; + + /* fill the first part */ + size = skb_headlen(skb); + dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(ndev, "TX head DMA map failed\n"); + ring->stats.sw_err_cnt++; + goto out_err_tx_ok; + } + fill_desc(ring, skb, size, dma, buf_num == 1 ? 1 : 0, buf_num, + DESC_TYPE_SKB); + + /* fill the fragments */ + for (i = 1; i < buf_num; i++) { + frag = &skb_shinfo(skb)->frags[i - 1]; + size = skb_frag_size(frag); + dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(ndev, "TX frag(%d) DMA map failed\n", i); + ring->stats.sw_err_cnt++; + goto out_map_frag_fail; + } + fill_desc(ring, skb_frag_page(frag), size, dma, + buf_num - 1 == i ? 1 : 0, buf_num, DESC_TYPE_PAGE); + } + + /*complete translate all packets*/ + dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping); + netdev_tx_sent_queue(dev_queue, skb->len); + + wmb(); /* commit all data before submit */ + assert(skb->queue_mapping < priv->ae_handle->q_num); + hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num); + ring->stats.tx_pkts++; + ring->stats.tx_bytes += skb->len; + + return NETDEV_TX_OK; + +out_map_frag_fail: + + for (j = i - 1; j > 0; j--) { + unfill_desc(ring); + next_to_use = ring->next_to_use; + dma_unmap_page(dev, ring->desc_cb[next_to_use].dma, + ring->desc_cb[next_to_use].length, + DMA_TO_DEVICE); + } + + unfill_desc(ring); + next_to_use = ring->next_to_use; + dma_unmap_single(dev, ring->desc_cb[next_to_use].dma, + ring->desc_cb[next_to_use].length, DMA_TO_DEVICE); + +out_err_tx_ok: + + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + +out_net_tx_busy: + + netif_stop_subqueue(ndev, skb->queue_mapping); + + /* Herbert's original patch had: + * smp_mb__after_netif_stop_queue(); + * but since that doesn't exist yet, just open code it. + */ + smp_mb(); + return NETDEV_TX_BUSY; +} + +/** + * hns_nic_get_headlen - determine size of header for RSC/LRO/GRO/FCOE + * @data: pointer to the start of the headers + * @max: total length of section to find headers in + * + * This function is meant to determine the length of headers that will + * be recognized by hardware for LRO, GRO, and RSC offloads. The main + * motivation of doing this is to only perform one pull for IPv4 TCP + * packets so that we can do basic things like calculating the gso_size + * based on the average data per packet. + **/ +static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag, + unsigned int max_size) +{ + unsigned char *network; + u8 hlen; + + /* this should never happen, but better safe than sorry */ + if (max_size < ETH_HLEN) + return max_size; + + /* initialize network frame pointer */ + network = data; + + /* set first protocol and move network header forward */ + network += ETH_HLEN; + + /* handle any vlan tag if present */ + if (hnae_get_field(flag, HNS_RXD_VLAN_M, HNS_RXD_VLAN_S) + == HNS_RX_FLAG_VLAN_PRESENT) { + if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN)) + return max_size; + + network += VLAN_HLEN; + } + + /* handle L3 protocols */ + if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S) + == HNS_RX_FLAG_L3ID_IPV4) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct iphdr))) + return max_size; + + /* access ihl as a u8 to avoid unaligned access on ia64 */ + hlen = (network[0] & 0x0F) << 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct iphdr)) + return network - data; + + /* record next protocol if header is present */ + } else if (hnae_get_field(flag, HNS_RXD_L3ID_M, HNS_RXD_L3ID_S) + == HNS_RX_FLAG_L3ID_IPV6) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct ipv6hdr))) + return max_size; + + /* record next protocol */ + hlen = sizeof(struct ipv6hdr); + } else { + return network - data; + } + + /* relocate pointer to start of L4 header */ + network += hlen; + + /* finally sort out TCP/UDP */ + if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S) + == HNS_RX_FLAG_L4ID_TCP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct tcphdr))) + return max_size; + + /* access doff as a u8 to avoid unaligned access on ia64 */ + hlen = (network[12] & 0xF0) >> 2; + + /* verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct tcphdr)) + return network - data; + + network += hlen; + } else if (hnae_get_field(flag, HNS_RXD_L4ID_M, HNS_RXD_L4ID_S) + == HNS_RX_FLAG_L4ID_UDP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct udphdr))) + return max_size; + + network += sizeof(struct udphdr); + } + + /* If everything has gone correctly network should be the + * data section of the packet and will be the end of the header. + * If not then it probably represents the end of the last recognized + * header. + */ + if ((typeof(max_size))(network - data) < max_size) + return network - data; + else + return max_size; +} + +static void +hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset) +{ + /* avoid re-using remote pages,flag default unreuse */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) { + /* move offset up to the next cache line */ + desc_cb->page_offset += tsize; + + if (desc_cb->page_offset <= last_offset) { + desc_cb->reuse_flag = 1; + /* bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } + } +} + +static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, + struct sk_buff **out_skb, int *out_bnum) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct sk_buff *skb; + struct hnae_desc *desc; + struct hnae_desc_cb *desc_cb; + unsigned char *va; + int bnum, length, size, i, truesize, last_offset; + int pull_len; + u32 bnum_flag; + + last_offset = hnae_page_size(ring) - hnae_buf_size(ring); + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + length = le16_to_cpu(desc->rx.pkt_len); + bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); + bnum = hnae_get_field(bnum_flag, HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S); + *out_bnum = bnum; + va = (unsigned char *)desc_cb->buf + desc_cb->page_offset; + + skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE); + if (unlikely(!skb)) { + netdev_err(ndev, "alloc rx skb fail\n"); + ring->stats.sw_err_cnt++; + return -ENOMEM; + } + + if (length <= HNS_RX_HEAD_SIZE) { + memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long))); + + /* we can reuse buffer as-is, just make sure it is local */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) + desc_cb->reuse_flag = 1; + else /* this page cannot be reused so discard it */ + put_page(desc_cb->priv); + + ring_ptr_move_fw(ring, next_to_clean); + + if (unlikely(bnum != 1)) { /* check err*/ + *out_bnum = 1; + goto out_bnum_err; + } + } else { + ring->stats.seg_pkt_cnt++; + + pull_len = hns_nic_get_headlen(va, bnum_flag, HNS_RX_HEAD_SIZE); + memcpy(__skb_put(skb, pull_len), va, + ALIGN(pull_len, sizeof(long))); + + size = le16_to_cpu(desc->rx.size); + truesize = ALIGN(size, L1_CACHE_BYTES); + skb_add_rx_frag(skb, 0, desc_cb->priv, + desc_cb->page_offset + pull_len, + size - pull_len, truesize - pull_len); + + hns_nic_reuse_page(desc_cb, truesize, last_offset); + ring_ptr_move_fw(ring, next_to_clean); + + if (unlikely(bnum >= (int)MAX_SKB_FRAGS)) { /* check err*/ + *out_bnum = 1; + goto out_bnum_err; + } + for (i = 1; i < bnum; i++) { + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + size = le16_to_cpu(desc->rx.size); + truesize = ALIGN(size, L1_CACHE_BYTES); + skb_add_rx_frag(skb, i, desc_cb->priv, + desc_cb->page_offset, + size, truesize); + + hns_nic_reuse_page(desc_cb, truesize, last_offset); + ring_ptr_move_fw(ring, next_to_clean); + } + } + + /* check except process, free skb and jump the desc */ + if (unlikely((!bnum) || (bnum > ring->max_desc_num_per_pkt))) { +out_bnum_err: + *out_bnum = *out_bnum ? *out_bnum : 1; /* ntc moved,cannot 0*/ + netdev_err(ndev, "invalid bnum(%d,%d,%d,%d),%016llx,%016llx\n", + bnum, ring->max_desc_num_per_pkt, + length, (int)MAX_SKB_FRAGS, + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.err_bd_num++; + dev_kfree_skb_any(skb); + return -EDOM; + } + + bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); + + if (unlikely(!hnae_get_bit(bnum_flag, HNS_RXD_VLD_B))) { + netdev_err(ndev, "no valid bd,%016llx,%016llx\n", + ((u64 *)desc)[0], ((u64 *)desc)[1]); + ring->stats.non_vld_descs++; + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (unlikely((!desc->rx.pkt_len) || + hnae_get_bit(bnum_flag, HNS_RXD_DROP_B))) { + ring->stats.err_pkt_len++; + dev_kfree_skb_any(skb); + return -EFAULT; + } + + if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L2E_B))) { + ring->stats.l2_err++; + dev_kfree_skb_any(skb); + return -EFAULT; + } + + ring->stats.rx_pkts++; + ring->stats.rx_bytes += skb->len; + + if (unlikely(hnae_get_bit(bnum_flag, HNS_RXD_L3E_B) || + hnae_get_bit(bnum_flag, HNS_RXD_L4E_B))) { + ring->stats.l3l4_csum_err++; + return 0; + } + + skb->ip_summed = CHECKSUM_UNNECESSARY; + + return 0; +} + +static void +hns_nic_alloc_rx_buffers(struct hns_nic_ring_data *ring_data, int cleand_count) +{ + int i, ret; + struct hnae_desc_cb res_cbs; + struct hnae_desc_cb *desc_cb; + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + + for (i = 0; i < cleand_count; i++) { + desc_cb = &ring->desc_cb[ring->next_to_use]; + if (desc_cb->reuse_flag) { + ring->stats.reuse_pg_cnt++; + hnae_reuse_buffer(ring, ring->next_to_use); + } else { + ret = hnae_reserve_buffer_map(ring, &res_cbs); + if (ret) { + ring->stats.sw_err_cnt++; + netdev_err(ndev, "hnae reserve buffer map failed.\n"); + break; + } + hnae_replace_buffer(ring, ring->next_to_use, &res_cbs); + } + + ring_ptr_move_fw(ring, next_to_use); + } + + wmb(); /* make all data has been write before submit */ + writel_relaxed(i, ring->io_base + RCB_REG_HEAD); +} + +/* return error number for error or number of desc left to take + */ +static void hns_nic_rx_up_pro(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb) +{ + struct net_device *ndev = ring_data->napi.dev; + + skb->protocol = eth_type_trans(skb, ndev); + (void)napi_gro_receive(&ring_data->napi, skb); + ndev->last_rx = jiffies; +} + +static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data, + int budget, void *v) +{ + struct hnae_ring *ring = ring_data->ring; + struct sk_buff *skb; + int num, bnum, ex_num; +#define RCB_NOF_ALLOC_RX_BUFF_ONCE 16 + int recv_pkts, recv_bds, clean_count, err; + + num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + rmb(); /* make sure num taken effect before the other data is touched */ + + recv_pkts = 0, recv_bds = 0, clean_count = 0; +recv: + while (recv_pkts < budget && recv_bds < num) { + /* reuse or realloc buffers*/ + if (clean_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) { + hns_nic_alloc_rx_buffers(ring_data, clean_count); + clean_count = 0; + } + + /* poll one pkg*/ + err = hns_nic_poll_rx_skb(ring_data, &skb, &bnum); + if (unlikely(!skb)) /* this fault cannot be repaired */ + break; + + recv_bds += bnum; + clean_count += bnum; + if (unlikely(err)) { /* do jump the err */ + recv_pkts++; + continue; + } + + /* do update ip stack process*/ + ((void (*)(struct hns_nic_ring_data *, struct sk_buff *))v)( + ring_data, skb); + recv_pkts++; + } + + /* make all data has been write before submit */ + if (clean_count > 0) { + hns_nic_alloc_rx_buffers(ring_data, clean_count); + clean_count = 0; + } + + if (recv_pkts < budget) { + ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + rmb(); /*complete read rx ring bd number*/ + if (ex_num > 0) { + num += ex_num; + goto recv; + } + } + + return recv_pkts; +} + +static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + int num = 0; + + /* for hardware bug fixed */ + num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); + + if (num > 0) { + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + + napi_schedule(&ring_data->napi); + } +} + +static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring, + int *bytes, int *pkts) +{ + struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean]; + + (*pkts) += (desc_cb->type == DESC_TYPE_SKB); + (*bytes) += desc_cb->length; + /* desc_cb will be cleaned, after hnae_free_buffer_detach*/ + hnae_free_buffer_detach(ring, ring->next_to_clean); + + ring_ptr_move_fw(ring, next_to_clean); +} + +static int is_valid_clean_head(struct hnae_ring *ring, int h) +{ + int u = ring->next_to_use; + int c = ring->next_to_clean; + + if (unlikely(h > ring->desc_num)) + return 0; + + assert(u > 0 && u < ring->desc_num); + assert(c > 0 && c < ring->desc_num); + assert(u != c && h != c); /* must be checked before call this func */ + + return u > c ? (h > c && h <= u) : (h > c || h <= u); +} + +/* netif_tx_lock will turn down the performance, set only when necessary */ +#ifdef CONFIG_NET_POLL_CONTROLLER +#define NETIF_TX_LOCK(ndev) netif_tx_lock(ndev) +#define NETIF_TX_UNLOCK(ndev) netif_tx_unlock(ndev) +#else +#define NETIF_TX_LOCK(ndev) +#define NETIF_TX_UNLOCK(ndev) +#endif +/* reclaim all desc in one budget + * return error or number of desc left + */ +static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, + int budget, void *v) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct netdev_queue *dev_queue; + struct hns_nic_priv *priv = netdev_priv(ndev); + int head; + int bytes, pkts; + + NETIF_TX_LOCK(ndev); + + head = readl_relaxed(ring->io_base + RCB_REG_HEAD); + rmb(); /* make sure head is ready before touch any data */ + + if (is_ring_empty(ring) || head == ring->next_to_clean) { + NETIF_TX_UNLOCK(ndev); + return 0; /* no data to poll */ + } + + if (!is_valid_clean_head(ring, head)) { + netdev_err(ndev, "wrong head (%d, %d-%d)\n", head, + ring->next_to_use, ring->next_to_clean); + ring->stats.io_err_cnt++; + NETIF_TX_UNLOCK(ndev); + return -EIO; + } + + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean) + hns_nic_reclaim_one_desc(ring, &bytes, &pkts); + + NETIF_TX_UNLOCK(ndev); + + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_completed_queue(dev_queue, pkts, bytes); + + if (unlikely(pkts && netif_carrier_ok(ndev) && + (ring_space(ring) >= ring->max_desc_num_per_pkt * 2))) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (netif_tx_queue_stopped(dev_queue) && + !test_bit(NIC_STATE_DOWN, &priv->state)) { + netif_tx_wake_queue(dev_queue); + ring->stats.restart_queue++; + } + } + return 0; +} + +static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + int head = ring->next_to_clean; + + /* for hardware bug fixed */ + head = readl_relaxed(ring->io_base + RCB_REG_HEAD); + + if (head != ring->next_to_clean) { + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + + napi_schedule(&ring_data->napi); + } +} + +static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + struct net_device *ndev = ring_data->napi.dev; + struct netdev_queue *dev_queue; + int head; + int bytes, pkts; + + NETIF_TX_LOCK(ndev); + + head = ring->next_to_use; /* ntu :soft setted ring position*/ + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean) + hns_nic_reclaim_one_desc(ring, &bytes, &pkts); + + NETIF_TX_UNLOCK(ndev); + + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_reset_queue(dev_queue); +} + +static int hns_nic_common_poll(struct napi_struct *napi, int budget) +{ + struct hns_nic_ring_data *ring_data = + container_of(napi, struct hns_nic_ring_data, napi); + int clean_complete = ring_data->poll_one( + ring_data, budget, ring_data->ex_process); + + if (clean_complete >= 0 && clean_complete < budget) { + napi_complete(napi); + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 0); + + ring_data->fini_process(ring_data); + } + + return clean_complete; +} + +static irqreturn_t hns_irq_handle(int irq, void *dev) +{ + struct hns_nic_ring_data *ring_data = (struct hns_nic_ring_data *)dev; + + ring_data->ring->q->handle->dev->ops->toggle_ring_irq( + ring_data->ring, 1); + napi_schedule(&ring_data->napi); + + return IRQ_HANDLED; +} + +/** + *hns_nic_adjust_link - adjust net work mode by the phy stat or new param + *@ndev: net device + */ +static void hns_nic_adjust_link(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->adjust_link(h, ndev->phydev->speed, ndev->phydev->duplex); +} + +/** + *hns_nic_init_phy - init phy + *@ndev: net device + *@h: ae handle + * Return 0 on success, negative on failure + */ +int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = NULL; + + if (!h->phy_node) + return 0; + + if (h->phy_if != PHY_INTERFACE_MODE_XGMII) + phy_dev = of_phy_connect(ndev, h->phy_node, + hns_nic_adjust_link, 0, h->phy_if); + else + phy_dev = of_phy_attach(ndev, h->phy_node, 0, h->phy_if); + + if (unlikely(!phy_dev) || IS_ERR(phy_dev)) + return !phy_dev ? -ENODEV : PTR_ERR(phy_dev); + + phy_dev->supported &= h->if_support; + phy_dev->advertising = phy_dev->supported; + + if (h->phy_if == PHY_INTERFACE_MODE_XGMII) + phy_dev->autoneg = false; + + priv->phy = phy_dev; + + return 0; +} + +static int hns_nic_ring_open(struct net_device *netdev, int idx) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + napi_enable(&priv->ring_data[idx].napi); + + enable_irq(priv->ring_data[idx].ring->irq); + h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 0); + + return 0; +} + +static int hns_nic_net_set_mac_address(struct net_device *ndev, void *p) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct sockaddr *mac_addr = p; + int ret; + + if (!mac_addr || !is_valid_ether_addr((const u8 *)mac_addr->sa_data)) + return -EADDRNOTAVAIL; + + ret = h->dev->ops->set_mac_addr(h, mac_addr->sa_data); + if (ret) { + netdev_err(ndev, "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + memcpy(ndev->dev_addr, mac_addr->sa_data, ndev->addr_len); + + return 0; +} + +void hns_nic_update_stats(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->update_stats(h, &netdev->stats); +} + +/* set mac addr if it is configed. or leave it to the AE driver */ +static void hns_init_mac_addr(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct device_node *node = priv->dev->of_node; + const void *mac_addr_temp; + + mac_addr_temp = of_get_mac_address(node); + if (mac_addr_temp && is_valid_ether_addr(mac_addr_temp)) { + memcpy(ndev->dev_addr, mac_addr_temp, ndev->addr_len); + } else { + eth_hw_addr_random(ndev); + dev_warn(priv->dev, "No valid mac, use random mac %pM", + ndev->dev_addr); + } +} + +static void hns_nic_ring_close(struct net_device *netdev, int idx) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + h->dev->ops->toggle_ring_irq(priv->ring_data[idx].ring, 1); + disable_irq(priv->ring_data[idx].ring->irq); + + napi_disable(&priv->ring_data[idx].napi); +} + +static int hns_nic_init_irq(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hns_nic_ring_data *rd; + int i; + int ret; + int cpu; + cpumask_t mask; + + for (i = 0; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + + if (rd->ring->irq_init_flag == RCB_IRQ_INITED) + break; + + snprintf(rd->ring->ring_name, RCB_RING_NAME_LEN, + "%s-%s%d", priv->netdev->name, + (i < h->q_num ? "tx" : "rx"), rd->queue_index); + + rd->ring->ring_name[RCB_RING_NAME_LEN - 1] = '\0'; + + ret = request_irq(rd->ring->irq, + hns_irq_handle, 0, rd->ring->ring_name, rd); + if (ret) { + netdev_err(priv->netdev, "request irq(%d) fail\n", + rd->ring->irq); + return ret; + } + disable_irq(rd->ring->irq); + rd->ring->irq_init_flag = RCB_IRQ_INITED; + + /*set cpu affinity*/ + if (cpu_online(rd->queue_index)) { + cpumask_clear(&mask); + cpu = rd->queue_index; + cpumask_set_cpu(cpu, &mask); + irq_set_affinity_hint(rd->ring->irq, &mask); + } + } + + return 0; +} + +static int hns_nic_net_up(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int i, j, k; + int ret; + + ret = hns_nic_init_irq(priv); + if (ret != 0) { + netdev_err(ndev, "hns init irq failed! ret=%d\n", ret); + return ret; + } + + for (i = 0; i < h->q_num * 2; i++) { + ret = hns_nic_ring_open(ndev, i); + if (ret) + goto out_has_some_queues; + } + + for (k = 0; k < h->q_num; k++) + h->dev->ops->toggle_queue_status(h->qs[k], 1); + + ret = h->dev->ops->set_mac_addr(h, ndev->dev_addr); + if (ret) + goto out_set_mac_addr_err; + + ret = h->dev->ops->start ? h->dev->ops->start(h) : 0; + if (ret) + goto out_start_err; + + if (priv->phy) + phy_start(priv->phy); + + clear_bit(NIC_STATE_DOWN, &priv->state); + (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ); + + return 0; + +out_start_err: + netif_stop_queue(ndev); +out_set_mac_addr_err: + for (k = 0; k < h->q_num; k++) + h->dev->ops->toggle_queue_status(h->qs[k], 0); +out_has_some_queues: + for (j = i - 1; j >= 0; j--) + hns_nic_ring_close(ndev, j); + + set_bit(NIC_STATE_DOWN, &priv->state); + + return ret; +} + +static void hns_nic_net_down(struct net_device *ndev) +{ + int i; + struct hnae_ae_ops *ops; + struct hns_nic_priv *priv = netdev_priv(ndev); + + if (test_and_set_bit(NIC_STATE_DOWN, &priv->state)) + return; + + (void)del_timer_sync(&priv->service_timer); + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + netif_tx_disable(ndev); + priv->link = 0; + + if (priv->phy) + phy_stop(priv->phy); + + ops = priv->ae_handle->dev->ops; + + if (ops->stop) + ops->stop(priv->ae_handle); + + netif_tx_stop_all_queues(ndev); + + for (i = priv->ae_handle->q_num - 1; i >= 0; i--) { + hns_nic_ring_close(ndev, i); + hns_nic_ring_close(ndev, i + priv->ae_handle->q_num); + + /* clean tx buffers*/ + hns_nic_tx_clr_all_bufs(priv->ring_data + i); + } +} + +void hns_nic_net_reset(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *handle = priv->ae_handle; + + while (test_and_set_bit(NIC_STATE_RESETTING, &priv->state)) + usleep_range(1000, 2000); + + (void)hnae_reinit_handle(handle); + + clear_bit(NIC_STATE_RESETTING, &priv->state); +} + +void hns_nic_net_reinit(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + priv->netdev->trans_start = jiffies; + while (test_and_set_bit(NIC_STATE_REINITING, &priv->state)) + usleep_range(1000, 2000); + + hns_nic_net_down(netdev); + hns_nic_net_reset(netdev); + (void)hns_nic_net_up(netdev); + clear_bit(NIC_STATE_REINITING, &priv->state); +} + +static int hns_nic_net_open(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + if (test_bit(NIC_STATE_TESTING, &priv->state)) + return -EBUSY; + + priv->link = 0; + netif_carrier_off(ndev); + + ret = netif_set_real_num_tx_queues(ndev, h->q_num); + if (ret < 0) { + netdev_err(ndev, "netif_set_real_num_tx_queues fail, ret=%d!\n", + ret); + return ret; + } + + ret = netif_set_real_num_rx_queues(ndev, h->q_num); + if (ret < 0) { + netdev_err(ndev, + "netif_set_real_num_rx_queues fail, ret=%d!\n", ret); + return ret; + } + + ret = hns_nic_net_up(ndev); + if (ret) { + netdev_err(ndev, + "hns net up fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static int hns_nic_net_stop(struct net_device *ndev) +{ + hns_nic_net_down(ndev); + + return 0; +} + +static void hns_tx_timeout_reset(struct hns_nic_priv *priv); +static void hns_nic_net_timeout(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + + hns_tx_timeout_reset(priv); +} + +static int hns_nic_do_ioctl(struct net_device *netdev, struct ifreq *ifr, + int cmd) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy_dev = priv->phy; + + if (!netif_running(netdev)) + return -EINVAL; + + if (!phy_dev) + return -ENOTSUPP; + + return phy_mii_ioctl(phy_dev, ifr, cmd); +} + +/* use only for netconsole to poll with the device without interrupt */ +#ifdef CONFIG_NET_POLL_CONTROLLER +void hns_nic_poll_controller(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < priv->ae_handle->q_num * 2; i++) + napi_schedule(&priv->ring_data[i].napi); + local_irq_restore(flags); +} +#endif + +static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + int ret; + + assert(skb->queue_mapping < ndev->ae_handle->q_num); + ret = hns_nic_net_xmit_hw(ndev, skb, + &tx_ring_data(priv, skb->queue_mapping)); + if (ret == NETDEV_TX_OK) { + ndev->trans_start = jiffies; + ndev->stats.tx_bytes += skb->len; + ndev->stats.tx_packets++; + } + return (netdev_tx_t)ret; +} + +static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + /* MTU < 68 is an error and causes problems on some kernels */ + if (new_mtu < 68) + return -EINVAL; + + if (!h->dev->ops->set_mtu) + return -ENOTSUPP; + + if (netif_running(ndev)) { + (void)hns_nic_net_stop(ndev); + msleep(100); + + ret = h->dev->ops->set_mtu(h, new_mtu); + if (ret) + netdev_err(ndev, "set mtu fail, return value %d\n", + ret); + + if (hns_nic_net_open(ndev)) + netdev_err(ndev, "hns net open fail\n"); + } else { + ret = h->dev->ops->set_mtu(h, new_mtu); + } + + if (!ret) + ndev->mtu = new_mtu; + + return ret; +} + +/** + * nic_set_multicast_list - set mutl mac address + * @netdev: net device + * @p: mac address + * + * return void + */ +void hns_set_multicast_list(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct netdev_hw_addr *ha = NULL; + + if (!h) { + netdev_err(ndev, "hnae handle is null\n"); + return; + } + + if (h->dev->ops->set_mc_addr) { + netdev_for_each_mc_addr(ha, ndev) + if (h->dev->ops->set_mc_addr(h, ha->addr)) + netdev_err(ndev, "set multicast fail\n"); + } +} + +void hns_nic_set_rx_mode(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + if (h->dev->ops->set_promisc_mode) { + if (ndev->flags & IFF_PROMISC) + h->dev->ops->set_promisc_mode(h, 1); + else + h->dev->ops->set_promisc_mode(h, 0); + } + + hns_set_multicast_list(ndev); +} + +struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev, + struct rtnl_link_stats64 *stats) +{ + int idx = 0; + u64 tx_bytes = 0; + u64 rx_bytes = 0; + u64 tx_pkts = 0; + u64 rx_pkts = 0; + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + + for (idx = 0; idx < h->q_num; idx++) { + tx_bytes += h->qs[idx]->tx_ring.stats.tx_bytes; + tx_pkts += h->qs[idx]->tx_ring.stats.tx_pkts; + rx_bytes += h->qs[idx]->rx_ring.stats.rx_bytes; + rx_pkts += h->qs[idx]->rx_ring.stats.rx_pkts; + } + + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_pkts; + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_pkts; + + stats->rx_errors = ndev->stats.rx_errors; + stats->multicast = ndev->stats.multicast; + stats->rx_length_errors = ndev->stats.rx_length_errors; + stats->rx_crc_errors = ndev->stats.rx_crc_errors; + stats->rx_missed_errors = ndev->stats.rx_missed_errors; + + stats->tx_errors = ndev->stats.tx_errors; + stats->rx_dropped = ndev->stats.rx_dropped; + stats->tx_dropped = ndev->stats.tx_dropped; + stats->collisions = ndev->stats.collisions; + stats->rx_over_errors = ndev->stats.rx_over_errors; + stats->rx_frame_errors = ndev->stats.rx_frame_errors; + stats->rx_fifo_errors = ndev->stats.rx_fifo_errors; + stats->tx_aborted_errors = ndev->stats.tx_aborted_errors; + stats->tx_carrier_errors = ndev->stats.tx_carrier_errors; + stats->tx_fifo_errors = ndev->stats.tx_fifo_errors; + stats->tx_heartbeat_errors = ndev->stats.tx_heartbeat_errors; + stats->tx_window_errors = ndev->stats.tx_window_errors; + stats->rx_compressed = ndev->stats.rx_compressed; + stats->tx_compressed = ndev->stats.tx_compressed; + + return stats; +} + +static const struct net_device_ops hns_nic_netdev_ops = { + .ndo_open = hns_nic_net_open, + .ndo_stop = hns_nic_net_stop, + .ndo_start_xmit = hns_nic_net_xmit, + .ndo_tx_timeout = hns_nic_net_timeout, + .ndo_set_mac_address = hns_nic_net_set_mac_address, + .ndo_change_mtu = hns_nic_change_mtu, + .ndo_do_ioctl = hns_nic_do_ioctl, + .ndo_get_stats64 = hns_nic_get_stats64, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = hns_nic_poll_controller, +#endif + .ndo_set_rx_mode = hns_nic_set_rx_mode, +}; + +static void hns_nic_update_link_status(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + struct hnae_handle *h = priv->ae_handle; + int state = 1; + + if (priv->phy) { + if (!genphy_update_link(priv->phy)) + state = priv->phy->link; + else + state = 0; + } + state = state && h->dev->ops->get_status(h); + + if (state != priv->link) { + if (state) { + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + netdev_info(netdev, "link up\n"); + } else { + netif_carrier_off(netdev); + netdev_info(netdev, "link down\n"); + } + priv->link = state; + } +} + +/* for dumping key regs*/ +static void hns_nic_dump(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + u32 *data, reg_num, i; + + if (ops->get_regs_len && ops->get_regs) { + reg_num = ops->get_regs_len(priv->ae_handle); + reg_num = (reg_num + 3ul) & ~3ul; + data = kcalloc(reg_num, sizeof(u32), GFP_KERNEL); + if (data) { + ops->get_regs(priv->ae_handle, data); + for (i = 0; i < reg_num; i += 4) + pr_info("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", + i, data[i], data[i + 1], + data[i + 2], data[i + 3]); + kfree(data); + } + } + + for (i = 0; i < h->q_num; i++) { + pr_info("tx_queue%d_next_to_clean:%d\n", + i, h->qs[i]->tx_ring.next_to_clean); + pr_info("tx_queue%d_next_to_use:%d\n", + i, h->qs[i]->tx_ring.next_to_use); + pr_info("rx_queue%d_next_to_clean:%d\n", + i, h->qs[i]->rx_ring.next_to_clean); + pr_info("rx_queue%d_next_to_use:%d\n", + i, h->qs[i]->rx_ring.next_to_use); + } +} + +/* for resetting suntask*/ +static void hns_nic_reset_subtask(struct hns_nic_priv *priv) +{ + enum hnae_port_type type = priv->ae_handle->port_type; + + if (!test_bit(NIC_STATE2_RESET_REQUESTED, &priv->state)) + return; + clear_bit(NIC_STATE2_RESET_REQUESTED, &priv->state); + + /* If we're already down, removing or resetting, just bail */ + if (test_bit(NIC_STATE_DOWN, &priv->state) || + test_bit(NIC_STATE_REMOVING, &priv->state) || + test_bit(NIC_STATE_RESETTING, &priv->state)) + return; + + hns_nic_dump(priv); + netdev_info(priv->netdev, "Reset %s port\n", + (type == HNAE_PORT_DEBUG ? "debug" : "business")); + + rtnl_lock(); + /* put off any impending NetWatchDogTimeout */ + priv->netdev->trans_start = jiffies; + + if (type == HNAE_PORT_DEBUG) + hns_nic_net_reinit(priv->netdev); + rtnl_unlock(); +} + +/* for doing service complete*/ +static void hns_nic_service_event_complete(struct hns_nic_priv *priv) +{ + assert(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state)); + + smp_mb__before_atomic(); + clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); +} + +static void hns_nic_service_task(struct work_struct *work) +{ + struct hns_nic_priv *priv + = container_of(work, struct hns_nic_priv, service_task); + struct hnae_handle *h = priv->ae_handle; + + hns_nic_update_link_status(priv->netdev); + h->dev->ops->update_led_status(h); + hns_nic_update_stats(priv->netdev); + + hns_nic_reset_subtask(priv); + hns_nic_service_event_complete(priv); +} + +static void hns_nic_task_schedule(struct hns_nic_priv *priv) +{ + if (!test_bit(NIC_STATE_DOWN, &priv->state) && + !test_bit(NIC_STATE_REMOVING, &priv->state) && + !test_and_set_bit(NIC_STATE_SERVICE_SCHED, &priv->state)) + (void)schedule_work(&priv->service_task); +} + +static void hns_nic_service_timer(unsigned long data) +{ + struct hns_nic_priv *priv = (struct hns_nic_priv *)data; + + (void)mod_timer(&priv->service_timer, jiffies + SERVICE_TIMER_HZ); + + hns_nic_task_schedule(priv); +} + +/** + * hns_tx_timeout_reset - initiate reset due to Tx timeout + * @priv: driver private struct + **/ +static void hns_tx_timeout_reset(struct hns_nic_priv *priv) +{ + /* Do the reset outside of interrupt context */ + if (!test_bit(NIC_STATE_DOWN, &priv->state)) { + set_bit(NIC_STATE2_RESET_REQUESTED, &priv->state); + netdev_warn(priv->netdev, + "initiating reset due to tx timeout(%llu,0x%lx)\n", + priv->tx_timeout_count, priv->state); + priv->tx_timeout_count++; + hns_nic_task_schedule(priv); + } +} + +static int hns_nic_init_ring_data(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hns_nic_ring_data *rd; + int i; + + if (h->q_num > NIC_MAX_Q_PER_VF) { + netdev_err(priv->netdev, "too much queue (%d)\n", h->q_num); + return -EINVAL; + } + + priv->ring_data = kzalloc(h->q_num * sizeof(*priv->ring_data) * 2, + GFP_KERNEL); + if (!priv->ring_data) + return -ENOMEM; + + for (i = 0; i < h->q_num; i++) { + rd = &priv->ring_data[i]; + rd->queue_index = i; + rd->ring = &h->qs[i]->tx_ring; + rd->poll_one = hns_nic_tx_poll_one; + rd->fini_process = hns_nic_tx_fini_pro; + + netif_napi_add(priv->netdev, &rd->napi, + hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM); + rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + for (i = h->q_num; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + rd->queue_index = i - h->q_num; + rd->ring = &h->qs[i - h->q_num]->rx_ring; + rd->poll_one = hns_nic_rx_poll_one; + rd->ex_process = hns_nic_rx_up_pro; + rd->fini_process = hns_nic_rx_fini_pro; + + netif_napi_add(priv->netdev, &rd->napi, + hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM); + rd->ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + + return 0; +} + +static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + int i; + + for (i = 0; i < h->q_num * 2; i++) { + netif_napi_del(&priv->ring_data[i].napi); + if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) { + irq_set_affinity_hint(priv->ring_data[i].ring->irq, + NULL); + free_irq(priv->ring_data[i].ring->irq, + &priv->ring_data[i]); + } + + priv->ring_data[i].ring->irq_init_flag = RCB_IRQ_NOT_INITED; + } + kfree(priv->ring_data); +} + +static int hns_nic_try_get_ae(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h; + int ret; + + h = hnae_get_handle(&priv->netdev->dev, + priv->ae_name, priv->port_id, NULL); + if (IS_ERR_OR_NULL(h)) { + ret = PTR_ERR(h); + dev_dbg(priv->dev, "has not handle, register notifier!\n"); + goto out; + } + priv->ae_handle = h; + + ret = hns_nic_init_phy(ndev, h); + if (ret) { + dev_err(priv->dev, "probe phy device fail!\n"); + goto out_init_phy; + } + + ret = hns_nic_init_ring_data(priv); + if (ret) { + ret = -ENOMEM; + goto out_init_ring_data; + } + + ret = register_netdev(ndev); + if (ret) { + dev_err(priv->dev, "probe register netdev fail!\n"); + goto out_reg_ndev_fail; + } + return 0; + +out_reg_ndev_fail: + hns_nic_uninit_ring_data(priv); + priv->ring_data = NULL; +out_init_phy: +out_init_ring_data: + hnae_put_handle(priv->ae_handle); + priv->ae_handle = NULL; +out: + return ret; +} + +static int hns_nic_notifier_action(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct hns_nic_priv *priv = + container_of(nb, struct hns_nic_priv, notifier_block); + + assert(action == HNAE_AE_REGISTER); + + if (!hns_nic_try_get_ae(priv->netdev)) { + hnae_unregister_notifier(&priv->notifier_block); + priv->notifier_block.notifier_call = NULL; + } + return 0; +} + +static int hns_nic_dev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct hns_nic_priv *priv; + struct device_node *node = dev->of_node; + int ret; + + ndev = alloc_etherdev_mq(sizeof(struct hns_nic_priv), NIC_MAX_Q_PER_VF); + if (!ndev) + return -ENOMEM; + + platform_set_drvdata(pdev, ndev); + + priv = netdev_priv(ndev); + priv->dev = dev; + priv->netdev = ndev; + + if (of_device_is_compatible(node, "hisilicon,hns-nic-v2")) + priv->enet_ver = AE_VERSION_2; + else + priv->enet_ver = AE_VERSION_1; + + ret = of_property_read_string(node, "ae-name", &priv->ae_name); + if (ret) + goto out_read_string_fail; + + ret = of_property_read_u32(node, "port-id", &priv->port_id); + if (ret) + goto out_read_string_fail; + + hns_init_mac_addr(ndev); + + ndev->watchdog_timeo = HNS_NIC_TX_TIMEOUT; + ndev->priv_flags |= IFF_UNICAST_FLT; + ndev->netdev_ops = &hns_nic_netdev_ops; + hns_ethtool_set_ops(ndev); + ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO; + ndev->vlan_features |= + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; + ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO; + + SET_NETDEV_DEV(ndev, dev); + + if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) + dev_dbg(dev, "set mask to 64bit\n"); + else + dev_err(dev, "set mask to 32bit fail!\n"); + + /* carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(ndev); + + setup_timer(&priv->service_timer, hns_nic_service_timer, + (unsigned long)priv); + INIT_WORK(&priv->service_task, hns_nic_service_task); + + set_bit(NIC_STATE_SERVICE_INITED, &priv->state); + clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); + set_bit(NIC_STATE_DOWN, &priv->state); + + if (hns_nic_try_get_ae(priv->netdev)) { + priv->notifier_block.notifier_call = hns_nic_notifier_action; + ret = hnae_register_notifier(&priv->notifier_block); + if (ret) { + dev_err(dev, "register notifier fail!\n"); + goto out_notify_fail; + } + dev_dbg(dev, "has not handle, register notifier!\n"); + } + + return 0; + +out_notify_fail: + (void)cancel_work_sync(&priv->service_task); +out_read_string_fail: + free_netdev(ndev); + return ret; +} + +static int hns_nic_dev_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct hns_nic_priv *priv = netdev_priv(ndev); + + if (ndev->reg_state != NETREG_UNINITIALIZED) + unregister_netdev(ndev); + + if (priv->ring_data) + hns_nic_uninit_ring_data(priv); + priv->ring_data = NULL; + + if (priv->phy) + phy_disconnect(priv->phy); + priv->phy = NULL; + + if (!IS_ERR_OR_NULL(priv->ae_handle)) + hnae_put_handle(priv->ae_handle); + priv->ae_handle = NULL; + if (priv->notifier_block.notifier_call) + hnae_unregister_notifier(&priv->notifier_block); + priv->notifier_block.notifier_call = NULL; + + set_bit(NIC_STATE_REMOVING, &priv->state); + (void)cancel_work_sync(&priv->service_task); + + free_netdev(ndev); + return 0; +} + +static const struct of_device_id hns_enet_of_match[] = { + {.compatible = "hisilicon,hns-nic-v1",}, + {.compatible = "hisilicon,hns-nic-v2",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, hns_enet_of_match); + +static struct platform_driver hns_nic_dev_driver = { + .driver = { + .name = "hns-nic", + .owner = THIS_MODULE, + .of_match_table = hns_enet_of_match, + }, + .probe = hns_nic_dev_probe, + .remove = hns_nic_dev_remove, +}; + +module_platform_driver(hns_nic_dev_driver); + +MODULE_DESCRIPTION("HISILICON HNS Ethernet driver"); +MODULE_AUTHOR("Hisilicon, Inc."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hns-nic"); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h new file mode 100644 index 000000000000..dae0ed19ac6d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNS_ENET_H +#define __HNS_ENET_H + +#include <linux/netdevice.h> +#include <linux/of_net.h> +#include <linux/of_mdio.h> +#include <linux/timer.h> +#include <linux/workqueue.h> + +#include "hnae.h" + +enum hns_nic_state { + NIC_STATE_TESTING = 0, + NIC_STATE_RESETTING, + NIC_STATE_REINITING, + NIC_STATE_DOWN, + NIC_STATE_DISABLED, + NIC_STATE_REMOVING, + NIC_STATE_SERVICE_INITED, + NIC_STATE_SERVICE_SCHED, + NIC_STATE2_RESET_REQUESTED, + NIC_STATE_MAX +}; + +struct hns_nic_ring_data { + struct hnae_ring *ring; + struct napi_struct napi; + int queue_index; + int (*poll_one)(struct hns_nic_ring_data *, int, void *); + void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *); + void (*fini_process)(struct hns_nic_ring_data *); +}; + +struct hns_nic_priv { + const char *ae_name; + u32 enet_ver; + u32 port_id; + int phy_mode; + int phy_led_val; + struct phy_device *phy; + struct net_device *netdev; + struct device *dev; + struct hnae_handle *ae_handle; + + /* the cb for nic to manage the ring buffer, the first half of the + * array is for tx_ring and vice versa for the second half + */ + struct hns_nic_ring_data *ring_data; + + /* The most recently read link state */ + int link; + u64 tx_timeout_count; + + unsigned long state; + + struct timer_list service_timer; + + struct work_struct service_task; + + struct notifier_block notifier_block; +}; + +#define tx_ring_data(priv, idx) ((priv)->ring_data[idx]) +#define rx_ring_data(priv, idx) \ + ((priv)->ring_data[(priv)->ae_handle->q_num + (idx)]) + +void hns_ethtool_set_ops(struct net_device *ndev); +void hns_nic_net_reset(struct net_device *ndev); +void hns_nic_net_reinit(struct net_device *netdev); +int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h); +int hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data); + +#endif /**__HNS_ENET_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c new file mode 100644 index 000000000000..a0332129970b --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -0,0 +1,1214 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/etherdevice.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "hns_enet.h" + +#define HNS_PHY_PAGE_MDIX 0 +#define HNS_PHY_PAGE_LED 3 +#define HNS_PHY_PAGE_COPPER 0 + +#define HNS_PHY_PAGE_REG 22 /* Page Selection Reg. */ +#define HNS_PHY_CSC_REG 16 /* Copper Specific Control Register */ +#define HNS_PHY_CSS_REG 17 /* Copper Specific Status Register */ +#define HNS_LED_FC_REG 16 /* LED Function Control Reg. */ +#define HNS_LED_PC_REG 17 /* LED Polarity Control Reg. */ + +#define HNS_LED_FORCE_ON 9 +#define HNS_LED_FORCE_OFF 8 + +#define HNS_CHIP_VERSION 660 +#define HNS_NET_STATS_CNT 26 + +#define PHY_MDIX_CTRL_S (5) +#define PHY_MDIX_CTRL_M (3 << PHY_MDIX_CTRL_S) + +#define PHY_MDIX_STATUS_B (6) +#define PHY_SPEED_DUP_RESOLVE_B (11) + +/** + *hns_nic_get_link - get current link status + *@net_dev: net_device + *retuen 0 - success , negative --fail + */ +static u32 hns_nic_get_link(struct net_device *net_dev) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + u32 link_stat = priv->link; + struct hnae_handle *h; + + assert(priv && priv->ae_handle); + h = priv->ae_handle; + + if (priv->phy) { + if (!genphy_update_link(priv->phy)) + link_stat = priv->phy->link; + else + link_stat = 0; + } + + if (h->dev && h->dev->ops && h->dev->ops->get_status) + link_stat = link_stat && h->dev->ops->get_status(h); + else + link_stat = 0; + + return link_stat; +} + +static void hns_get_mdix_mode(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + int mdix_ctrl, mdix, retval, is_resolved; + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct phy_device *phy_dev = priv->phy; + + if (!phy_dev || !phy_dev->bus) { + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + cmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + return; + } + + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_MDIX); + + retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSC_REG); + mdix_ctrl = hnae_get_field(retval, PHY_MDIX_CTRL_M, PHY_MDIX_CTRL_S); + + retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSS_REG); + mdix = hnae_get_bit(retval, PHY_MDIX_STATUS_B); + is_resolved = hnae_get_bit(retval, PHY_SPEED_DUP_RESOLVE_B); + + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + + switch (mdix_ctrl) { + case 0x0: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI; + break; + case 0x1: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X; + break; + case 0x3: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + break; + default: + cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + break; + } + + if (!is_resolved) + cmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + else if (mdix) + cmd->eth_tp_mdix = ETH_TP_MDI_X; + else + cmd->eth_tp_mdix = ETH_TP_MDI; +} + +/** + *hns_nic_get_settings - implement ethtool get settings + *@net_dev: net_device + *@cmd: ethtool_cmd + *retuen 0 - success , negative --fail + */ +static int hns_nic_get_settings(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + u32 link_stat; + int ret; + u8 duplex; + u16 speed; + + if (!priv || !priv->ae_handle) + return -ESRCH; + + h = priv->ae_handle; + if (!h->dev || !h->dev->ops || !h->dev->ops->get_info) + return -ESRCH; + + ret = h->dev->ops->get_info(h, NULL, &speed, &duplex); + if (ret < 0) { + netdev_err(net_dev, "%s get_info error!\n", __func__); + return -EINVAL; + } + + /* When there is no phy, autoneg is off. */ + cmd->autoneg = false; + ethtool_cmd_speed_set(cmd, speed); + cmd->duplex = duplex; + + if (priv->phy) + (void)phy_ethtool_gset(priv->phy, cmd); + + link_stat = hns_nic_get_link(net_dev); + if (!link_stat) { + ethtool_cmd_speed_set(cmd, (u32)SPEED_UNKNOWN); + cmd->duplex = DUPLEX_UNKNOWN; + } + + if (cmd->autoneg) + cmd->advertising |= ADVERTISED_Autoneg; + + cmd->supported |= h->if_support; + if (h->phy_if == PHY_INTERFACE_MODE_SGMII) { + cmd->supported |= SUPPORTED_TP; + cmd->advertising |= ADVERTISED_1000baseT_Full; + } else if (h->phy_if == PHY_INTERFACE_MODE_XGMII) { + cmd->supported |= SUPPORTED_FIBRE; + cmd->advertising |= ADVERTISED_10000baseKR_Full; + } + + if (h->port_type == HNAE_PORT_SERVICE) { + cmd->port = PORT_FIBRE; + cmd->supported |= SUPPORTED_Pause; + } else { + cmd->port = PORT_TP; + } + + cmd->transceiver = XCVR_EXTERNAL; + cmd->mdio_support = (ETH_MDIO_SUPPORTS_C45 | ETH_MDIO_SUPPORTS_C22); + hns_get_mdix_mode(net_dev, cmd); + + return 0; +} + +/** + *hns_nic_set_settings - implement ethtool set settings + *@net_dev: net_device + *@cmd: ethtool_cmd + *retuen 0 - success , negative --fail + */ +static int hns_nic_set_settings(struct net_device *net_dev, + struct ethtool_cmd *cmd) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + u32 speed; + + if (!netif_running(net_dev)) + return -ESRCH; + + if (!priv || !priv->ae_handle || !priv->ae_handle->dev || + !priv->ae_handle->dev->ops) + return -ENODEV; + + h = priv->ae_handle; + speed = ethtool_cmd_speed(cmd); + + if (h->phy_if == PHY_INTERFACE_MODE_XGMII) { + if (cmd->autoneg == AUTONEG_ENABLE || speed != SPEED_10000 || + cmd->duplex != DUPLEX_FULL) + return -EINVAL; + } else if (h->phy_if == PHY_INTERFACE_MODE_SGMII) { + if (!priv->phy && cmd->autoneg == AUTONEG_ENABLE) + return -EINVAL; + + if (speed == SPEED_1000 && cmd->duplex == DUPLEX_HALF) + return -EINVAL; + if (priv->phy) + return phy_ethtool_sset(priv->phy, cmd); + + if ((speed != SPEED_10 && speed != SPEED_100 && + speed != SPEED_1000) || (cmd->duplex != DUPLEX_HALF && + cmd->duplex != DUPLEX_FULL)) + return -EINVAL; + } else { + netdev_err(net_dev, "Not supported!"); + return -ENOTSUPP; + } + + if (h->dev->ops->adjust_link) { + h->dev->ops->adjust_link(h, (int)speed, cmd->duplex); + return 0; + } + + netdev_err(net_dev, "Not supported!"); + return -ENOTSUPP; +} + +static const char hns_nic_test_strs[][ETH_GSTRING_LEN] = { + "Mac Loopback test", + "Serdes Loopback test", + "Phy Loopback test" +}; + +static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en) +{ +#define COPPER_CONTROL_REG 0 +#define PHY_LOOP_BACK BIT(14) + u16 val = 0; + + if (phy_dev->is_c45) /* c45 branch adding for XGE PHY */ + return -ENOTSUPP; + + if (en) { + /* speed : 1000M */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, 2); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 21, 0x1046); + /* Force Master */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 9, 0x1F00); + /* Soft-reset */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 0, 0x9140); + /* If autoneg disabled,two soft-reset operations */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 0, 0x9140); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0xFA); + + /* Default is 0x0400 */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 1, 0x418); + + /* Force 1000M Link, Default is 0x0200 */ + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 7, 0x20C); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0); + + /* Enable MAC loop-back */ + val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG); + val |= PHY_LOOP_BACK; + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG, val); + } else { + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0xFA); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 1, 0x400); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 7, 0x200); + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + 22, 0); + + val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG); + val &= ~PHY_LOOP_BACK; + (void)mdiobus_write(phy_dev->bus, phy_dev->addr, + COPPER_CONTROL_REG, val); + } + return 0; +} + +static int __lb_setup(struct net_device *ndev, + enum hnae_loop loop) +{ + int ret = 0; + struct hns_nic_priv *priv = netdev_priv(ndev); + struct phy_device *phy_dev = priv->phy; + struct hnae_handle *h = priv->ae_handle; + + switch (loop) { + case MAC_INTERNALLOOP_PHY: + if ((phy_dev) && (!phy_dev->is_c45)) + ret = hns_nic_config_phy_loopback(phy_dev, 0x1); + break; + case MAC_INTERNALLOOP_MAC: + if ((h->dev->ops->set_loopback) && + (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)) + ret = h->dev->ops->set_loopback(h, loop, 0x1); + break; + case MAC_INTERNALLOOP_SERDES: + if (h->dev->ops->set_loopback) + ret = h->dev->ops->set_loopback(h, loop, 0x1); + break; + case MAC_LOOP_NONE: + if ((phy_dev) && (!phy_dev->is_c45)) + ret |= hns_nic_config_phy_loopback(phy_dev, 0x0); + + if (h->dev->ops->set_loopback) { + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) + ret |= h->dev->ops->set_loopback(h, + MAC_INTERNALLOOP_MAC, 0x0); + + ret |= h->dev->ops->set_loopback(h, + MAC_INTERNALLOOP_SERDES, 0x0); + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int __lb_up(struct net_device *ndev, + enum hnae_loop loop_mode) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int speed, duplex; + int ret; + + hns_nic_net_reset(ndev); + + if (priv->phy) { + phy_disconnect(priv->phy); + msleep(100); + + ret = hns_nic_init_phy(ndev, h); + if (ret) + return ret; + } + + ret = __lb_setup(ndev, loop_mode); + if (ret) + return ret; + + msleep(100); + + ret = h->dev->ops->start ? h->dev->ops->start(h) : 0; + if (ret) + return ret; + + if (priv->phy) + phy_start(priv->phy); + + /* link adjust duplex*/ + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) + speed = 1000; + else + speed = 10000; + duplex = 1; + + h->dev->ops->adjust_link(h, speed, duplex); + + return 0; +} + +static void __lb_other_process(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb) +{ + struct net_device *ndev; + struct hnae_ring *ring; + struct netdev_queue *dev_queue; + struct sk_buff *new_skb; + unsigned int frame_size; + int check_ok; + u32 i; + char buff[33]; /* 32B data and the last character '\0' */ + + if (!ring_data) { /* Just for doing create frame*/ + frame_size = skb->len; + memset(skb->data, 0xFF, frame_size); + frame_size &= ~1ul; + memset(&skb->data[frame_size / 2], 0xAA, frame_size / 2 - 1); + memset(&skb->data[frame_size / 2 + 10], 0xBE, + frame_size / 2 - 11); + memset(&skb->data[frame_size / 2 + 12], 0xAF, + frame_size / 2 - 13); + return; + } + + ring = ring_data->ring; + ndev = ring_data->napi.dev; + if (is_tx_ring(ring)) { /* for tx queue reset*/ + dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); + netdev_tx_reset_queue(dev_queue); + return; + } + + frame_size = skb->len; + frame_size &= ~1ul; + /* for mutl buffer*/ + new_skb = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb_any(skb); + skb = new_skb; + + check_ok = 0; + if (*(skb->data + 10) == 0xFF) { /* for rx check frame*/ + if ((*(skb->data + frame_size / 2 + 10) == 0xBE) && + (*(skb->data + frame_size / 2 + 12) == 0xAF)) + check_ok = 1; + } + + if (check_ok) { + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + } else { + ndev->stats.rx_frame_errors++; + for (i = 0; i < skb->len; i++) { + snprintf(buff + i % 16 * 2, 3, /* tailing \0*/ + "%02x", *(skb->data + i)); + if ((i % 16 == 15) || (i == skb->len - 1)) + pr_info("%s\n", buff); + } + } + dev_kfree_skb_any(skb); +} + +static int __lb_clean_rings(struct hns_nic_priv *priv, + int ringid0, int ringid1, int budget) +{ + int i, ret; + struct hns_nic_ring_data *ring_data; + struct net_device *ndev = priv->netdev; + unsigned long rx_packets = ndev->stats.rx_packets; + unsigned long rx_bytes = ndev->stats.rx_bytes; + unsigned long rx_frame_errors = ndev->stats.rx_frame_errors; + + for (i = ringid0; i <= ringid1; i++) { + ring_data = &priv->ring_data[i]; + (void)ring_data->poll_one(ring_data, + budget, __lb_other_process); + } + ret = (int)(ndev->stats.rx_packets - rx_packets); + ndev->stats.rx_packets = rx_packets; + ndev->stats.rx_bytes = rx_bytes; + ndev->stats.rx_frame_errors = rx_frame_errors; + return ret; +} + +/** + * nic_run_loopback_test - run loopback test + * @nic_dev: net device + * @loopback_type: loopback type + */ +static int __lb_run_test(struct net_device *ndev, + enum hnae_loop loop_mode) +{ +#define NIC_LB_TEST_PKT_NUM_PER_CYCLE 1 +#define NIC_LB_TEST_RING_ID 0 +#define NIC_LB_TEST_FRAME_SIZE 128 +/* nic loopback test err */ +#define NIC_LB_TEST_NO_MEM_ERR 1 +#define NIC_LB_TEST_TX_CNT_ERR 2 +#define NIC_LB_TEST_RX_CNT_ERR 3 +#define NIC_LB_TEST_RX_PKG_ERR 4 + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int i, j, lc, good_cnt, ret_val = 0; + unsigned int size; + netdev_tx_t tx_ret_val; + struct sk_buff *skb; + + size = NIC_LB_TEST_FRAME_SIZE; + /* allocate test skb */ + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return NIC_LB_TEST_NO_MEM_ERR; + + /* place data into test skb */ + (void)skb_put(skb, size); + __lb_other_process(NULL, skb); + skb->queue_mapping = NIC_LB_TEST_RING_ID; + + lc = 1; + for (j = 0; j < lc; j++) { + /* reset count of good packets */ + good_cnt = 0; + /* place 64 packets on the transmit queue*/ + for (i = 0; i < NIC_LB_TEST_PKT_NUM_PER_CYCLE; i++) { + (void)skb_get(skb); + + tx_ret_val = (netdev_tx_t)hns_nic_net_xmit_hw( + ndev, skb, + &tx_ring_data(priv, skb->queue_mapping)); + if (tx_ret_val == NETDEV_TX_OK) + good_cnt++; + else + break; + } + if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) { + ret_val = NIC_LB_TEST_TX_CNT_ERR; + dev_err(priv->dev, "%s sent fail, cnt=0x%x, budget=0x%x\n", + hns_nic_test_strs[loop_mode], good_cnt, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + break; + } + + /* allow 100 milliseconds for packets to go from Tx to Rx */ + msleep(100); + + good_cnt = __lb_clean_rings(priv, + h->q_num, h->q_num * 2 - 1, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + if (good_cnt != NIC_LB_TEST_PKT_NUM_PER_CYCLE) { + ret_val = NIC_LB_TEST_RX_CNT_ERR; + dev_err(priv->dev, "%s recv fail, cnt=0x%x, budget=0x%x\n", + hns_nic_test_strs[loop_mode], good_cnt, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + break; + } + (void)__lb_clean_rings(priv, + NIC_LB_TEST_RING_ID, NIC_LB_TEST_RING_ID, + NIC_LB_TEST_PKT_NUM_PER_CYCLE); + } + + /* free the original skb */ + kfree_skb(skb); + + return ret_val; +} + +static int __lb_down(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + int ret; + + ret = __lb_setup(ndev, MAC_LOOP_NONE); + if (ret) + netdev_err(ndev, "%s: __lb_setup return error(%d)!\n", + __func__, + ret); + + if (priv->phy) + phy_stop(priv->phy); + + if (h->dev->ops->stop) + h->dev->ops->stop(h); + + usleep_range(10000, 20000); + (void)__lb_clean_rings(priv, 0, h->q_num - 1, 256); + + hns_nic_net_reset(ndev); + + return 0; +} + +/** + * hns_nic_self_test - self test + * @dev: net device + * @eth_test: test cmd + * @data: test result + */ +static void hns_nic_self_test(struct net_device *ndev, + struct ethtool_test *eth_test, u64 *data) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + bool if_running = netif_running(ndev); +#define SELF_TEST_TPYE_NUM 3 + int st_param[SELF_TEST_TPYE_NUM][2]; + int i; + int test_index = 0; + + st_param[0][0] = MAC_INTERNALLOOP_MAC; /* XGE not supported lb */ + st_param[0][1] = (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII); + st_param[1][0] = MAC_INTERNALLOOP_SERDES; + st_param[1][1] = 1; /*serdes must exist*/ + st_param[2][0] = MAC_INTERNALLOOP_PHY; /* only supporte phy node*/ + st_param[2][1] = ((!!(priv->ae_handle->phy_node)) && + (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII)); + + if (eth_test->flags == ETH_TEST_FL_OFFLINE) { + set_bit(NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_close(ndev); + + for (i = 0; i < SELF_TEST_TPYE_NUM; i++) { + if (!st_param[i][1]) + continue; /* NEXT testing */ + + data[test_index] = __lb_up(ndev, + (enum hnae_loop)st_param[i][0]); + if (!data[test_index]) { + data[test_index] = __lb_run_test( + ndev, (enum hnae_loop)st_param[i][0]); + (void)__lb_down(ndev); + } + + if (data[test_index]) + eth_test->flags |= ETH_TEST_FL_FAILED; + + test_index++; + } + + hns_nic_net_reset(priv->netdev); + + clear_bit(NIC_STATE_TESTING, &priv->state); + + if (if_running) + (void)dev_open(ndev); + } + /* Online tests aren't run; pass by default */ + + (void)msleep_interruptible(4 * 1000); +} + +/** + * hns_nic_get_drvinfo - get net driver info + * @dev: net device + * @drvinfo: driver info + */ +static void hns_nic_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *drvinfo) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + + assert(priv); + + strncpy(drvinfo->version, HNAE_DRIVER_VERSION, + sizeof(drvinfo->version)); + drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; + + strncpy(drvinfo->driver, HNAE_DRIVER_NAME, sizeof(drvinfo->driver)); + drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + + strncpy(drvinfo->bus_info, priv->dev->bus->name, + sizeof(drvinfo->bus_info)); + drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; + + strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN); +} + +/** + * hns_get_ringparam - get ring parameter + * @dev: net device + * @param: ethtool parameter + */ +void hns_get_ringparam(struct net_device *net_dev, + struct ethtool_ringparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + struct hnae_queue *queue; + u32 uplimit = 0; + + queue = priv->ae_handle->qs[0]; + ops = priv->ae_handle->dev->ops; + + if (ops->get_ring_bdnum_limit) + ops->get_ring_bdnum_limit(queue, &uplimit); + + param->rx_max_pending = uplimit; + param->tx_max_pending = uplimit; + param->rx_pending = queue->rx_ring.desc_num; + param->tx_pending = queue->tx_ring.desc_num; +} + +/** + * hns_get_pauseparam - get pause parameter + * @dev: net device + * @param: pause parameter + */ +static void hns_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + ops = priv->ae_handle->dev->ops; + + if (ops->get_pauseparam) + ops->get_pauseparam(priv->ae_handle, ¶m->autoneg, + ¶m->rx_pause, ¶m->tx_pause); +} + +/** + * hns_set_pauseparam - set pause parameter + * @dev: net device + * @param: pause parameter + * + * Return 0 on success, negative on failure + */ +static int hns_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *param) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_handle *h; + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + h = priv->ae_handle; + ops = h->dev->ops; + + if (!ops->set_pauseparam) + return -ESRCH; + + return ops->set_pauseparam(priv->ae_handle, param->autoneg, + param->rx_pause, param->tx_pause); +} + +/** + * hns_get_coalesce - get coalesce info. + * @dev: net device + * @ec: coalesce info. + * + * Return 0 on success, negative on failure. + */ +static int hns_get_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *ec) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + ops = priv->ae_handle->dev->ops; + + ec->use_adaptive_rx_coalesce = 1; + ec->use_adaptive_tx_coalesce = 1; + + if ((!ops->get_coalesce_usecs) || + (!ops->get_rx_max_coalesced_frames)) + return -ESRCH; + + ops->get_coalesce_usecs(priv->ae_handle, + &ec->tx_coalesce_usecs, + &ec->rx_coalesce_usecs); + + ops->get_rx_max_coalesced_frames( + priv->ae_handle, + &ec->tx_max_coalesced_frames, + &ec->rx_max_coalesced_frames); + + return 0; +} + +/** + * hns_set_coalesce - set coalesce info. + * @dev: net device + * @ec: coalesce info. + * + * Return 0 on success, negative on failure. + */ +static int hns_set_coalesce(struct net_device *net_dev, + struct ethtool_coalesce *ec) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + int ret; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + + if (ec->tx_coalesce_usecs != ec->rx_coalesce_usecs) + return -EINVAL; + + if (ec->rx_max_coalesced_frames != ec->tx_max_coalesced_frames) + return -EINVAL; + + if ((!ops->set_coalesce_usecs) || + (!ops->set_coalesce_frames)) + return -ESRCH; + + ops->set_coalesce_usecs(priv->ae_handle, + ec->rx_coalesce_usecs); + + ret = ops->set_coalesce_frames( + priv->ae_handle, + ec->rx_max_coalesced_frames); + + return ret; +} + +/** + * hns_get_channels - get channel info. + * @dev: net device + * @ch: channel info. + */ +void hns_get_channels(struct net_device *net_dev, struct ethtool_channels *ch) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + + ch->max_rx = priv->ae_handle->q_num; + ch->max_tx = priv->ae_handle->q_num; + + ch->rx_count = priv->ae_handle->q_num; + ch->tx_count = priv->ae_handle->q_num; +} + +/** + * get_ethtool_stats - get detail statistics. + * @dev: net device + * @stats: statistics info. + * @data: statistics data. + */ +void hns_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + u64 *p = data; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + const struct rtnl_link_stats64 *net_stats; + struct rtnl_link_stats64 temp; + + if (!h->dev->ops->get_stats || !h->dev->ops->update_stats) { + netdev_err(netdev, "get_stats or update_stats is null!\n"); + return; + } + + h->dev->ops->update_stats(h, &netdev->stats); + + net_stats = dev_get_stats(netdev, &temp); + + /* get netdev statistics */ + p[0] = net_stats->rx_packets; + p[1] = net_stats->tx_packets; + p[2] = net_stats->rx_bytes; + p[3] = net_stats->tx_bytes; + p[4] = net_stats->rx_errors; + p[5] = net_stats->tx_errors; + p[6] = net_stats->rx_dropped; + p[7] = net_stats->tx_dropped; + p[8] = net_stats->multicast; + p[9] = net_stats->collisions; + p[10] = net_stats->rx_over_errors; + p[11] = net_stats->rx_crc_errors; + p[12] = net_stats->rx_frame_errors; + p[13] = net_stats->rx_fifo_errors; + p[14] = net_stats->rx_missed_errors; + p[15] = net_stats->tx_aborted_errors; + p[16] = net_stats->tx_carrier_errors; + p[17] = net_stats->tx_fifo_errors; + p[18] = net_stats->tx_heartbeat_errors; + p[19] = net_stats->rx_length_errors; + p[20] = net_stats->tx_window_errors; + p[21] = net_stats->rx_compressed; + p[22] = net_stats->tx_compressed; + + p[23] = netdev->rx_dropped.counter; + p[24] = netdev->tx_dropped.counter; + + p[25] = priv->tx_timeout_count; + + /* get driver statistics */ + h->dev->ops->get_stats(h, &p[26]); +} + +/** + * get_strings: Return a set of strings that describe the requested objects + * @dev: net device + * @stats: string set ID. + * @data: objects data. + */ +void hns_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + char *buff = (char *)data; + + if (!h->dev->ops->get_strings) { + netdev_err(netdev, "h->dev->ops->get_strings is null!\n"); + return; + } + + if (stringset == ETH_SS_TEST) { + if (priv->ae_handle->phy_if != PHY_INTERFACE_MODE_XGMII) { + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_MAC], + ETH_GSTRING_LEN); + buff += ETH_GSTRING_LEN; + } + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_SERDES], + ETH_GSTRING_LEN); + buff += ETH_GSTRING_LEN; + if ((priv->phy) && (!priv->phy->is_c45)) + memcpy(buff, hns_nic_test_strs[MAC_INTERNALLOOP_PHY], + ETH_GSTRING_LEN); + + } else { + snprintf(buff, ETH_GSTRING_LEN, "rx_packets"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_packets"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_bytes"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_bytes"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "multicast"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "collisions"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_over_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_crc_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_frame_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_fifo_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_missed_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_aborted_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_carrier_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_fifo_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_heartbeat_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_length_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_window_errors"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "rx_compressed"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "tx_compressed"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "netdev_rx_dropped"); + buff = buff + ETH_GSTRING_LEN; + snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_dropped"); + buff = buff + ETH_GSTRING_LEN; + + snprintf(buff, ETH_GSTRING_LEN, "netdev_tx_timeout"); + buff = buff + ETH_GSTRING_LEN; + + h->dev->ops->get_strings(h, stringset, (u8 *)buff); + } +} + +/** + * nic_get_sset_count - get string set count witch returned by nic_get_strings. + * @dev: net device + * @stringset: string set index, 0: self test string; 1: statistics string. + * + * Return string set count. + */ +int hns_get_sset_count(struct net_device *netdev, int stringset) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + + if (!ops->get_sset_count) { + netdev_err(netdev, "get_sset_count is null!\n"); + return -EOPNOTSUPP; + } + if (stringset == ETH_SS_TEST) { + u32 cnt = (sizeof(hns_nic_test_strs) / ETH_GSTRING_LEN); + + if (priv->ae_handle->phy_if == PHY_INTERFACE_MODE_XGMII) + cnt--; + + if ((!priv->phy) || (priv->phy->is_c45)) + cnt--; + + return cnt; + } else { + return (HNS_NET_STATS_CNT + ops->get_sset_count(h, stringset)); + } +} + +/** + * hns_phy_led_set - set phy LED status. + * @dev: net device + * @value: LED state. + * + * Return 0 on success, negative on failure. + */ +int hns_phy_led_set(struct net_device *netdev, int value) +{ + int retval; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy_dev = priv->phy; + + if (!phy_dev->bus) { + netdev_err(netdev, "phy_dev->bus is null!\n"); + return -EINVAL; + } + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED); + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_LED_FC_REG, + value); + retval = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER); + if (retval) { + netdev_err(netdev, "mdiobus_write fail !\n"); + return retval; + } + return 0; +} + +/** + * nic_set_phys_id - set phy identify LED. + * @dev: net device + * @state: LED state. + * + * Return 0 on success, negative on failure. + */ +int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + struct phy_device *phy_dev = priv->phy; + int ret; + + if (phy_dev) + switch (state) { + case ETHTOOL_ID_ACTIVE: + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); + if (ret) + return ret; + + priv->phy_led_val = (u16)mdiobus_read(phy_dev->bus, + phy_dev->addr, + HNS_LED_FC_REG); + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + if (ret) + return ret; + return 2; + case ETHTOOL_ID_ON: + ret = hns_phy_led_set(netdev, HNS_LED_FORCE_ON); + if (ret) + return ret; + break; + case ETHTOOL_ID_OFF: + ret = hns_phy_led_set(netdev, HNS_LED_FORCE_OFF); + if (ret) + return ret; + break; + case ETHTOOL_ID_INACTIVE: + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); + if (ret) + return ret; + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_LED_FC_REG, priv->phy_led_val); + if (ret) + return ret; + + ret = mdiobus_write(phy_dev->bus, phy_dev->addr, + HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + else + switch (state) { + case ETHTOOL_ID_ACTIVE: + return h->dev->ops->set_led_id(h, HNAE_LED_ACTIVE); + case ETHTOOL_ID_ON: + return h->dev->ops->set_led_id(h, HNAE_LED_ON); + case ETHTOOL_ID_OFF: + return h->dev->ops->set_led_id(h, HNAE_LED_OFF); + case ETHTOOL_ID_INACTIVE: + return h->dev->ops->set_led_id(h, HNAE_LED_INACTIVE); + default: + return -EINVAL; + } + + return 0; +} + +/** + * hns_get_regs - get net device register + * @dev: net device + * @cmd: ethtool cmd + * @date: register data + */ +void hns_get_regs(struct net_device *net_dev, struct ethtool_regs *cmd, + void *data) +{ + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + + cmd->version = HNS_CHIP_VERSION; + if (!ops->get_regs) { + netdev_err(net_dev, "ops->get_regs is null!\n"); + return; + } + ops->get_regs(priv->ae_handle, data); +} + +/** + * nic_get_regs_len - get total register len. + * @dev: net device + * + * Return total register len. + */ +static int hns_get_regs_len(struct net_device *net_dev) +{ + u32 reg_num; + struct hns_nic_priv *priv = netdev_priv(net_dev); + struct hnae_ae_ops *ops; + + assert(priv || priv->ae_handle); + + ops = priv->ae_handle->dev->ops; + if (!ops->get_regs_len) { + netdev_err(net_dev, "ops->get_regs_len is null!\n"); + return -EOPNOTSUPP; + } + + reg_num = ops->get_regs_len(priv->ae_handle); + if (reg_num > 0) + return reg_num * sizeof(u32); + else + return reg_num; /* error code */ +} + +/** + * hns_nic_nway_reset - nway reset + * @dev: net device + * + * Return 0 on success, negative on failure + */ +static int hns_nic_nway_reset(struct net_device *netdev) +{ + int ret = 0; + struct hns_nic_priv *priv = netdev_priv(netdev); + struct phy_device *phy = priv->phy; + + if (netif_running(netdev)) { + if (phy) + ret = genphy_restart_aneg(phy); + } + + return ret; +} + +static struct ethtool_ops hns_ethtool_ops = { + .get_drvinfo = hns_nic_get_drvinfo, + .get_link = hns_nic_get_link, + .get_settings = hns_nic_get_settings, + .set_settings = hns_nic_set_settings, + .get_ringparam = hns_get_ringparam, + .get_pauseparam = hns_get_pauseparam, + .set_pauseparam = hns_set_pauseparam, + .get_coalesce = hns_get_coalesce, + .set_coalesce = hns_set_coalesce, + .get_channels = hns_get_channels, + .self_test = hns_nic_self_test, + .get_strings = hns_get_strings, + .get_sset_count = hns_get_sset_count, + .get_ethtool_stats = hns_get_ethtool_stats, + .set_phys_id = hns_set_phys_id, + .get_regs_len = hns_get_regs_len, + .get_regs = hns_get_regs, + .nway_reset = hns_nic_nway_reset, +}; + +void hns_ethtool_set_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &hns_ethtool_ops; +} diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c new file mode 100644 index 000000000000..e4ec52ae61ff --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2014-2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/netdevice.h> +#include <linux/of_address.h> +#include <linux/of.h> +#include <linux/of_mdio.h> +#include <linux/of_platform.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <linux/spinlock_types.h> + +#define MDIO_DRV_NAME "Hi-HNS_MDIO" +#define MDIO_BUS_NAME "Hisilicon MII Bus" +#define MDIO_DRV_VERSION "1.3.0" +#define MDIO_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." +#define MDIO_DRV_STRING MDIO_BUS_NAME +#define MDIO_DEFAULT_DEVICE_DESCR MDIO_BUS_NAME + +#define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) +#define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) + +#define MDIO_TIMEOUT 1000000 + +struct hns_mdio_device { + void *vbase; /* mdio reg base address */ + void *sys_vbase; +}; + +/* mdio reg */ +#define MDIO_COMMAND_REG 0x0 +#define MDIO_ADDR_REG 0x4 +#define MDIO_WDATA_REG 0x8 +#define MDIO_RDATA_REG 0xc +#define MDIO_STA_REG 0x10 + +/* cfg phy bit map */ +#define MDIO_CMD_DEVAD_M 0x1f +#define MDIO_CMD_DEVAD_S 0 +#define MDIO_CMD_PRTAD_M 0x1f +#define MDIO_CMD_PRTAD_S 5 +#define MDIO_CMD_OP_M 0x3 +#define MDIO_CMD_OP_S 10 +#define MDIO_CMD_ST_M 0x3 +#define MDIO_CMD_ST_S 12 +#define MDIO_CMD_START_B 14 + +#define MDIO_ADDR_DATA_M 0xffff +#define MDIO_ADDR_DATA_S 0 + +#define MDIO_WDATA_DATA_M 0xffff +#define MDIO_WDATA_DATA_S 0 + +#define MDIO_RDATA_DATA_M 0xffff +#define MDIO_RDATA_DATA_S 0 + +#define MDIO_STATE_STA_B 0 + +enum mdio_st_clause { + MDIO_ST_CLAUSE_45 = 0, + MDIO_ST_CLAUSE_22 +}; + +enum mdio_c22_op_seq { + MDIO_C22_WRITE = 1, + MDIO_C22_READ = 2 +}; + +enum mdio_c45_op_seq { + MDIO_C45_WRITE_ADDR = 0, + MDIO_C45_WRITE_DATA, + MDIO_C45_READ_INCREMENT, + MDIO_C45_READ +}; + +/* peri subctrl reg */ +#define MDIO_SC_CLK_EN 0x338 +#define MDIO_SC_CLK_DIS 0x33C +#define MDIO_SC_RESET_REQ 0xA38 +#define MDIO_SC_RESET_DREQ 0xA3C +#define MDIO_SC_CTRL 0x2010 +#define MDIO_SC_CLK_ST 0x531C +#define MDIO_SC_RESET_ST 0x5A1C + +static void mdio_write_reg(void *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = (u8 __iomem *)base; + + writel_relaxed(value, reg_addr + reg); +} + +#define MDIO_WRITE_REG(a, reg, value) \ + mdio_write_reg((a)->vbase, (reg), (value)) + +static u32 mdio_read_reg(void *base, u32 reg) +{ + u8 __iomem *reg_addr = (u8 __iomem *)base; + + return readl_relaxed(reg_addr + reg); +} + +#define mdio_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~((mask) << (shift))); \ + (origin) |= (((val) & (mask)) << (shift)); \ + } while (0) + +#define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask)) + +static void mdio_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, + u32 val) +{ + u32 origin = mdio_read_reg(base, reg); + + mdio_set_field(origin, mask, shift, val); + mdio_write_reg(base, reg, origin); +} + +#define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) \ + mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val)) + +static u32 mdio_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) +{ + u32 origin; + + origin = mdio_read_reg(base, reg); + return mdio_get_field(origin, mask, shift); +} + +#define MDIO_GET_REG_FIELD(dev, reg, mask, shift) \ + mdio_get_reg_field((dev)->vbase, (reg), (mask), (shift)) + +#define MDIO_GET_REG_BIT(dev, reg, bit) \ + mdio_get_reg_field((dev)->vbase, (reg), 0x1ull, (bit)) + +#define MDIO_CHECK_SET_ST 1 +#define MDIO_CHECK_CLR_ST 0 + +static int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev, + u32 cfg_reg, u32 set_val, + u32 st_reg, u32 st_msk, u8 check_st) +{ + u32 time_cnt; + u32 reg_value; + + mdio_write_reg((void *)mdio_dev->sys_vbase, cfg_reg, set_val); + + for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) { + reg_value = mdio_read_reg((void *)mdio_dev->sys_vbase, st_reg); + reg_value &= st_msk; + if ((!!check_st) == (!!reg_value)) + break; + } + + if ((!!check_st) != (!!reg_value)) + return -EBUSY; + + return 0; +} + +static int hns_mdio_wait_ready(struct mii_bus *bus) +{ + struct hns_mdio_device *mdio_dev = bus->priv; + int i; + u32 cmd_reg_value = 1; + + /* waitting for MDIO_COMMAND_REG 's mdio_start==0 */ + /* after that can do read or write*/ + for (i = 0; cmd_reg_value; i++) { + cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev, + MDIO_COMMAND_REG, + MDIO_CMD_START_B); + if (i == MDIO_TIMEOUT) + return -ETIMEDOUT; + } + + return 0; +} + +static void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev, + u8 is_c45, u8 op, u8 phy_id, u16 cmd) +{ + u32 cmd_reg_value; + u8 st = is_c45 ? MDIO_ST_CLAUSE_45 : MDIO_ST_CLAUSE_22; + + cmd_reg_value = st << MDIO_CMD_ST_S; + cmd_reg_value |= op << MDIO_CMD_OP_S; + cmd_reg_value |= + (phy_id & MDIO_CMD_PRTAD_M) << MDIO_CMD_PRTAD_S; + cmd_reg_value |= (cmd & MDIO_CMD_DEVAD_M) << MDIO_CMD_DEVAD_S; + cmd_reg_value |= 1 << MDIO_CMD_START_B; + + MDIO_WRITE_REG(mdio_dev, MDIO_COMMAND_REG, cmd_reg_value); +} + +/** + * hns_mdio_write - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @regnum: register num + * @value: register value + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_write(struct mii_bus *bus, + int phy_id, int regnum, u16 data) +{ + int ret; + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + u8 devad = ((regnum >> 16) & 0x1f); + u8 is_c45 = !!(regnum & MII_ADDR_C45); + u16 reg = (u16)(regnum & 0xffff); + u8 op; + u16 cmd_reg_cfg; + + dev_dbg(&bus->dev, "mdio write %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x, write data=%d\n", + phy_id, is_c45, devad, reg, data); + + /* wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + if (!is_c45) { + cmd_reg_cfg = reg; + op = MDIO_C22_WRITE; + } else { + /* config the cmd-reg to write addr*/ + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + /* config the data needed writing */ + cmd_reg_cfg = devad; + op = MDIO_C45_WRITE_ADDR; + } + + MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, + MDIO_WDATA_DATA_S, data); + + hns_mdio_cmd_write(mdio_dev, is_c45, op, phy_id, cmd_reg_cfg); + + return 0; +} + +/** + * hns_mdio_read - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @regnum: register num + * @value: register value + * + * Return phy register value + */ +static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +{ + int ret; + u16 reg_val = 0; + u8 devad = ((regnum >> 16) & 0x1f); + u8 is_c45 = !!(regnum & MII_ADDR_C45); + u16 reg = (u16)(regnum & 0xffff); + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + + dev_dbg(&bus->dev, "mdio read %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x!\n", + phy_id, is_c45, devad, reg); + + /* Step 1: wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + if (!is_c45) { + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C22_READ, phy_id, reg); + } else { + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + /* Step 2; config the cmd-reg to write addr*/ + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* Step 3: check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + hns_mdio_cmd_write(mdio_dev, is_c45, + MDIO_C45_WRITE_ADDR, phy_id, devad); + } + + /* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/ + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B); + if (reg_val) { + dev_err(&bus->dev, " ERROR! MDIO Read failed!\n"); + return -EBUSY; + } + + /* Step 6; get out data*/ + reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG, + MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S); + + return reg_val; +} + +/** + * hns_mdio_reset - reset mdio bus + * @bus: mdio bus + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_reset(struct mii_bus *bus) +{ + struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + int ret; + + if (!mdio_dev->sys_vbase) { + dev_err(&bus->dev, "mdio sys ctl reg has not maped\n"); + return -ENODEV; + } + + /*1. reset req, and read reset st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_REQ, 0x1, + MDIO_SC_RESET_ST, 0x1, + MDIO_CHECK_SET_ST); + if (ret) { + dev_err(&bus->dev, "MDIO reset fail\n"); + return ret; + } + + /*2. dis clk, and read clk st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_DIS, + 0x1, MDIO_SC_CLK_ST, 0x1, + MDIO_CHECK_CLR_ST); + if (ret) { + dev_err(&bus->dev, "MDIO dis clk fail\n"); + return ret; + } + + /*3. reset dreq, and read reset st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_RESET_DREQ, 0x1, + MDIO_SC_RESET_ST, 0x1, + MDIO_CHECK_CLR_ST); + if (ret) { + dev_err(&bus->dev, "MDIO dis clk fail\n"); + return ret; + } + + /*4. en clk, and read clk st check*/ + ret = mdio_sc_cfg_reg_write(mdio_dev, MDIO_SC_CLK_EN, + 0x1, MDIO_SC_CLK_ST, 0x1, + MDIO_CHECK_SET_ST); + if (ret) + dev_err(&bus->dev, "MDIO en clk fail\n"); + + return ret; +} + +/** + * hns_mdio_bus_name - get mdio bus name + * @name: mdio bus name + * @np: mdio device node pointer + */ +static void hns_mdio_bus_name(char *name, struct device_node *np) +{ + const u32 *addr; + u64 taddr = OF_BAD_ADDR; + + addr = of_get_address(np, 0, NULL, NULL); + if (addr) + taddr = of_translate_address(np, addr); + + snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name, + (unsigned long long)taddr); +} + +/** + * hns_mdio_probe - probe mdio device + * @pdev: mdio platform device + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_probe(struct platform_device *pdev) +{ + struct device_node *np; + struct hns_mdio_device *mdio_dev; + struct mii_bus *new_bus; + struct resource *res; + int ret; + + if (!pdev) { + dev_err(NULL, "pdev is NULL!\r\n"); + return -ENODEV; + } + np = pdev->dev.of_node; + mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL); + if (!mdio_dev) + return -ENOMEM; + + new_bus = devm_mdiobus_alloc(&pdev->dev); + if (!new_bus) { + dev_err(&pdev->dev, "mdiobus_alloc fail!\n"); + return -ENOMEM; + } + + new_bus->name = MDIO_BUS_NAME; + new_bus->read = hns_mdio_read; + new_bus->write = hns_mdio_write; + new_bus->reset = hns_mdio_reset; + new_bus->priv = mdio_dev; + hns_mdio_bus_name(new_bus->id, np); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdio_dev->vbase)) { + ret = PTR_ERR(mdio_dev->vbase); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + mdio_dev->sys_vbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mdio_dev->sys_vbase)) { + ret = PTR_ERR(mdio_dev->sys_vbase); + return ret; + } + + new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR, + sizeof(int), GFP_KERNEL); + if (!new_bus->irq) + return -ENOMEM; + + new_bus->parent = &pdev->dev; + platform_set_drvdata(pdev, new_bus); + + ret = of_mdiobus_register(new_bus, np); + if (ret) { + dev_err(&pdev->dev, "Cannot register as MDIO bus!\n"); + platform_set_drvdata(pdev, NULL); + return ret; + } + + return 0; +} + +/** + * hns_mdio_remove - remove mdio device + * @pdev: mdio platform device + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_remove(struct platform_device *pdev) +{ + struct mii_bus *bus; + + bus = platform_get_drvdata(pdev); + + mdiobus_unregister(bus); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id hns_mdio_match[] = { + {.compatible = "hisilicon,mdio"}, + {.compatible = "hisilicon,hns-mdio"}, + {} +}; + +static struct platform_driver hns_mdio_driver = { + .probe = hns_mdio_probe, + .remove = hns_mdio_remove, + .driver = { + .name = MDIO_DRV_NAME, + .of_match_table = hns_mdio_match, + }, +}; + +module_platform_driver(hns_mdio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("Hisilicon HNS MDIO driver"); +MODULE_ALIAS("platform:" MDIO_DRV_NAME); diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index b60a34d982a9..5d7db6c01c46 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2204,7 +2204,6 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %s", dev->cell_index, dev->ofdev->dev.of_node->full_name); - info->regdump_len = emac_ethtool_get_regs_len(ndev); } static const struct ethtool_ops emac_ethtool_ops = { diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index ac02c675c59c..93ae11494810 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -181,7 +181,7 @@ struct emac_instance { struct mal_commac commac; /* PHY infos */ - u32 phy_mode; + int phy_mode; u32 phy_map; u32 phy_address; u32 phy_feat_exc; diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index 4270ad2d4ddf..83e557c7f279 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -559,8 +559,6 @@ static void e1000_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = e1000_get_regs_len(netdev); - drvinfo->eedump_len = e1000_get_eeprom_len(netdev); } static void e1000_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index 45c8c864104e..b1af0d613caa 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -3900,10 +3900,6 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, return E1000_SUCCESS; } - /* If eeprom is not yet detected, do so now */ - if (eeprom->word_size == 0) - e1000_init_eeprom_params(hw); - /* A check for invalid values: offset too large, too many words, and * not enough words. */ @@ -4074,10 +4070,6 @@ static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, return E1000_SUCCESS; } - /* If eeprom is not yet detected, do so now */ - if (eeprom->word_size == 0) - e1000_init_eeprom_params(hw); - /* A check for invalid values: offset too large, too many words, and * not enough words. */ diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 74dc15055971..fd7be860c201 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -3820,7 +3820,7 @@ static int e1000_clean(struct napi_struct *napi, int budget) if (work_done < budget) { if (likely(adapter->itr_setting & 3)) e1000_set_itr(adapter); - napi_complete(napi); + napi_complete_done(napi, work_done); if (!test_bit(__E1000_DOWN, &adapter->flags)) e1000_irq_enable(adapter); } diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index ad6daa656d3e..6cab1f30d41e 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -648,8 +648,6 @@ static void e1000_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = e1000_get_regs_len(netdev); - drvinfo->eedump_len = e1000_get_eeprom_len(netdev); } static void e1000_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index faf4b3f3d0b5..0a854a47d31a 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -2693,7 +2693,7 @@ static int e1000e_poll(struct napi_struct *napi, int weight) if (work_done < weight) { if (adapter->itr_setting & 3) e1000_set_itr(adapter); - napi_complete(napi); + napi_complete_done(napi, work_done); if (!test_bit(__E1000_DOWN, &adapter->state)) { if (adapter->msix_entries) ew32(IMS, adapter->rx_ring->ims_val); @@ -6952,6 +6952,7 @@ static const struct net_device_ops e1000e_netdev_ops = { #endif .ndo_set_features = e1000_set_features, .ndo_fix_features = e1000_fix_features, + .ndo_features_check = passthru_features_check, }; /** diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index c8c8c5baefda..14440200499b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -101,12 +101,19 @@ struct fm10k_tx_queue_stats { u64 csum_err; u64 tx_busy; u64 tx_done_old; + u64 csum_good; }; struct fm10k_rx_queue_stats { u64 alloc_failed; u64 csum_err; u64 errors; + u64 csum_good; + u64 switch_errors; + u64 drops; + u64 pp_errors; + u64 link_errors; + u64 length_errors; }; struct fm10k_ring { @@ -251,6 +258,7 @@ struct fm10k_intfc { #define FM10K_FLAG_RSS_FIELD_IPV6_UDP (u32)(1 << 2) #define FM10K_FLAG_RX_TS_ENABLED (u32)(1 << 3) #define FM10K_FLAG_SWPRI_CONFIG (u32)(1 << 4) +#define FM10K_FLAG_DEBUG_STATS (u32)(1 << 5) int xcast_mode; /* Tx fast path data */ @@ -277,6 +285,17 @@ struct fm10k_intfc { u64 rx_drops_nic; u64 rx_overrun_pf; u64 rx_overrun_vf; + + /* Debug Statistics */ + u64 hw_sm_mbx_full; + u64 hw_csum_tx_good; + u64 hw_csum_rx_good; + u64 rx_switch_errors; + u64 rx_drops; + u64 rx_pp_errors; + u64 rx_link_errors; + u64 rx_length_errors; + u32 tx_timeout_count; /* RX */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c index f45b4d71adb8..5304bc1fbecd 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c @@ -37,7 +37,8 @@ static void *fm10k_dbg_desc_seq_start(struct seq_file *s, loff_t *pos) } static void *fm10k_dbg_desc_seq_next(struct seq_file *s, - void __always_unused *v, loff_t *pos) + void __always_unused *v, + loff_t *pos) { struct fm10k_ring *ring = s->private; @@ -45,7 +46,7 @@ static void *fm10k_dbg_desc_seq_next(struct seq_file *s, } static void fm10k_dbg_desc_seq_stop(struct seq_file __always_unused *s, - __always_unused void *v) + void __always_unused *v) { /* Do nothing. */ } @@ -175,7 +176,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) return; /* Generate a folder for each q_vector */ - sprintf(name, "q_vector.%03d", q_vector->v_idx); + snprintf(name, sizeof(name), "q_vector.%03d", q_vector->v_idx); q_vector->dbg_q_vector = debugfs_create_dir(name, interface->dbg_intfc); if (!q_vector->dbg_q_vector) @@ -185,7 +186,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) for (i = 0; i < q_vector->tx.count; i++) { struct fm10k_ring *ring = &q_vector->tx.ring[i]; - sprintf(name, "tx_ring.%03d", ring->queue_index); + snprintf(name, sizeof(name), "tx_ring.%03d", ring->queue_index); debugfs_create_file(name, 0600, q_vector->dbg_q_vector, ring, @@ -196,7 +197,7 @@ void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) for (i = 0; i < q_vector->rx.count; i++) { struct fm10k_ring *ring = &q_vector->rx.ring[i]; - sprintf(name, "rx_ring.%03d", ring->queue_index); + snprintf(name, sizeof(name), "rx_ring.%03d", ring->queue_index); debugfs_create_file(name, 0600, q_vector->dbg_q_vector, ring, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index c6dc9683429e..2ce0eba5e040 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -76,19 +76,22 @@ static const struct fm10k_stats fm10k_gstrings_global_stats[] = { FM10K_STAT("mac_rules_used", hw.swapi.mac.used), FM10K_STAT("mac_rules_avail", hw.swapi.mac.avail), - FM10K_STAT("mbx_tx_busy", hw.mbx.tx_busy), - FM10K_STAT("mbx_tx_oversized", hw.mbx.tx_dropped), - FM10K_STAT("mbx_tx_messages", hw.mbx.tx_messages), - FM10K_STAT("mbx_tx_dwords", hw.mbx.tx_dwords), - FM10K_STAT("mbx_rx_messages", hw.mbx.rx_messages), - FM10K_STAT("mbx_rx_dwords", hw.mbx.rx_dwords), - FM10K_STAT("mbx_rx_parse_err", hw.mbx.rx_parse_err), - FM10K_STAT("tx_hang_count", tx_timeout_count), FM10K_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts), }; +static const struct fm10k_stats fm10k_gstrings_debug_stats[] = { + FM10K_STAT("hw_sm_mbx_full", hw_sm_mbx_full), + FM10K_STAT("hw_csum_tx_good", hw_csum_tx_good), + FM10K_STAT("hw_csum_rx_good", hw_csum_rx_good), + FM10K_STAT("rx_switch_errors", rx_switch_errors), + FM10K_STAT("rx_drops", rx_drops), + FM10K_STAT("rx_pp_errors", rx_pp_errors), + FM10K_STAT("rx_link_errors", rx_link_errors), + FM10K_STAT("rx_length_errors", rx_length_errors), +}; + static const struct fm10k_stats fm10k_gstrings_pf_stats[] = { FM10K_STAT("timeout", stats.timeout.count), FM10K_STAT("ur", stats.ur.count), @@ -100,14 +103,33 @@ static const struct fm10k_stats fm10k_gstrings_pf_stats[] = { FM10K_STAT("nodesc_drop", stats.nodesc_drop.count), }; +#define FM10K_MBX_STAT(_name, _stat) { \ + .stat_string = _name, \ + .sizeof_stat = FIELD_SIZEOF(struct fm10k_mbx_info, _stat), \ + .stat_offset = offsetof(struct fm10k_mbx_info, _stat) \ +} + +static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = { + FM10K_MBX_STAT("mbx_tx_busy", tx_busy), + FM10K_MBX_STAT("mbx_tx_oversized", tx_dropped), + FM10K_MBX_STAT("mbx_tx_messages", tx_messages), + FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords), + FM10K_MBX_STAT("mbx_rx_messages", rx_messages), + FM10K_MBX_STAT("mbx_rx_dwords", rx_dwords), + FM10K_MBX_STAT("mbx_rx_parse_err", rx_parse_err), +}; + #define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_global_stats) +#define FM10K_DEBUG_STATS_LEN ARRAY_SIZE(fm10k_gstrings_debug_stats) #define FM10K_PF_STATS_LEN ARRAY_SIZE(fm10k_gstrings_pf_stats) +#define FM10K_MBX_STATS_LEN ARRAY_SIZE(fm10k_gstrings_mbx_stats) #define FM10K_QUEUE_STATS_LEN(_n) \ ( (_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64))) #define FM10K_STATIC_STATS_LEN (FM10K_GLOBAL_STATS_LEN + \ - FM10K_NETDEV_STATS_LEN) + FM10K_NETDEV_STATS_LEN + \ + FM10K_MBX_STATS_LEN) static const char fm10k_gstrings_test[][ETH_GSTRING_LEN] = { "Mailbox test (on/offline)" @@ -120,47 +142,97 @@ enum fm10k_self_test_types { FM10K_TEST_MAX = FM10K_TEST_LEN }; -static void fm10k_get_strings(struct net_device *dev, u32 stringset, u8 *data) +enum { + FM10K_PRV_FLAG_DEBUG_STATS, + FM10K_PRV_FLAG_LEN, +}; + +static const char fm10k_prv_flags[FM10K_PRV_FLAG_LEN][ETH_GSTRING_LEN] = { + "debug-statistics", +}; + +static void fm10k_get_stat_strings(struct net_device *dev, u8 *data) { struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_iov_data *iov_data = interface->iov_data; char *p = (char *)data; unsigned int i; + unsigned int j; - switch (stringset) { - case ETH_SS_TEST: - memcpy(data, *fm10k_gstrings_test, - FM10K_TEST_LEN * ETH_GSTRING_LEN); - break; - case ETH_SS_STATS: - for (i = 0; i < FM10K_NETDEV_STATS_LEN; i++) { - memcpy(p, fm10k_gstrings_net_stats[i].stat_string, + for (i = 0; i < FM10K_NETDEV_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_net_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + for (i = 0; i < FM10K_GLOBAL_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_global_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + if (interface->flags & FM10K_FLAG_DEBUG_STATS) { + for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_debug_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - for (i = 0; i < FM10K_GLOBAL_STATS_LEN; i++) { - memcpy(p, fm10k_gstrings_global_stats[i].stat_string, + } + + for (i = 0; i < FM10K_MBX_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_mbx_stats[i].stat_string, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + if (interface->hw.mac.type != fm10k_mac_vf) { + for (i = 0; i < FM10K_PF_STATS_LEN; i++) { + memcpy(p, fm10k_gstrings_pf_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } + } - if (interface->hw.mac.type != fm10k_mac_vf) { - for (i = 0; i < FM10K_PF_STATS_LEN; i++) { - memcpy(p, fm10k_gstrings_pf_stats[i].stat_string, - ETH_GSTRING_LEN); + if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) { + for (i = 0; i < iov_data->num_vfs; i++) { + for (j = 0; j < FM10K_MBX_STATS_LEN; j++) { + snprintf(p, + ETH_GSTRING_LEN, + "vf_%u_%s", i, + fm10k_gstrings_mbx_stats[j].stat_string); p += ETH_GSTRING_LEN; } } + } - for (i = 0; i < interface->hw.mac.max_queues; i++) { - sprintf(p, "tx_queue_%u_packets", i); - p += ETH_GSTRING_LEN; - sprintf(p, "tx_queue_%u_bytes", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_packets", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_bytes", i); - p += ETH_GSTRING_LEN; - } + for (i = 0; i < interface->hw.mac.max_queues; i++) { + snprintf(p, ETH_GSTRING_LEN, "tx_queue_%u_packets", i); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "tx_queue_%u_bytes", i); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "rx_queue_%u_packets", i); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "rx_queue_%u_bytes", i); + p += ETH_GSTRING_LEN; + } +} + +static void fm10k_get_strings(struct net_device *dev, + u32 stringset, u8 *data) +{ + char *p = (char *)data; + + switch (stringset) { + case ETH_SS_TEST: + memcpy(data, *fm10k_gstrings_test, + FM10K_TEST_LEN * ETH_GSTRING_LEN); + break; + case ETH_SS_STATS: + fm10k_get_stat_strings(dev, data); + break; + case ETH_SS_PRIV_FLAGS: + memcpy(p, fm10k_prv_flags, + FM10K_PRV_FLAG_LEN * ETH_GSTRING_LEN); break; } } @@ -168,6 +240,7 @@ static void fm10k_get_strings(struct net_device *dev, u32 stringset, u8 *data) static int fm10k_get_sset_count(struct net_device *dev, int sset) { struct fm10k_intfc *interface = netdev_priv(dev); + struct fm10k_iov_data *iov_data = interface->iov_data; struct fm10k_hw *hw = &interface->hw; int stats_len = FM10K_STATIC_STATS_LEN; @@ -180,7 +253,16 @@ static int fm10k_get_sset_count(struct net_device *dev, int sset) if (hw->mac.type != fm10k_mac_vf) stats_len += FM10K_PF_STATS_LEN; + if (interface->flags & FM10K_FLAG_DEBUG_STATS) { + stats_len += FM10K_DEBUG_STATS_LEN; + + if (iov_data) + stats_len += FM10K_MBX_STATS_LEN * iov_data->num_vfs; + } + return stats_len; + case ETH_SS_PRIV_FLAGS: + return FM10K_PRV_FLAG_LEN; default: return -EOPNOTSUPP; } @@ -192,6 +274,7 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev, { const int stat_count = sizeof(struct fm10k_queue_stats) / sizeof(u64); struct fm10k_intfc *interface = netdev_priv(netdev); + struct fm10k_iov_data *iov_data = interface->iov_data; struct net_device_stats *net_stats = &netdev->stats; char *p; int i, j; @@ -211,13 +294,47 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev, sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } - if (interface->hw.mac.type != fm10k_mac_vf) + if (interface->flags & FM10K_FLAG_DEBUG_STATS) { + for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) { + p = (char *)interface + fm10k_gstrings_debug_stats[i].stat_offset; + *(data++) = (fm10k_gstrings_debug_stats[i].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + } + + for (i = 0; i < FM10K_MBX_STATS_LEN; i++) { + p = (char *)&interface->hw.mbx + fm10k_gstrings_mbx_stats[i].stat_offset; + *(data++) = (fm10k_gstrings_mbx_stats[i].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + + if (interface->hw.mac.type != fm10k_mac_vf) { for (i = 0; i < FM10K_PF_STATS_LEN; i++) { p = (char *)interface + fm10k_gstrings_pf_stats[i].stat_offset; *(data++) = (fm10k_gstrings_pf_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } + } + + if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) { + for (i = 0; i < iov_data->num_vfs; i++) { + struct fm10k_vf_info *vf_info; + vf_info = &iov_data->vf_info[i]; + + /* skip stats if we don't have a vf info */ + if (!vf_info) { + data += FM10K_MBX_STATS_LEN; + continue; + } + + for (j = 0; j < FM10K_MBX_STATS_LEN; j++) { + p = (char *)&vf_info->mbx + fm10k_gstrings_mbx_stats[j].stat_offset; + *(data++) = (fm10k_gstrings_mbx_stats[j].sizeof_stat == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + } + } for (i = 0; i < interface->hw.mac.max_queues; i++) { struct fm10k_ring *ring; @@ -398,10 +515,6 @@ static void fm10k_get_drvinfo(struct net_device *dev, sizeof(info->version) - 1); strncpy(info->bus_info, pci_name(interface->pdev), sizeof(info->bus_info) - 1); - - info->n_stats = fm10k_get_sset_count(dev, ETH_SS_STATS); - - info->regdump_len = fm10k_get_regs_len(dev); } static void fm10k_get_pauseparam(struct net_device *dev, @@ -881,6 +994,33 @@ static void fm10k_self_test(struct net_device *dev, eth_test->flags |= ETH_TEST_FL_FAILED; } +static u32 fm10k_get_priv_flags(struct net_device *netdev) +{ + struct fm10k_intfc *interface = netdev_priv(netdev); + u32 priv_flags = 0; + + if (interface->flags & FM10K_FLAG_DEBUG_STATS) + priv_flags |= 1 << FM10K_PRV_FLAG_DEBUG_STATS; + + return priv_flags; +} + +static int fm10k_set_priv_flags(struct net_device *netdev, u32 priv_flags) +{ + struct fm10k_intfc *interface = netdev_priv(netdev); + + if (priv_flags >= (1 << FM10K_PRV_FLAG_LEN)) + return -EINVAL; + + if (priv_flags & (1 << FM10K_PRV_FLAG_DEBUG_STATS)) + interface->flags |= FM10K_FLAG_DEBUG_STATS; + else + interface->flags &= ~FM10K_FLAG_DEBUG_STATS; + + return 0; +} + + static u32 fm10k_get_reta_size(struct net_device __always_unused *netdev) { return FM10K_RETA_SIZE * FM10K_RETA_ENTRIES_PER_REG; @@ -1094,6 +1234,8 @@ static const struct ethtool_ops fm10k_ethtool_ops = { .get_regs = fm10k_get_regs, .get_regs_len = fm10k_get_regs_len, .self_test = fm10k_self_test, + .get_priv_flags = fm10k_get_priv_flags, + .set_priv_flags = fm10k_set_priv_flags, .get_rxfh_indir_size = fm10k_get_reta_size, .get_rxfh_key_size = fm10k_get_rssrk_size, .get_rxfh = fm10k_get_rssh, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c index 94571e6e790c..acfb8b1f88a7 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_iov.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_iov.c @@ -137,8 +137,11 @@ process_mbx: } /* guarantee we have free space in the SM mailbox */ - if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU)) + if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU)) { + /* keep track of how many times this occurs */ + interface->hw_sm_mbx_full++; break; + } /* cleanup mailbox and process received messages */ mbx->ops.process(hw, mbx); @@ -228,9 +231,6 @@ int fm10k_iov_resume(struct pci_dev *pdev) hw->iov.ops.set_lport(hw, vf_info, i, FM10K_VF_FLAG_MULTI_CAPABLE); - /* assign our default vid to the VF following reset */ - vf_info->sw_vid = hw->mac.default_vid; - /* mailbox is disconnected so we don't send a message */ hw->iov.ops.assign_default_mac_vlan(hw, vf_info); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index b5b2925103ec..e76a44cf330c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -398,6 +398,8 @@ static inline void fm10k_rx_checksum(struct fm10k_ring *ring, return; skb->ip_summed = CHECKSUM_UNNECESSARY; + + ring->rx_stats.csum_good++; } #define FM10K_RSS_L4_TYPES_MASK \ @@ -497,8 +499,11 @@ static unsigned int fm10k_process_skb_fields(struct fm10k_ring *rx_ring, if (rx_desc->w.vlan) { u16 vid = le16_to_cpu(rx_desc->w.vlan); - if (vid != rx_ring->vid) + if ((vid & VLAN_VID_MASK) != rx_ring->vid) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); + else if (vid & VLAN_PRIO_MASK) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + vid & VLAN_PRIO_MASK); } fm10k_type_trans(rx_ring, rx_desc, skb); @@ -553,6 +558,18 @@ static bool fm10k_cleanup_headers(struct fm10k_ring *rx_ring, { if (unlikely((fm10k_test_staterr(rx_desc, FM10K_RXD_STATUS_RXE)))) { +#define FM10K_TEST_RXD_BIT(rxd, bit) \ + ((rxd)->w.csum_err & cpu_to_le16(bit)) + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_SWITCH_ERROR)) + rx_ring->rx_stats.switch_errors++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_NO_DESCRIPTOR)) + rx_ring->rx_stats.drops++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_PP_ERROR)) + rx_ring->rx_stats.pp_errors++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_SWITCH_READY)) + rx_ring->rx_stats.link_errors++; + if (FM10K_TEST_RXD_BIT(rx_desc, FM10K_RXD_ERR_TOO_BIG)) + rx_ring->rx_stats.length_errors++; dev_kfree_skb_any(skb); rx_ring->rx_stats.errors++; return true; @@ -576,9 +593,9 @@ static void fm10k_receive_skb(struct fm10k_q_vector *q_vector, napi_gro_receive(&q_vector->napi, skb); } -static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector, - struct fm10k_ring *rx_ring, - int budget) +static int fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector, + struct fm10k_ring *rx_ring, + int budget) { struct sk_buff *skb = rx_ring->skb; unsigned int total_bytes = 0, total_packets = 0; @@ -645,7 +662,7 @@ static bool fm10k_clean_rx_irq(struct fm10k_q_vector *q_vector, q_vector->rx.total_packets += total_packets; q_vector->rx.total_bytes += total_bytes; - return total_packets < budget; + return total_packets; } #define VXLAN_HLEN (sizeof(struct udphdr) + 8) @@ -878,6 +895,7 @@ static void fm10k_tx_csum(struct fm10k_ring *tx_ring, /* update TX checksum flag */ first->tx_flags |= FM10K_TX_FLAGS_CSUM; + tx_ring->tx_stats.csum_good++; no_csum: /* populate Tx descriptor header size and mss */ @@ -1079,9 +1097,7 @@ netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb, struct fm10k_tx_buffer *first; int tso; u32 tx_flags = 0; -#if PAGE_SIZE > FM10K_MAX_DATA_PER_TXD unsigned short f; -#endif u16 count = TXD_USE_COUNT(skb_headlen(skb)); /* need: 1 descriptor per page * PAGE_SIZE/FM10K_MAX_DATA_PER_TXD, @@ -1089,12 +1105,9 @@ netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb, * + 2 desc gap to keep tail from touching head * otherwise try next time */ -#if PAGE_SIZE > FM10K_MAX_DATA_PER_TXD for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size); -#else - count += skb_shinfo(skb)->nr_frags; -#endif + if (fm10k_maybe_stop_tx(tx_ring, count + 3)) { tx_ring->tx_stats.tx_busy++; return NETDEV_TX_BUSY; @@ -1409,7 +1422,7 @@ static int fm10k_poll(struct napi_struct *napi, int budget) struct fm10k_q_vector *q_vector = container_of(napi, struct fm10k_q_vector, napi); struct fm10k_ring *ring; - int per_ring_budget; + int per_ring_budget, work_done = 0; bool clean_complete = true; fm10k_for_each_ring(ring, q_vector->tx) @@ -1423,16 +1436,19 @@ static int fm10k_poll(struct napi_struct *napi, int budget) else per_ring_budget = budget; - fm10k_for_each_ring(ring, q_vector->rx) - clean_complete &= fm10k_clean_rx_irq(q_vector, ring, - per_ring_budget); + fm10k_for_each_ring(ring, q_vector->rx) { + int work = fm10k_clean_rx_irq(q_vector, ring, per_ring_budget); + + work_done += work; + clean_complete &= !!(work < per_ring_budget); + } /* If all work not completed, return budget and keep polling */ if (!clean_complete) return budget; /* all work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); /* re-enable the q_vector */ fm10k_qv_enable(q_vector); @@ -1892,7 +1908,7 @@ static void fm10k_init_reta(struct fm10k_intfc *interface) u32 reta, base; /* If the netdev is initialized we have to maintain table if possible */ - if (interface->netdev->reg_state) { + if (interface->netdev->reg_state != NETREG_UNINITIALIZED) { for (i = FM10K_RETA_SIZE; i--;) { reta = interface->reta[i]; if ((((reta << 24) >> 24) < rss_i) && diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c index 1a4b52637de9..af09a1b272e6 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c @@ -129,8 +129,8 @@ static u16 fm10k_fifo_head_drop(struct fm10k_mbx_fifo *fifo) * fm10k_fifo_drop_all - Drop all messages in FIFO * @fifo: pointer to FIFO * - * This function resets the head pointer to drop all messages in the FIFO, - * and ensure the FIFO is empty. + * This function resets the head pointer to drop all messages in the FIFO and + * ensure the FIFO is empty. **/ static void fm10k_fifo_drop_all(struct fm10k_mbx_fifo *fifo) { @@ -899,6 +899,27 @@ static void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx) } /** + * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mailbox header + * @mbx: pointer to mailbox + * + * This function creates a fake disconnect header for loading into remote + * mailbox header. The primary purpose is to prevent errors on immediate + * start up after mbx->connect. + **/ +static void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx) +{ + u32 hdr = FM10K_MSG_HDR_FIELD_SET(FM10K_MSG_DISCONNECT, TYPE) | + FM10K_MSG_HDR_FIELD_SET(mbx->head, TAIL) | + FM10K_MSG_HDR_FIELD_SET(mbx->tail, HEAD); + u16 crc = fm10k_crc_16b(&hdr, mbx->local, 1); + + mbx->mbx_lock |= FM10K_MBX_ACK; + + /* load header to memory to be written */ + mbx->mbx_hdr = hdr | FM10K_MSG_HDR_FIELD_SET(crc, CRC); +} + +/** * fm10k_mbx_create_error_msg - Generate a error message * @mbx: pointer to mailbox * @err: local error encountered @@ -1046,9 +1067,26 @@ static s32 fm10k_mbx_create_reply(struct fm10k_hw *hw, **/ static void fm10k_mbx_reset_work(struct fm10k_mbx_info *mbx) { + u16 len, head, ack; + /* reset our outgoing max size back to Rx limits */ mbx->max_size = mbx->rx.size - 1; + /* update mbx->pulled to account for tail_len and ack */ + head = FM10K_MSG_HDR_FIELD_GET(mbx->mbx_hdr, HEAD); + ack = fm10k_mbx_index_len(mbx, head, mbx->tail); + mbx->pulled += mbx->tail_len - ack; + + /* now drop any messages which have started or finished transmitting */ + while (fm10k_fifo_head_len(&mbx->tx) && mbx->pulled) { + len = fm10k_fifo_head_drop(&mbx->tx); + mbx->tx_dropped++; + if (mbx->pulled >= len) + mbx->pulled -= len; + else + mbx->pulled = 0; + } + /* just do a quick resysnc to start of message */ mbx->pushed = 0; mbx->pulled = 0; @@ -1418,8 +1456,10 @@ static s32 fm10k_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx) /* Place mbx in ready to connect state */ mbx->state = FM10K_STATE_CONNECT; + fm10k_mbx_reset_work(mbx); + /* initialize header of remote mailbox */ - fm10k_mbx_create_disconnect_hdr(mbx); + fm10k_mbx_create_fake_disconnect_hdr(mbx); fm10k_write_reg(hw, mbx->mbmem_reg ^ mbx->mbmem_len, mbx->mbx_hdr); /* enable interrupt and notify other party of new message */ @@ -1725,7 +1765,7 @@ static void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw, mbx->state = FM10K_STATE_CLOSED; mbx->remote = 0; fm10k_mbx_reset_work(mbx); - fm10k_mbx_update_max_size(mbx, 0); + fm10k_fifo_drop_all(&mbx->tx); fm10k_write_reg(hw, mbx->mbmem_reg, 0); } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 99228bf46c12..639263d5e833 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -758,6 +758,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) struct fm10k_intfc *interface = netdev_priv(netdev); struct fm10k_hw *hw = &interface->hw; s32 err; + int i; /* updates do not apply to VLAN 0 */ if (!vid) @@ -775,8 +776,25 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) if (!set) clear_bit(vid, interface->active_vlans); - /* if default VLAN is already present do nothing */ - if (vid == hw->mac.default_vid) + /* disable the default VID on ring if we have an active VLAN */ + for (i = 0; i < interface->num_rx_queues; i++) { + struct fm10k_ring *rx_ring = interface->rx_ring[i]; + u16 rx_vid = rx_ring->vid & (VLAN_N_VID - 1); + + if (test_bit(rx_vid, interface->active_vlans)) + rx_ring->vid |= FM10K_VLAN_CLEAR; + else + rx_ring->vid &= ~FM10K_VLAN_CLEAR; + } + + /* Do not remove default VID related entries from VLAN and MAC tables */ + if (!set && vid == hw->mac.default_vid) + return 0; + + /* Do not throw an error if the interface is down. We will sync once + * we come up + */ + if (test_bit(__FM10K_DOWN, &interface->state)) return 0; fm10k_mbx_lock(interface); @@ -996,21 +1014,6 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface) int xcast_mode; u16 vid, glort; - /* restore our address if perm_addr is set */ - if (hw->mac.type == fm10k_mac_vf) { - if (is_valid_ether_addr(hw->mac.perm_addr)) { - ether_addr_copy(hw->mac.addr, hw->mac.perm_addr); - ether_addr_copy(netdev->perm_addr, hw->mac.perm_addr); - ether_addr_copy(netdev->dev_addr, hw->mac.perm_addr); - netdev->addr_assign_type &= ~NET_ADDR_RANDOM; - } - - if (hw->mac.vlan_override) - netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX; - else - netdev->features |= NETIF_F_HW_VLAN_CTAG_RX; - } - /* record glort for this interface */ glort = interface->glort; @@ -1045,7 +1048,7 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface) vid, true, 0); } - /* update xcast mode before syncronizing addresses */ + /* update xcast mode before synchronizing addresses */ hw->mac.ops.update_xcast_mode(hw, glort, xcast_mode); /* synchronize all of the addresses */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index ce53ff25f88d..74be792f3f1b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -170,6 +170,21 @@ static void fm10k_reinit(struct fm10k_intfc *interface) /* reassociate interrupts */ fm10k_mbx_request_irq(interface); + /* update hardware address for VFs if perm_addr has changed */ + if (hw->mac.type == fm10k_mac_vf) { + if (is_valid_ether_addr(hw->mac.perm_addr)) { + ether_addr_copy(hw->mac.addr, hw->mac.perm_addr); + ether_addr_copy(netdev->perm_addr, hw->mac.perm_addr); + ether_addr_copy(netdev->dev_addr, hw->mac.perm_addr); + netdev->addr_assign_type &= ~NET_ADDR_RANDOM; + } + + if (hw->mac.vlan_override) + netdev->features &= ~NETIF_F_HW_VLAN_CTAG_RX; + else + netdev->features |= NETIF_F_HW_VLAN_CTAG_RX; + } + /* reset clock */ fm10k_ts_reset(interface); @@ -259,8 +274,6 @@ static void fm10k_watchdog_update_host_state(struct fm10k_intfc *interface) * @interface: board private structure * * This function will process both the upstream and downstream mailboxes. - * It is necessary for us to hold the rtnl_lock while doing this as the - * mailbox accesses are protected by this lock. **/ static void fm10k_mbx_subtask(struct fm10k_intfc *interface) { @@ -315,6 +328,9 @@ void fm10k_update_stats(struct fm10k_intfc *interface) { struct net_device_stats *net_stats = &interface->netdev->stats; struct fm10k_hw *hw = &interface->hw; + u64 hw_csum_tx_good = 0, hw_csum_rx_good = 0, rx_length_errors = 0; + u64 rx_switch_errors = 0, rx_drops = 0, rx_pp_errors = 0; + u64 rx_link_errors = 0; u64 rx_errors = 0, rx_csum_errors = 0, tx_csum_errors = 0; u64 restart_queue = 0, tx_busy = 0, alloc_failed = 0; u64 rx_bytes_nic = 0, rx_pkts_nic = 0, rx_drops_nic = 0; @@ -334,6 +350,7 @@ void fm10k_update_stats(struct fm10k_intfc *interface) tx_csum_errors += tx_ring->tx_stats.csum_err; bytes += tx_ring->stats.bytes; pkts += tx_ring->stats.packets; + hw_csum_tx_good += tx_ring->tx_stats.csum_good; } interface->restart_queue = restart_queue; @@ -341,6 +358,8 @@ void fm10k_update_stats(struct fm10k_intfc *interface) net_stats->tx_bytes = bytes; net_stats->tx_packets = pkts; interface->tx_csum_errors = tx_csum_errors; + interface->hw_csum_tx_good = hw_csum_tx_good; + /* gather some stats to the interface struct that are per queue */ for (bytes = 0, pkts = 0, i = 0; i < interface->num_rx_queues; i++) { struct fm10k_ring *rx_ring = interface->rx_ring[i]; @@ -350,12 +369,24 @@ void fm10k_update_stats(struct fm10k_intfc *interface) alloc_failed += rx_ring->rx_stats.alloc_failed; rx_csum_errors += rx_ring->rx_stats.csum_err; rx_errors += rx_ring->rx_stats.errors; + hw_csum_rx_good += rx_ring->rx_stats.csum_good; + rx_switch_errors += rx_ring->rx_stats.switch_errors; + rx_drops += rx_ring->rx_stats.drops; + rx_pp_errors += rx_ring->rx_stats.pp_errors; + rx_link_errors += rx_ring->rx_stats.link_errors; + rx_length_errors += rx_ring->rx_stats.length_errors; } net_stats->rx_bytes = bytes; net_stats->rx_packets = pkts; interface->alloc_failed = alloc_failed; interface->rx_csum_errors = rx_csum_errors; + interface->hw_csum_rx_good = hw_csum_rx_good; + interface->rx_switch_errors = rx_switch_errors; + interface->rx_drops = rx_drops; + interface->rx_pp_errors = rx_pp_errors; + interface->rx_link_errors = rx_link_errors; + interface->rx_length_errors = rx_length_errors; hw->mac.ops.update_hw_stats(hw, &interface->stats); @@ -483,7 +514,7 @@ static void fm10k_service_task(struct work_struct *work) interface = container_of(work, struct fm10k_intfc, service_task); - /* tasks always capable of running, but must be rtnl protected */ + /* tasks run even when interface is down */ fm10k_mbx_subtask(interface); fm10k_detach_subtask(interface); fm10k_reset_subtask(interface); @@ -663,6 +694,10 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface, /* assign default VLAN to queue */ ring->vid = hw->mac.default_vid; + /* if we have an active VLAN, disable default VID */ + if (test_bit(hw->mac.default_vid, interface->active_vlans)) + ring->vid |= FM10K_VLAN_CLEAR; + /* Map interrupt */ if (ring->q_vector) { rxint = ring->q_vector->v_idx + NON_Q_VECTORS(hw); @@ -861,10 +896,12 @@ void fm10k_netpoll(struct net_device *netdev) #endif #define FM10K_ERR_MSG(type) case (type): error = #type; break -static void fm10k_print_fault(struct fm10k_intfc *interface, int type, +static void fm10k_handle_fault(struct fm10k_intfc *interface, int type, struct fm10k_fault *fault) { struct pci_dev *pdev = interface->pdev; + struct fm10k_hw *hw = &interface->hw; + struct fm10k_iov_data *iov_data = interface->iov_data; char *error; switch (type) { @@ -918,6 +955,30 @@ static void fm10k_print_fault(struct fm10k_intfc *interface, int type, "%s Address: 0x%llx SpecInfo: 0x%x Func: %02x.%0x\n", error, fault->address, fault->specinfo, PCI_SLOT(fault->func), PCI_FUNC(fault->func)); + + /* For VF faults, clear out the respective LPORT, reset the queue + * resources, and then reconnect to the mailbox. This allows the + * VF in question to resume behavior. For transient faults that are + * the result of non-malicious behavior this will log the fault and + * allow the VF to resume functionality. Obviously for malicious VFs + * they will be able to attempt malicious behavior again. In this + * case, the system administrator will need to step in and manually + * remove or disable the VF in question. + */ + if (fault->func && iov_data) { + int vf = fault->func - 1; + struct fm10k_vf_info *vf_info = &iov_data->vf_info[vf]; + + hw->iov.ops.reset_lport(hw, vf_info); + hw->iov.ops.reset_resources(hw, vf_info); + + /* reset_lport disables the VF, so re-enable it */ + hw->iov.ops.set_lport(hw, vf_info, vf, + FM10K_VF_FLAG_MULTI_CAPABLE); + + /* reset_resources will disconnect from the mbx */ + vf_info->mbx.ops.connect(hw, &vf_info->mbx); + } } static void fm10k_report_fault(struct fm10k_intfc *interface, u32 eicr) @@ -941,7 +1002,7 @@ static void fm10k_report_fault(struct fm10k_intfc *interface, u32 eicr) continue; } - fm10k_print_fault(interface, type, &fault); + fm10k_handle_fault(interface, type, &fault); } } @@ -1705,22 +1766,86 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, static void fm10k_slot_warn(struct fm10k_intfc *interface) { - struct device *dev = &interface->pdev->dev; + enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN; + enum pci_bus_speed speed = PCI_SPEED_UNKNOWN; struct fm10k_hw *hw = &interface->hw; + int max_gts = 0, expected_gts = 0; + + if (pcie_get_minimum_link(interface->pdev, &speed, &width) || + speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) { + dev_warn(&interface->pdev->dev, + "Unable to determine PCI Express bandwidth.\n"); + return; + } + + switch (speed) { + case PCIE_SPEED_2_5GT: + /* 8b/10b encoding reduces max throughput by 20% */ + max_gts = 2 * width; + break; + case PCIE_SPEED_5_0GT: + /* 8b/10b encoding reduces max throughput by 20% */ + max_gts = 4 * width; + break; + case PCIE_SPEED_8_0GT: + /* 128b/130b encoding has less than 2% impact on throughput */ + max_gts = 8 * width; + break; + default: + dev_warn(&interface->pdev->dev, + "Unable to determine PCI Express bandwidth.\n"); + return; + } + + dev_info(&interface->pdev->dev, + "PCI Express bandwidth of %dGT/s available\n", + max_gts); + dev_info(&interface->pdev->dev, + "(Speed:%s, Width: x%d, Encoding Loss:%s, Payload:%s)\n", + (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : + speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : + speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : + "Unknown"), + hw->bus.width, + (speed == PCIE_SPEED_2_5GT ? "20%" : + speed == PCIE_SPEED_5_0GT ? "20%" : + speed == PCIE_SPEED_8_0GT ? "<2%" : + "Unknown"), + (hw->bus.payload == fm10k_bus_payload_128 ? "128B" : + hw->bus.payload == fm10k_bus_payload_256 ? "256B" : + hw->bus.payload == fm10k_bus_payload_512 ? "512B" : + "Unknown")); - if (hw->mac.ops.is_slot_appropriate(hw)) + switch (hw->bus_caps.speed) { + case fm10k_bus_speed_2500: + /* 8b/10b encoding reduces max throughput by 20% */ + expected_gts = 2 * hw->bus_caps.width; + break; + case fm10k_bus_speed_5000: + /* 8b/10b encoding reduces max throughput by 20% */ + expected_gts = 4 * hw->bus_caps.width; + break; + case fm10k_bus_speed_8000: + /* 128b/130b encoding has less than 2% impact on throughput */ + expected_gts = 8 * hw->bus_caps.width; + break; + default: + dev_warn(&interface->pdev->dev, + "Unable to determine expected PCI Express bandwidth.\n"); return; + } - dev_warn(dev, - "For optimal performance, a %s %s slot is recommended.\n", - (hw->bus_caps.width == fm10k_bus_width_pcie_x1 ? "x1" : - hw->bus_caps.width == fm10k_bus_width_pcie_x4 ? "x4" : - "x8"), - (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s" : - hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s" : - "8.0GT/s")); - dev_warn(dev, - "A slot with more lanes and/or higher speed is suggested.\n"); + if (max_gts < expected_gts) { + dev_warn(&interface->pdev->dev, + "This device requires %dGT/s of bandwidth for optimal performance.\n", + expected_gts); + dev_warn(&interface->pdev->dev, + "A %sslot with x%d lanes is suggested.\n", + (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " : + hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " : + hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""), + hw->bus_caps.width); + } } /** @@ -1739,7 +1864,6 @@ static int fm10k_probe(struct pci_dev *pdev, { struct net_device *netdev; struct fm10k_intfc *interface; - struct fm10k_hw *hw; int err; err = pci_enable_device_mem(pdev); @@ -1783,7 +1907,6 @@ static int fm10k_probe(struct pci_dev *pdev, interface->netdev = netdev; interface->pdev = pdev; - hw = &interface->hw; interface->uc_addr = ioremap(pci_resource_start(pdev, 0), FM10K_UC_ADDR_SIZE); @@ -1825,24 +1948,12 @@ static int fm10k_probe(struct pci_dev *pdev, /* Register PTP interface */ fm10k_ptp_register(interface); - /* print bus type/speed/width info */ - dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n", - (hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" : - hw->bus.speed == fm10k_bus_speed_5000 ? "5.0GT/s" : - hw->bus.speed == fm10k_bus_speed_2500 ? "2.5GT/s" : - "Unknown"), - (hw->bus.width == fm10k_bus_width_pcie_x8 ? "x8" : - hw->bus.width == fm10k_bus_width_pcie_x4 ? "x4" : - hw->bus.width == fm10k_bus_width_pcie_x1 ? "x1" : - "Unknown"), - (hw->bus.payload == fm10k_bus_payload_128 ? "128B" : - hw->bus.payload == fm10k_bus_payload_256 ? "256B" : - hw->bus.payload == fm10k_bus_payload_512 ? "512B" : - "Unknown")); - /* print warning for non-optimal configurations */ fm10k_slot_warn(interface); + /* report MAC address for logging */ + dev_info(&pdev->dev, "%pM\n", netdev->dev_addr); + /* enable SR-IOV after registering netdev to enforce PF/VF ordering */ fm10k_iov_configure(pdev, 0); @@ -1983,6 +2094,16 @@ static int fm10k_resume(struct pci_dev *pdev) if (err) return err; + /* assume host is not ready, to prevent race with watchdog in case we + * actually don't have connection to the switch + */ + interface->host_ready = false; + fm10k_watchdog_host_not_ready(interface); + + /* clear the service task disable bit to allow service task to start */ + clear_bit(__FM10K_SERVICE_DISABLE, &interface->state); + fm10k_service_event_schedule(interface); + /* restore SR-IOV interface */ fm10k_iov_resume(pdev); @@ -2010,6 +2131,15 @@ static int fm10k_suspend(struct pci_dev *pdev, fm10k_iov_suspend(pdev); + /* the watchdog tasks may read registers, which will appear like a + * surprise-remove event once the PCI device is disabled. This will + * cause us to close the netdevice, so we don't retain the open/closed + * state post-resume. Prevent this by disabling the service task while + * suspended, until we actually resume. + */ + set_bit(__FM10K_SERVICE_DISABLE, &interface->state); + cancel_work_sync(&interface->service_task); + rtnl_lock(); if (netif_running(netdev)) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 3ca0233b3ea2..8c0bdc4e4edd 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -59,6 +59,11 @@ static s32 fm10k_reset_hw_pf(struct fm10k_hw *hw) if (reg & (FM10K_DMA_CTRL_TX_ACTIVE | FM10K_DMA_CTRL_RX_ACTIVE)) return FM10K_ERR_DMA_PENDING; + /* verify the switch is ready for reset */ + reg = fm10k_read_reg(hw, FM10K_DMA_CTRL2); + if (!(reg & FM10K_DMA_CTRL2_SWITCH_READY)) + goto out; + /* Inititate data path reset */ reg |= FM10K_DMA_CTRL_DATAPATH_RESET; fm10k_write_reg(hw, FM10K_DMA_CTRL, reg); @@ -72,6 +77,7 @@ static s32 fm10k_reset_hw_pf(struct fm10k_hw *hw) if (!(reg & FM10K_IP_NOTINRESET)) err = FM10K_ERR_RESET_FAILED; +out: return err; } @@ -185,19 +191,6 @@ static s32 fm10k_init_hw_pf(struct fm10k_hw *hw) } /** - * fm10k_is_slot_appropriate_pf - Indicate appropriate slot for this SKU - * @hw: pointer to hardware structure - * - * Looks at the PCIe bus info to confirm whether or not this slot can support - * the necessary bandwidth for this device. - **/ -static bool fm10k_is_slot_appropriate_pf(struct fm10k_hw *hw) -{ - return (hw->bus.speed == hw->bus_caps.speed) && - (hw->bus.width == hw->bus_caps.width); -} - -/** * fm10k_update_vlan_pf - Update status of VLAN ID in VLAN filter table * @hw: pointer to hardware structure * @vid: VLAN ID to add to table @@ -1162,6 +1155,24 @@ s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *hw, u32 **results, } /** + * fm10k_iov_select_vid - Select correct default VID + * @hw: Pointer to hardware structure + * @vid: VID to correct + * + * Will report an error if VID is out of range. For VID = 0, it will return + * either the pf_vid or sw_vid depending on which one is set. + */ +static inline s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid) +{ + if (!vid) + return vf_info->pf_vid ? vf_info->pf_vid : vf_info->sw_vid; + else if (vf_info->pf_vid && vid != vf_info->pf_vid) + return FM10K_ERR_PARAM; + else + return vid; +} + +/** * fm10k_iov_msg_mac_vlan_pf - Message handler for MAC/VLAN request from VF * @hw: Pointer to hardware structure * @results: Pointer array to message, results[0] is pointer to message @@ -1175,9 +1186,10 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, struct fm10k_mbx_info *mbx) { struct fm10k_vf_info *vf_info = (struct fm10k_vf_info *)mbx; - int err = 0; u8 mac[ETH_ALEN]; u32 *result; + int err = 0; + bool set; u16 vlan; u32 vid; @@ -1193,19 +1205,21 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, if (err) return err; - /* if VLAN ID is 0, set the default VLAN ID instead of 0 */ - if (!vid || (vid == FM10K_VLAN_CLEAR)) { - if (vf_info->pf_vid) - vid |= vf_info->pf_vid; - else - vid |= vf_info->sw_vid; - } else if (vid != vf_info->pf_vid) { + /* verify upper 16 bits are zero */ + if (vid >> 16) return FM10K_ERR_PARAM; - } + + set = !(vid & FM10K_VLAN_CLEAR); + vid &= ~FM10K_VLAN_CLEAR; + + err = fm10k_iov_select_vid(vf_info, vid); + if (err < 0) + return err; + else + vid = err; /* update VSI info for VF in regards to VLAN table */ - err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi, - !(vid & FM10K_VLAN_CLEAR)); + err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi, set); } if (!err && !!results[FM10K_MAC_VLAN_MSG_MAC]) { @@ -1221,19 +1235,18 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, memcmp(mac, vf_info->mac, ETH_ALEN)) return FM10K_ERR_PARAM; - /* if VLAN ID is 0, set the default VLAN ID instead of 0 */ - if (!vlan || (vlan == FM10K_VLAN_CLEAR)) { - if (vf_info->pf_vid) - vlan |= vf_info->pf_vid; - else - vlan |= vf_info->sw_vid; - } else if (vf_info->pf_vid) { - return FM10K_ERR_PARAM; - } + set = !(vlan & FM10K_VLAN_CLEAR); + vlan &= ~FM10K_VLAN_CLEAR; + + err = fm10k_iov_select_vid(vf_info, vlan); + if (err < 0) + return err; + else + vlan = err; /* notify switch of request for new unicast address */ - err = hw->mac.ops.update_uc_addr(hw, vf_info->glort, mac, vlan, - !(vlan & FM10K_VLAN_CLEAR), 0); + err = hw->mac.ops.update_uc_addr(hw, vf_info->glort, + mac, vlan, set, 0); } if (!err && !!results[FM10K_MAC_VLAN_MSG_MULTICAST]) { @@ -1248,19 +1261,18 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, if (!(vf_info->vf_flags & FM10K_VF_FLAG_MULTI_ENABLED)) return FM10K_ERR_PARAM; - /* if VLAN ID is 0, set the default VLAN ID instead of 0 */ - if (!vlan || (vlan == FM10K_VLAN_CLEAR)) { - if (vf_info->pf_vid) - vlan |= vf_info->pf_vid; - else - vlan |= vf_info->sw_vid; - } else if (vf_info->pf_vid) { - return FM10K_ERR_PARAM; - } + set = !(vlan & FM10K_VLAN_CLEAR); + vlan &= ~FM10K_VLAN_CLEAR; + + err = fm10k_iov_select_vid(vf_info, vlan); + if (err < 0) + return err; + else + vlan = err; /* notify switch of request for new multicast address */ - err = hw->mac.ops.update_mc_addr(hw, vf_info->glort, mac, vlan, - !(vlan & FM10K_VLAN_CLEAR)); + err = hw->mac.ops.update_mc_addr(hw, vf_info->glort, + mac, vlan, set); } return err; @@ -1849,7 +1861,6 @@ static struct fm10k_mac_ops mac_ops_pf = { .init_hw = &fm10k_init_hw_pf, .start_hw = &fm10k_start_hw_generic, .stop_hw = &fm10k_stop_hw_generic, - .is_slot_appropriate = &fm10k_is_slot_appropriate_pf, .update_vlan = &fm10k_update_vlan_pf, .read_mac_addr = &fm10k_read_mac_addr_pf, .update_uc_addr = &fm10k_update_uc_addr_pf, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index 2a17d82fa37d..318a212f0a78 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -521,7 +521,6 @@ struct fm10k_mac_ops { s32 (*stop_hw)(struct fm10k_hw *); s32 (*get_bus_info)(struct fm10k_hw *); s32 (*get_host_state)(struct fm10k_hw *, bool *); - bool (*is_slot_appropriate)(struct fm10k_hw *); s32 (*update_vlan)(struct fm10k_hw *, u32, u8, bool); s32 (*read_mac_addr)(struct fm10k_hw *); s32 (*update_uc_addr)(struct fm10k_hw *, u16, const u8 *, @@ -763,6 +762,12 @@ enum fm10k_rxdesc_xc { #define FM10K_RXD_STATUS_L4E 0x4000 /* L4 csum error */ #define FM10K_RXD_STATUS_IPE 0x8000 /* IPv4 csum error */ +#define FM10K_RXD_ERR_SWITCH_ERROR 0x0001 /* Switch found bad packet */ +#define FM10K_RXD_ERR_NO_DESCRIPTOR 0x0002 /* No descriptor available */ +#define FM10K_RXD_ERR_PP_ERROR 0x0004 /* RAM error during processing */ +#define FM10K_RXD_ERR_SWITCH_READY 0x0008 /* Link transition mid-packet */ +#define FM10K_RXD_ERR_TOO_BIG 0x0010 /* Pkt too big for single buf */ + struct fm10k_ftag { __be16 swpri_type_user; __be16 vlan; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index 94f0f6a146d9..36c8b0aa08fd 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -131,19 +131,6 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw) return 0; } -/** - * fm10k_is_slot_appropriate_vf - Indicate appropriate slot for this SKU - * @hw: pointer to hardware structure - * - * Looks at the PCIe bus info to confirm whether or not this slot can support - * the necessary bandwidth for this device. Since the VF has no control over - * the "slot" it is in, always indicate that the slot is appropriate. - **/ -static bool fm10k_is_slot_appropriate_vf(struct fm10k_hw *hw) -{ - return true; -} - /* This structure defines the attibutes to be parsed below */ const struct fm10k_tlv_attr fm10k_mac_vlan_msg_attr[] = { FM10K_TLV_ATTR_U32(FM10K_MAC_VLAN_MSG_VLAN), @@ -552,7 +539,6 @@ static struct fm10k_mac_ops mac_ops_vf = { .init_hw = &fm10k_init_hw_vf, .start_hw = &fm10k_start_hw_generic, .stop_hw = &fm10k_stop_hw_vf, - .is_slot_appropriate = &fm10k_is_slot_appropriate_vf, .update_vlan = &fm10k_update_vlan_vf, .read_mac_addr = &fm10k_read_mac_addr_vf, .update_uc_addr = &fm10k_update_uc_addr_vf, diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index e7462793d48d..7a58b1f9971f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -71,7 +71,6 @@ #define I40E_MAX_VEB 16 #define I40E_MAX_NUM_DESCRIPTORS 4096 -#define I40E_MAX_REGISTER 0x800000 #define I40E_MAX_CSR_SPACE (4 * 1024 * 1024 - 64 * 1024) #define I40E_DEFAULT_NUM_DESCRIPTORS 512 #define I40E_REQ_DESCRIPTOR_MULTIPLE 32 @@ -94,19 +93,24 @@ #endif /* I40E_FCOE */ #define I40E_MAX_AQ_BUF_SIZE 4096 #define I40E_AQ_LEN 256 -#define I40E_AQ_WORK_LIMIT 32 +#define I40E_AQ_WORK_LIMIT 66 /* max number of VFs + a little */ #define I40E_MAX_USER_PRIORITY 8 #define I40E_DEFAULT_MSG_ENABLE 4 #define I40E_QUEUE_WAIT_RETRY_LIMIT 10 -#define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 9) +#define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 16) /* Ethtool Private Flags */ #define I40E_PRIV_FLAGS_NPAR_FLAG BIT(0) +#define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) +#define I40E_PRIV_FLAGS_FD_ATR BIT(2) +#define I40E_PRIV_FLAGS_VEB_STATS BIT(3) #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) #define I40E_NVM_VERSION_HI_SHIFT 12 #define I40E_NVM_VERSION_HI_MASK (0xf << I40E_NVM_VERSION_HI_SHIFT) +#define I40E_OEM_VER_BUILD_MASK 0xff00 +#define I40E_OEM_VER_PATCH_MASK 0xff /* The values in here are decimal coded as hex as is the case in the NVM map*/ #define I40E_CURRENT_NVM_VERSION_HI 0x2 @@ -243,7 +247,6 @@ struct i40e_pf { struct pci_dev *pdev; struct i40e_hw hw; unsigned long state; - unsigned long link_check_timeout; struct msix_entry *msix_entries; bool fc_autoneg_status; @@ -305,7 +308,6 @@ struct i40e_pf { #ifdef I40E_FCOE #define I40E_FLAG_FCOE_ENABLED BIT_ULL(11) #endif /* I40E_FCOE */ -#define I40E_FLAG_IN_NETPOLL BIT_ULL(12) #define I40E_FLAG_16BYTE_RX_DESC_ENABLED BIT_ULL(13) #define I40E_FLAG_CLEAN_ADMINQ BIT_ULL(14) #define I40E_FLAG_FILTER_SYNC BIT_ULL(15) @@ -327,8 +329,11 @@ struct i40e_pf { #define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE BIT_ULL(33) #define I40E_FLAG_128_QP_RSS_CAPABLE BIT_ULL(34) #define I40E_FLAG_WB_ON_ITR_CAPABLE BIT_ULL(35) +#define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(37) #define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38) +#define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) +#define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42) /* tracks features that get auto disabled by errors */ u64 auto_disable_flags; @@ -409,6 +414,9 @@ struct i40e_pf { /* These are only valid in NPAR modes */ u32 npar_max_bw; u32 npar_min_bw; + + u32 ioremap_len; + u32 fd_inv; }; struct i40e_mac_filter { @@ -474,6 +482,7 @@ struct i40e_vsi { #endif u32 tx_restart; u32 tx_busy; + u64 tx_linearize; u32 rx_buf_failed; u32 rx_page_failed; @@ -489,6 +498,7 @@ struct i40e_vsi { */ u16 rx_itr_setting; u16 tx_itr_setting; + u16 int_rate_limit; /* value in usecs */ u16 rss_table_size; u16 rss_size; @@ -534,6 +544,7 @@ struct i40e_vsi { u16 idx; /* index in pf->vsi[] */ u16 veb_idx; /* index of VEB parent */ struct kobject *kobj; /* sysfs object */ + bool current_isup; /* Sync 'link up' logging */ /* VSI specific handlers */ irqreturn_t (*irq_handler)(int irq, void *data); @@ -573,22 +584,22 @@ struct i40e_device { }; /** - * i40e_fw_version_str - format the FW and NVM version strings + * i40e_nvm_version_str - format the NVM version strings * @hw: ptr to the hardware info **/ -static inline char *i40e_fw_version_str(struct i40e_hw *hw) +static inline char *i40e_nvm_version_str(struct i40e_hw *hw) { static char buf[32]; snprintf(buf, sizeof(buf), - "f%d.%d.%05d a%d.%d n%x.%02x e%x", - hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build, - hw->aq.api_maj_ver, hw->aq.api_min_ver, + "%x.%02x 0x%x %d.%d.%d", (hw->nvm.version & I40E_NVM_VERSION_HI_MASK) >> I40E_NVM_VERSION_HI_SHIFT, (hw->nvm.version & I40E_NVM_VERSION_LO_MASK) >> I40E_NVM_VERSION_LO_SHIFT, - (hw->nvm.eetrack & 0xffffff)); + hw->nvm.eetrack, (hw->nvm.oem_ver >> 24), + (hw->nvm.oem_ver & I40E_OEM_VER_BUILD_MASK) >> 8, + hw->nvm.oem_ver & I40E_OEM_VER_PATCH_MASK); return buf; } @@ -667,7 +678,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, bool is_vf, bool is_netdev); void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan, bool is_vf, bool is_netdev); -int i40e_sync_vsi_filters(struct i40e_vsi *vsi); +int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); int i40e_vsi_release(struct i40e_vsi *vsi); @@ -700,7 +711,24 @@ static inline void i40e_dbg_pf_exit(struct i40e_pf *pf) {} static inline void i40e_dbg_init(void) {} static inline void i40e_dbg_exit(void) {} #endif /* CONFIG_DEBUG_FS*/ -void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector); +/** + * i40e_irq_dynamic_enable - Enable default interrupt generation settings + * @vsi: pointer to a vsi + * @vector: enable a particular Hw Interrupt vector, without base_vector + **/ +static inline void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + u32 val; + + val = I40E_PFINT_DYN_CTLN_INTENA_MASK | + I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | + (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); + wr32(hw, I40E_PFINT_DYN_CTLN(vector + vsi->base_vector - 1), val); + /* skip the flush */ +} + void i40e_irq_dynamic_disable(struct i40e_vsi *vsi, int vector); void i40e_irq_dynamic_disable_icr0(struct i40e_pf *pf); void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf); @@ -739,7 +767,7 @@ int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt); u8 i40e_get_fcoe_tc_map(struct i40e_pf *pf); void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi); void i40e_fcoe_vsi_setup(struct i40e_pf *pf); -int i40e_init_pf_fcoe(struct i40e_pf *pf); +void i40e_init_pf_fcoe(struct i40e_pf *pf); int i40e_fcoe_setup_ddp_resources(struct i40e_vsi *vsi); void i40e_fcoe_free_ddp_resources(struct i40e_vsi *vsi); int i40e_fcoe_handle_offload(struct i40e_ring *rx_ring, @@ -771,4 +799,5 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi); i40e_status i40e_get_npar_bw_setting(struct i40e_pf *pf); i40e_status i40e_set_npar_bw_setting(struct i40e_pf *pf); i40e_status i40e_commit_npar_bw_setting(struct i40e_pf *pf); +void i40e_print_link_message(struct i40e_vsi *vsi, bool isup); #endif /* _I40E_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index c0e943aecd13..0ff8f01e57ee 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -482,8 +482,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.asq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_asq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.asq.head, 0); @@ -492,16 +496,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) wr32(hw, hw->aq.asq.bal, 0); wr32(hw, hw->aq.asq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.asq_mutex); - hw->aq.asq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_asq_bufs(hw); +shutdown_asq_out: mutex_unlock(&hw->aq.asq_mutex); - return ret_code; } @@ -515,8 +516,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.arq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.arq_mutex); + + if (hw->aq.arq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_arq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.arq.head, 0); @@ -525,16 +530,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) wr32(hw, hw->aq.arq.bal, 0); wr32(hw, hw->aq.arq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.arq_mutex); - hw->aq.arq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_arq_bufs(hw); +shutdown_arq_out: mutex_unlock(&hw->aq.arq_mutex); - return ret_code; } @@ -551,8 +553,9 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) **/ i40e_status i40e_init_adminq(struct i40e_hw *hw) { - i40e_status ret_code; + u16 cfg_ptr, oem_hi, oem_lo; u16 eetrack_lo, eetrack_hi; + i40e_status ret_code; int retry = 0; /* verify input for valid configuration */ @@ -611,6 +614,12 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw) i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_LO, &eetrack_lo); i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi); hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo; + i40e_read_nvm_word(hw, I40E_SR_BOOT_CONFIG_PTR, &cfg_ptr); + i40e_read_nvm_word(hw, (cfg_ptr + I40E_NVM_OEM_VER_OFF), + &oem_hi); + i40e_read_nvm_word(hw, (cfg_ptr + (I40E_NVM_OEM_VER_OFF + 1)), + &oem_lo); + hw->nvm.oem_ver = ((u32)oem_hi << 16) | oem_lo; if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) { ret_code = I40E_ERR_FIRMWARE_API_VERSION; @@ -657,6 +666,9 @@ i40e_status i40e_shutdown_adminq(struct i40e_hw *hw) /* destroy the locks */ + if (hw->nvm_buff.va) + i40e_free_virt_mem(hw, &hw->nvm_buff); + return ret_code; } @@ -678,8 +690,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw) details = I40E_ADMINQ_DETAILS(*asq, ntc); while (rd32(hw, hw->aq.asq.head) != ntc) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "%s: ntc %d head %d.\n", __func__, ntc, - rd32(hw, hw->aq.asq.head)); + "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head)); if (details->callback) { I40E_ADMINQ_CALLBACK cb_func = @@ -742,19 +753,23 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, u16 retval = 0; u32 val = 0; - val = rd32(hw, hw->aq.asq.head); - if (val >= hw->aq.num_asq_entries) { + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: head overrun at %d\n", val); + "AQTX: Admin queue not initialized.\n"); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } - if (hw->aq.asq.count == 0) { + hw->aq.asq_last_status = I40E_AQ_RC_OK; + + val = rd32(hw, hw->aq.asq.head); + if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: Admin queue not initialized.\n"); + "AQTX: head overrun at %d\n", val); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use); @@ -779,8 +794,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, desc->flags &= ~cpu_to_le16(details->flags_dis); desc->flags |= cpu_to_le16(details->flags_ena); - mutex_lock(&hw->aq.asq_mutex); - if (buff_size > hw->aq.asq_buf_size) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -889,6 +902,10 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, "AQTX: desc and buffer writeback:\n"); i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size); + /* save writeback aq if requested */ + if (details->wb_desc) + *details->wb_desc = *desc_on_ring; + /* update the error if time out occurred */ if ((!cmd_completed) && (!details->async && !details->postpone)) { @@ -900,7 +917,6 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, asq_send_command_error: mutex_unlock(&hw->aq.asq_mutex); -asq_send_command_exit: return status; } @@ -1023,6 +1039,19 @@ clean_arq_element_err: i40e_release_nvm(hw); hw->aq.nvm_release_on_done = false; } + + switch (hw->nvmupd_state) { + case I40E_NVMUPD_STATE_INIT_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + break; + + case I40E_NVMUPD_STATE_WRITE_WAIT: + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; + break; + + default: + break; + } } return ret_code; diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index 28e519a50de4..12fbbddea299 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -69,6 +69,7 @@ struct i40e_asq_cmd_details { u16 flags_dis; bool async; bool postpone; + struct i40e_aq_desc *wb_desc; }; #define I40E_ADMINQ_DETAILS(R, i) \ @@ -108,9 +109,10 @@ struct i40e_adminq_info { /** * i40e_aq_rc_to_posix - convert errors to user-land codes - * aq_rc: AdminQ error code to convert + * aq_ret: AdminQ handler error code can override aq_rc + * aq_rc: AdminQ firmware error code to convert **/ -static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) +static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) { int aq_to_posix[] = { 0, /* I40E_AQ_RC_OK */ @@ -142,8 +144,9 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT) return -EAGAIN; - if (aq_rc >= ARRAY_SIZE(aq_to_posix)) + if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) return -ERANGE; + return aq_to_posix[aq_rc]; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 95d23bfbcbf1..6584b6cd73fd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -1722,11 +1722,13 @@ struct i40e_aqc_get_link_status { u8 phy_type; /* i40e_aq_phy_type */ u8 link_speed; /* i40e_aq_link_speed */ u8 link_info; -#define I40E_AQ_LINK_UP 0x01 +#define I40E_AQ_LINK_UP 0x01 /* obsolete */ +#define I40E_AQ_LINK_UP_FUNCTION 0x01 #define I40E_AQ_LINK_FAULT 0x02 #define I40E_AQ_LINK_FAULT_TX 0x04 #define I40E_AQ_LINK_FAULT_RX 0x08 #define I40E_AQ_LINK_FAULT_REMOTE 0x10 +#define I40E_AQ_LINK_UP_PORT 0x20 #define I40E_AQ_MEDIA_AVAILABLE 0x40 #define I40E_AQ_SIGNAL_DETECT 0x80 u8 an_info; @@ -2062,6 +2064,7 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); #define I40E_AQC_CEE_APP_ISCSI_MASK (0x7 << I40E_AQC_CEE_APP_ISCSI_SHIFT) #define I40E_AQC_CEE_APP_FIP_SHIFT 0x8 #define I40E_AQC_CEE_APP_FIP_MASK (0x7 << I40E_AQC_CEE_APP_FIP_SHIFT) + #define I40E_AQC_CEE_PG_STATUS_SHIFT 0x0 #define I40E_AQC_CEE_PG_STATUS_MASK (0x7 << I40E_AQC_CEE_PG_STATUS_SHIFT) #define I40E_AQC_CEE_PFC_STATUS_SHIFT 0x3 @@ -2070,10 +2073,19 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_start); #define I40E_AQC_CEE_APP_STATUS_MASK (0x7 << I40E_AQC_CEE_APP_STATUS_SHIFT) #define I40E_AQC_CEE_FCOE_STATUS_SHIFT 0x8 #define I40E_AQC_CEE_FCOE_STATUS_MASK (0x7 << I40E_AQC_CEE_FCOE_STATUS_SHIFT) -#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT 0xA +#define I40E_AQC_CEE_ISCSI_STATUS_SHIFT 0xB #define I40E_AQC_CEE_ISCSI_STATUS_MASK (0x7 << I40E_AQC_CEE_ISCSI_STATUS_SHIFT) #define I40E_AQC_CEE_FIP_STATUS_SHIFT 0x10 #define I40E_AQC_CEE_FIP_STATUS_MASK (0x7 << I40E_AQC_CEE_FIP_STATUS_SHIFT) + +/* struct i40e_aqc_get_cee_dcb_cfg_v1_resp was originally defined with + * word boundary layout issues, which the Linux compilers silently deal + * with by adding padding, making the actual struct larger than designed. + * However, the FW compiler for the NIC is less lenient and complains + * about the struct. Hence, the struct defined here has an extra byte in + * fields reserved3 and reserved4 to directly acknowledge that padding, + * and the new length is used in the length check macro. + */ struct i40e_aqc_get_cee_dcb_cfg_v1_resp { u8 reserved1; u8 oper_num_tc; @@ -2081,9 +2093,9 @@ struct i40e_aqc_get_cee_dcb_cfg_v1_resp { u8 reserved2; u8 oper_tc_bw[8]; u8 oper_pfc_en; - u8 reserved3; + u8 reserved3[2]; __le16 oper_app_prio; - u8 reserved4; + u8 reserved4[2]; __le16 tlv_status; }; @@ -2120,6 +2132,13 @@ I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_get_cee_dcb_cfg_resp); struct i40e_aqc_lldp_set_local_mib { #define SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT 0 #define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK (1 << SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK (1 << \ + SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_LOCAL_MIB 0x0 +#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT (1) +#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_MASK (1 << \ + SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT) +#define SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS 0x1 u8 type; u8 reserved0; __le16 length; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 114dc6450183..daa6b177fdb7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -51,7 +51,9 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: + case I40E_DEV_ID_20G_KR2_A: hw->mac.type = I40E_MAC_XL710; break; case I40E_DEV_ID_SFP_X722: @@ -85,7 +87,7 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) * @hw: pointer to the HW structure * @aq_err: the AQ error code to convert **/ -char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) +const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) { switch (aq_err) { case I40E_AQ_RC_OK: @@ -145,7 +147,7 @@ char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) * @hw: pointer to the HW structure * @stat_err: the status error code to convert **/ -char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err) +const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err) { switch (stat_err) { case 0: @@ -441,9 +443,6 @@ static i40e_status i40e_aq_get_set_rss_lut(struct i40e_hw *hw, I40E_AQC_SET_RSS_LUT_TABLE_TYPE_SHIFT) & I40E_AQC_SET_RSS_LUT_TABLE_TYPE_MASK)); - cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)lut)); - cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)lut)); - status = i40e_asq_send_command(hw, &desc, lut, lut_size, NULL); return status; @@ -518,8 +517,6 @@ static i40e_status i40e_aq_get_set_rss_key(struct i40e_hw *hw, I40E_AQC_SET_RSS_KEY_VSI_ID_SHIFT) & I40E_AQC_SET_RSS_KEY_VSI_ID_MASK)); cmd_resp->vsi_id |= cpu_to_le16((u16)I40E_AQC_SET_RSS_KEY_VSI_VALID); - cmd_resp->addr_high = cpu_to_le32(high_16_bits((u64)key)); - cmd_resp->addr_low = cpu_to_le32(lower_32_bits((u64)key)); status = i40e_asq_send_command(hw, &desc, key, key_size, NULL); @@ -1038,7 +1035,7 @@ i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr) status = i40e_aq_mac_address_read(hw, &flags, &addrs, NULL); if (flags & I40E_AQC_LAN_ADDR_VALID) - memcpy(mac_addr, &addrs.pf_lan_mac, sizeof(addrs.pf_lan_mac)); + ether_addr_copy(mac_addr, addrs.pf_lan_mac); return status; } @@ -1061,7 +1058,7 @@ i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr) return status; if (flags & I40E_AQC_PORT_ADDR_VALID) - memcpy(mac_addr, &addrs.port_mac, sizeof(addrs.port_mac)); + ether_addr_copy(mac_addr, addrs.port_mac); else status = I40E_ERR_INVALID_MAC_ADDR; @@ -1119,7 +1116,7 @@ i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr) return status; if (flags & I40E_AQC_SAN_ADDR_VALID) - memcpy(mac_addr, &addrs.pf_san_mac, sizeof(addrs.pf_san_mac)); + ether_addr_copy(mac_addr, addrs.pf_san_mac); else status = I40E_ERR_INVALID_MAC_ADDR; @@ -1260,7 +1257,7 @@ i40e_status i40e_pf_reset(struct i40e_hw *hw) grst_del = (rd32(hw, I40E_GLGEN_RSTCTL) & I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >> I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT; - for (cnt = 0; cnt < grst_del + 2; cnt++) { + for (cnt = 0; cnt < grst_del + 10; cnt++) { reg = rd32(hw, I40E_GLGEN_RSTAT); if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK)) break; @@ -1620,6 +1617,9 @@ i40e_status i40e_aq_get_phy_capabilities(struct i40e_hw *hw, if (hw->aq.asq_last_status == I40E_AQ_RC_EIO) status = I40E_ERR_UNKNOWN_PHY; + if (report_init) + hw->phy.phy_types = le32_to_cpu(abilities->phy_type); + return status; } @@ -1720,14 +1720,14 @@ enum i40e_status_code i40e_set_fc(struct i40e_hw *hw, u8 *aq_failures, *aq_failures |= I40E_SET_FC_AQ_FAIL_SET; } /* Update the link info */ - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); if (status) { /* Wait a little bit (on 40G cards it sometimes takes a really * long time for link to come back from the atomic reset) * and try once more */ msleep(1000); - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); } if (status) *aq_failures |= I40E_SET_FC_AQ_FAIL_UPDATE; @@ -2238,27 +2238,52 @@ i40e_status i40e_aq_send_driver_version(struct i40e_hw *hw, /** * i40e_get_link_status - get status of the HW network link * @hw: pointer to the hw struct + * @link_up: pointer to bool (true/false = linkup/linkdown) * - * Returns true if link is up, false if link is down. + * Variable link_up true if link is up, false if link is down. + * The variable link_up is invalid if returned value of status != 0 * * Side effect: LinkStatusEvent reporting becomes enabled **/ -bool i40e_get_link_status(struct i40e_hw *hw) +i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up) { i40e_status status = 0; - bool link_status = false; if (hw->phy.get_link_info) { - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); if (status) - goto i40e_get_link_status_exit; + i40e_debug(hw, I40E_DEBUG_LINK, "get link failed: status %d\n", + status); } - link_status = hw->phy.link_info.link_info & I40E_AQ_LINK_UP; + *link_up = hw->phy.link_info.link_info & I40E_AQ_LINK_UP; + + return status; +} + +/** + * i40e_updatelink_status - update status of the HW network link + * @hw: pointer to the hw struct + **/ +i40e_status i40e_update_link_info(struct i40e_hw *hw) +{ + struct i40e_aq_get_phy_abilities_resp abilities; + i40e_status status = 0; + + status = i40e_aq_get_link_info(hw, true, NULL, NULL); + if (status) + return status; -i40e_get_link_status_exit: - return link_status; + status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, + NULL); + if (status) + return status; + + memcpy(hw->phy.link_info.module_type, &abilities.module_type, + sizeof(hw->phy.link_info.module_type)); + + return status; } /** @@ -2365,6 +2390,7 @@ i40e_status i40e_aq_get_veb_parameters(struct i40e_hw *hw, *vebs_free = le16_to_cpu(cmd_resp->vebs_free); if (floating) { u16 flags = le16_to_cpu(cmd_resp->veb_flags); + if (flags & I40E_AQC_ADD_VEB_FLOATING) *floating = true; else @@ -3779,7 +3805,7 @@ i40e_status i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw, } if (mac_addr) - memcpy(cmd->mac, mac_addr, ETH_ALEN); + ether_addr_copy(cmd->mac, mac_addr); cmd->etype = cpu_to_le16(ethtype); cmd->flags = cpu_to_le16(flags); diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.c b/drivers/net/ethernet/intel/i40e/i40e_dcb.c index 90de46aef557..2691277c0055 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.c @@ -292,6 +292,190 @@ static void i40e_parse_ieee_tlv(struct i40e_lldp_org_tlv *tlv, } /** + * i40e_parse_cee_pgcfg_tlv + * @tlv: CEE DCBX PG CFG TLV + * @dcbcfg: Local store to update ETS CFG data + * + * Parses CEE DCBX PG CFG TLV + **/ +static void i40e_parse_cee_pgcfg_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + struct i40e_dcb_ets_config *etscfg; + u8 *buf = tlv->tlvinfo; + u16 offset = 0; + u8 priority; + int i; + + etscfg = &dcbcfg->etscfg; + + if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK) + etscfg->willing = 1; + + etscfg->cbs = 0; + /* Priority Group Table (4 octets) + * Octets:| 1 | 2 | 3 | 4 | + * ----------------------------------------- + * |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7| + * ----------------------------------------- + * Bits:|7 4|3 0|7 4|3 0|7 4|3 0|7 4|3 0| + * ----------------------------------------- + */ + for (i = 0; i < 4; i++) { + priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_1_MASK) >> + I40E_CEE_PGID_PRIO_1_SHIFT); + etscfg->prioritytable[i * 2] = priority; + priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_0_MASK) >> + I40E_CEE_PGID_PRIO_0_SHIFT); + etscfg->prioritytable[i * 2 + 1] = priority; + offset++; + } + + /* PG Percentage Table (8 octets) + * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | + * --------------------------------- + * |pg0|pg1|pg2|pg3|pg4|pg5|pg6|pg7| + * --------------------------------- + */ + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) + etscfg->tcbwtable[i] = buf[offset++]; + + /* Number of TCs supported (1 octet) */ + etscfg->maxtcs = buf[offset]; +} + +/** + * i40e_parse_cee_pfccfg_tlv + * @tlv: CEE DCBX PFC CFG TLV + * @dcbcfg: Local store to update PFC CFG data + * + * Parses CEE DCBX PFC CFG TLV + **/ +static void i40e_parse_cee_pfccfg_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u8 *buf = tlv->tlvinfo; + + if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK) + dcbcfg->pfc.willing = 1; + + /* ------------------------ + * | PFC Enable | PFC TCs | + * ------------------------ + * | 1 octet | 1 octet | + */ + dcbcfg->pfc.pfcenable = buf[0]; + dcbcfg->pfc.pfccap = buf[1]; +} + +/** + * i40e_parse_cee_app_tlv + * @tlv: CEE DCBX APP TLV + * @dcbcfg: Local store to update APP PRIO data + * + * Parses CEE DCBX APP PRIO TLV + **/ +static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u16 length, typelength, offset = 0; + struct i40e_cee_app_prio *app; + u8 i, up, selector; + + typelength = ntohs(tlv->hdr.typelen); + length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + + dcbcfg->numapps = length / sizeof(*app); + if (!dcbcfg->numapps) + return; + + for (i = 0; i < dcbcfg->numapps; i++) { + app = (struct i40e_cee_app_prio *)(tlv->tlvinfo + offset); + for (up = 0; up < I40E_MAX_USER_PRIORITY; up++) { + if (app->prio_map & BIT(up)) + break; + } + dcbcfg->app[i].priority = up; + + /* Get Selector from lower 2 bits, and convert to IEEE */ + selector = (app->upper_oui_sel & I40E_CEE_APP_SELECTOR_MASK); + if (selector == I40E_CEE_APP_SEL_ETHTYPE) + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + else if (selector == I40E_CEE_APP_SEL_TCPIP) + dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP; + else + /* Keep selector as it is for unknown types */ + dcbcfg->app[i].selector = selector; + + dcbcfg->app[i].protocolid = ntohs(app->protocol); + /* Move to next app */ + offset += sizeof(*app); + } +} + +/** + * i40e_parse_cee_tlv + * @tlv: CEE DCBX TLV + * @dcbcfg: Local store to update DCBX config data + * + * Get the TLV subtype and send it to parsing function + * based on the subtype value + **/ +static void i40e_parse_cee_tlv(struct i40e_lldp_org_tlv *tlv, + struct i40e_dcbx_config *dcbcfg) +{ + u16 len, tlvlen, sublen, typelength; + struct i40e_cee_feat_tlv *sub_tlv; + u8 subtype, feat_tlv_count = 0; + u32 ouisubtype; + + ouisubtype = ntohl(tlv->ouisubtype); + subtype = (u8)((ouisubtype & I40E_LLDP_TLV_SUBTYPE_MASK) >> + I40E_LLDP_TLV_SUBTYPE_SHIFT); + /* Return if not CEE DCBX */ + if (subtype != I40E_CEE_DCBX_TYPE) + return; + + typelength = ntohs(tlv->typelength); + tlvlen = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + len = sizeof(tlv->typelength) + sizeof(ouisubtype) + + sizeof(struct i40e_cee_ctrl_tlv); + /* Return if no CEE DCBX Feature TLVs */ + if (tlvlen <= len) + return; + + sub_tlv = (struct i40e_cee_feat_tlv *)((char *)tlv + len); + while (feat_tlv_count < I40E_CEE_MAX_FEAT_TYPE) { + typelength = ntohs(sub_tlv->hdr.typelen); + sublen = (u16)((typelength & + I40E_LLDP_TLV_LEN_MASK) >> + I40E_LLDP_TLV_LEN_SHIFT); + subtype = (u8)((typelength & I40E_LLDP_TLV_TYPE_MASK) >> + I40E_LLDP_TLV_TYPE_SHIFT); + switch (subtype) { + case I40E_CEE_SUBTYPE_PG_CFG: + i40e_parse_cee_pgcfg_tlv(sub_tlv, dcbcfg); + break; + case I40E_CEE_SUBTYPE_PFC_CFG: + i40e_parse_cee_pfccfg_tlv(sub_tlv, dcbcfg); + break; + case I40E_CEE_SUBTYPE_APP_PRI: + i40e_parse_cee_app_tlv(sub_tlv, dcbcfg); + break; + default: + return; /* Invalid Sub-type return */ + } + feat_tlv_count++; + /* Move to next sub TLV */ + sub_tlv = (struct i40e_cee_feat_tlv *)((char *)sub_tlv + + sizeof(sub_tlv->hdr.typelen) + + sublen); + } +} + +/** * i40e_parse_org_tlv * @tlv: Organization specific TLV * @dcbcfg: Local store to update ETS REC data @@ -312,6 +496,9 @@ static void i40e_parse_org_tlv(struct i40e_lldp_org_tlv *tlv, case I40E_IEEE_8021QAZ_OUI: i40e_parse_ieee_tlv(tlv, dcbcfg); break; + case I40E_CEE_DCBX_OUI: + i40e_parse_cee_tlv(tlv, dcbcfg); + break; default: break; } @@ -502,15 +689,18 @@ static void i40e_cee_to_dcb_config( /* CEE PG data to ETS config */ dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc; + /* Note that the FW creates the oper_prio_tc nibbles reversed + * from those in the CEE Priority Group sub-TLV. + */ for (i = 0; i < 4; i++) { tc = (u8)((cee_cfg->oper_prio_tc[i] & - I40E_CEE_PGID_PRIO_1_MASK) >> - I40E_CEE_PGID_PRIO_1_SHIFT); - dcbcfg->etscfg.prioritytable[i*2] = tc; - tc = (u8)((cee_cfg->oper_prio_tc[i] & I40E_CEE_PGID_PRIO_0_MASK) >> I40E_CEE_PGID_PRIO_0_SHIFT); - dcbcfg->etscfg.prioritytable[i*2 + 1] = tc; + dcbcfg->etscfg.prioritytable[i * 2] = tc; + tc = (u8)((cee_cfg->oper_prio_tc[i] & + I40E_CEE_PGID_PRIO_1_MASK) >> + I40E_CEE_PGID_PRIO_1_SHIFT); + dcbcfg->etscfg.prioritytable[i * 2 + 1] = tc; } for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) @@ -531,37 +721,85 @@ static void i40e_cee_to_dcb_config( dcbcfg->pfc.pfcenable = cee_cfg->oper_pfc_en; dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS; - status = (tlv_status & I40E_AQC_CEE_APP_STATUS_MASK) >> - I40E_AQC_CEE_APP_STATUS_SHIFT; + i = 0; + status = (tlv_status & I40E_AQC_CEE_FCOE_STATUS_MASK) >> + I40E_AQC_CEE_FCOE_STATUS_SHIFT; err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; - /* Add APPs if Error is False and Oper/Sync is True */ + /* Add FCoE APP if Error is False and Oper/Sync is True */ if (!err && sync && oper) { - /* CEE operating configuration supports FCoE/iSCSI/FIP only */ - dcbcfg->numapps = I40E_CEE_OPER_MAX_APPS; - /* FCoE APP */ - dcbcfg->app[0].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_FCOE_MASK) >> I40E_AQC_CEE_APP_FCOE_SHIFT; - dcbcfg->app[0].selector = I40E_APP_SEL_ETHTYPE; - dcbcfg->app[0].protocolid = I40E_APP_PROTOID_FCOE; + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FCOE; + i++; + } + status = (tlv_status & I40E_AQC_CEE_ISCSI_STATUS_MASK) >> + I40E_AQC_CEE_ISCSI_STATUS_SHIFT; + err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; + sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; + oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; + /* Add iSCSI APP if Error is False and Oper/Sync is True */ + if (!err && sync && oper) { /* iSCSI APP */ - dcbcfg->app[1].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_ISCSI_MASK) >> I40E_AQC_CEE_APP_ISCSI_SHIFT; - dcbcfg->app[1].selector = I40E_APP_SEL_TCPIP; - dcbcfg->app[1].protocolid = I40E_APP_PROTOID_ISCSI; + dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_ISCSI; + i++; + } + status = (tlv_status & I40E_AQC_CEE_FIP_STATUS_MASK) >> + I40E_AQC_CEE_FIP_STATUS_SHIFT; + err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0; + sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0; + oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0; + /* Add FIP APP if Error is False and Oper/Sync is True */ + if (!err && sync && oper) { /* FIP APP */ - dcbcfg->app[2].priority = + dcbcfg->app[i].priority = (app_prio & I40E_AQC_CEE_APP_FIP_MASK) >> I40E_AQC_CEE_APP_FIP_SHIFT; - dcbcfg->app[2].selector = I40E_APP_SEL_ETHTYPE; - dcbcfg->app[2].protocolid = I40E_APP_PROTOID_FIP; + dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE; + dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FIP; + i++; } + dcbcfg->numapps = i; +} + +/** + * i40e_get_ieee_dcb_config + * @hw: pointer to the hw struct + * + * Get IEEE mode DCB configuration from the Firmware + **/ +static i40e_status i40e_get_ieee_dcb_config(struct i40e_hw *hw) +{ + i40e_status ret = 0; + + /* IEEE mode */ + hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE; + /* Get Local DCB Config */ + ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0, + &hw->local_dcbx_config); + if (ret) + goto out; + + /* Get Remote DCB Config */ + ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, + I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE, + &hw->remote_dcbx_config); + /* Don't treat ENOENT as an error for Remote MIBs */ + if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) + ret = 0; + +out: + return ret; } /** @@ -579,7 +817,7 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) /* If Firmware version < v4.33 IEEE only */ if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) || (hw->aq.fw_maj_ver < 4)) - goto ieee; + return i40e_get_ieee_dcb_config(hw); /* If Firmware version == v4.33 use old CEE struct */ if ((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver == 33)) { @@ -608,16 +846,14 @@ i40e_status i40e_get_dcb_config(struct i40e_hw *hw) /* CEE mode not enabled try querying IEEE data */ if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) - goto ieee; - else + return i40e_get_ieee_dcb_config(hw); + + if (ret) goto out; -ieee: - /* IEEE mode */ - hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE; - /* Get Local DCB Config */ + /* Get CEE DCB Desired Config */ ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0, - &hw->local_dcbx_config); + &hw->desired_dcbx_config); if (ret) goto out; diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb.h b/drivers/net/ethernet/intel/i40e/i40e_dcb.h index 50fc894a4cde..92d01042c1f6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb.h +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb.h @@ -44,6 +44,15 @@ #define I40E_IEEE_SUBTYPE_PFC_CFG 11 #define I40E_IEEE_SUBTYPE_APP_PRI 12 +#define I40E_CEE_DCBX_OUI 0x001b21 +#define I40E_CEE_DCBX_TYPE 2 + +#define I40E_CEE_SUBTYPE_CTRL 1 +#define I40E_CEE_SUBTYPE_PG_CFG 2 +#define I40E_CEE_SUBTYPE_PFC_CFG 3 +#define I40E_CEE_SUBTYPE_APP_PRI 4 + +#define I40E_CEE_MAX_FEAT_TYPE 3 /* Defines for LLDP TLV header */ #define I40E_LLDP_TLV_LEN_SHIFT 0 #define I40E_LLDP_TLV_LEN_MASK (0x01FF << I40E_LLDP_TLV_LEN_SHIFT) @@ -98,6 +107,36 @@ struct i40e_lldp_org_tlv { __be32 ouisubtype; u8 tlvinfo[1]; }; + +struct i40e_cee_tlv_hdr { + __be16 typelen; + u8 operver; + u8 maxver; +}; + +struct i40e_cee_ctrl_tlv { + struct i40e_cee_tlv_hdr hdr; + __be32 seqno; + __be32 ackno; +}; + +struct i40e_cee_feat_tlv { + struct i40e_cee_tlv_hdr hdr; + u8 en_will_err; /* Bits: |En|Will|Err|Reserved(5)| */ +#define I40E_CEE_FEAT_TLV_ENABLE_MASK 0x80 +#define I40E_CEE_FEAT_TLV_WILLING_MASK 0x40 +#define I40E_CEE_FEAT_TLV_ERR_MASK 0x20 + u8 subtype; + u8 tlvinfo[1]; +}; + +struct i40e_cee_app_prio { + __be16 protocol; + u8 upper_oui_sel; /* Bits: |Upper OUI(6)|Selector(2)| */ +#define I40E_CEE_APP_SELECTOR_MASK 0x03 + __be16 lower_oui; + u8 prio_map; +}; #pragma pack() i40e_status i40e_get_dcbx_status(struct i40e_hw *hw, diff --git a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c index 1c51f736a8d0..886e667f2f1c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c +++ b/drivers/net/ethernet/intel/i40e/i40e_dcb_nl.c @@ -236,14 +236,13 @@ static void i40e_dcbnl_del_app(struct i40e_pf *pf, struct i40e_dcb_app_priority_table *app) { int v, err; + for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && pf->vsi[v]->netdev) { err = i40e_dcbnl_vsi_del_app(pf->vsi[v], app); - if (err) - dev_info(&pf->pdev->dev, "%s: Failed deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", - __func__, pf->vsi[v]->seid, - err, app->selector, - app->protocolid, app->priority); + dev_dbg(&pf->pdev->dev, "Deleting app for VSI seid=%d err=%d sel=%d proto=0x%x prio=%d\n", + pf->vsi[v]->seid, err, app->selector, + app->protocolid, app->priority); } } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index d7c15d17faa6..d15bd62eb874 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -404,82 +404,82 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) nstat = i40e_get_vsi_stats_struct(vsi); dev_info(&pf->pdev->dev, " net_stats: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n", - (long unsigned int)nstat->rx_packets, - (long unsigned int)nstat->rx_bytes, - (long unsigned int)nstat->rx_errors, - (long unsigned int)nstat->rx_dropped); + (unsigned long int)nstat->rx_packets, + (unsigned long int)nstat->rx_bytes, + (unsigned long int)nstat->rx_errors, + (unsigned long int)nstat->rx_dropped); dev_info(&pf->pdev->dev, " net_stats: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n", - (long unsigned int)nstat->tx_packets, - (long unsigned int)nstat->tx_bytes, - (long unsigned int)nstat->tx_errors, - (long unsigned int)nstat->tx_dropped); + (unsigned long int)nstat->tx_packets, + (unsigned long int)nstat->tx_bytes, + (unsigned long int)nstat->tx_errors, + (unsigned long int)nstat->tx_dropped); dev_info(&pf->pdev->dev, " net_stats: multicast = %lu, collisions = %lu\n", - (long unsigned int)nstat->multicast, - (long unsigned int)nstat->collisions); + (unsigned long int)nstat->multicast, + (unsigned long int)nstat->collisions); dev_info(&pf->pdev->dev, " net_stats: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n", - (long unsigned int)nstat->rx_length_errors, - (long unsigned int)nstat->rx_over_errors, - (long unsigned int)nstat->rx_crc_errors); + (unsigned long int)nstat->rx_length_errors, + (unsigned long int)nstat->rx_over_errors, + (unsigned long int)nstat->rx_crc_errors); dev_info(&pf->pdev->dev, " net_stats: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n", - (long unsigned int)nstat->rx_frame_errors, - (long unsigned int)nstat->rx_fifo_errors, - (long unsigned int)nstat->rx_missed_errors); + (unsigned long int)nstat->rx_frame_errors, + (unsigned long int)nstat->rx_fifo_errors, + (unsigned long int)nstat->rx_missed_errors); dev_info(&pf->pdev->dev, " net_stats: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n", - (long unsigned int)nstat->tx_aborted_errors, - (long unsigned int)nstat->tx_carrier_errors, - (long unsigned int)nstat->tx_fifo_errors); + (unsigned long int)nstat->tx_aborted_errors, + (unsigned long int)nstat->tx_carrier_errors, + (unsigned long int)nstat->tx_fifo_errors); dev_info(&pf->pdev->dev, " net_stats: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n", - (long unsigned int)nstat->tx_heartbeat_errors, - (long unsigned int)nstat->tx_window_errors); + (unsigned long int)nstat->tx_heartbeat_errors, + (unsigned long int)nstat->tx_window_errors); dev_info(&pf->pdev->dev, " net_stats: rx_compressed = %lu, tx_compressed = %lu\n", - (long unsigned int)nstat->rx_compressed, - (long unsigned int)nstat->tx_compressed); + (unsigned long int)nstat->rx_compressed, + (unsigned long int)nstat->tx_compressed); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_packets = %lu, rx_bytes = %lu, rx_errors = %lu, rx_dropped = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_packets, - (long unsigned int)vsi->net_stats_offsets.rx_bytes, - (long unsigned int)vsi->net_stats_offsets.rx_errors, - (long unsigned int)vsi->net_stats_offsets.rx_dropped); + (unsigned long int)vsi->net_stats_offsets.rx_packets, + (unsigned long int)vsi->net_stats_offsets.rx_bytes, + (unsigned long int)vsi->net_stats_offsets.rx_errors, + (unsigned long int)vsi->net_stats_offsets.rx_dropped); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_packets = %lu, tx_bytes = %lu, tx_errors = %lu, tx_dropped = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_packets, - (long unsigned int)vsi->net_stats_offsets.tx_bytes, - (long unsigned int)vsi->net_stats_offsets.tx_errors, - (long unsigned int)vsi->net_stats_offsets.tx_dropped); + (unsigned long int)vsi->net_stats_offsets.tx_packets, + (unsigned long int)vsi->net_stats_offsets.tx_bytes, + (unsigned long int)vsi->net_stats_offsets.tx_errors, + (unsigned long int)vsi->net_stats_offsets.tx_dropped); dev_info(&pf->pdev->dev, " net_stats_offsets: multicast = %lu, collisions = %lu\n", - (long unsigned int)vsi->net_stats_offsets.multicast, - (long unsigned int)vsi->net_stats_offsets.collisions); + (unsigned long int)vsi->net_stats_offsets.multicast, + (unsigned long int)vsi->net_stats_offsets.collisions); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_length_errors = %lu, rx_over_errors = %lu, rx_crc_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_length_errors, - (long unsigned int)vsi->net_stats_offsets.rx_over_errors, - (long unsigned int)vsi->net_stats_offsets.rx_crc_errors); + (unsigned long int)vsi->net_stats_offsets.rx_length_errors, + (unsigned long int)vsi->net_stats_offsets.rx_over_errors, + (unsigned long int)vsi->net_stats_offsets.rx_crc_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_frame_errors = %lu, rx_fifo_errors = %lu, rx_missed_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_frame_errors, - (long unsigned int)vsi->net_stats_offsets.rx_fifo_errors, - (long unsigned int)vsi->net_stats_offsets.rx_missed_errors); + (unsigned long int)vsi->net_stats_offsets.rx_frame_errors, + (unsigned long int)vsi->net_stats_offsets.rx_fifo_errors, + (unsigned long int)vsi->net_stats_offsets.rx_missed_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_aborted_errors = %lu, tx_carrier_errors = %lu, tx_fifo_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_aborted_errors, - (long unsigned int)vsi->net_stats_offsets.tx_carrier_errors, - (long unsigned int)vsi->net_stats_offsets.tx_fifo_errors); + (unsigned long int)vsi->net_stats_offsets.tx_aborted_errors, + (unsigned long int)vsi->net_stats_offsets.tx_carrier_errors, + (unsigned long int)vsi->net_stats_offsets.tx_fifo_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: tx_heartbeat_errors = %lu, tx_window_errors = %lu\n", - (long unsigned int)vsi->net_stats_offsets.tx_heartbeat_errors, - (long unsigned int)vsi->net_stats_offsets.tx_window_errors); + (unsigned long int)vsi->net_stats_offsets.tx_heartbeat_errors, + (unsigned long int)vsi->net_stats_offsets.tx_window_errors); dev_info(&pf->pdev->dev, " net_stats_offsets: rx_compressed = %lu, tx_compressed = %lu\n", - (long unsigned int)vsi->net_stats_offsets.rx_compressed, - (long unsigned int)vsi->net_stats_offsets.tx_compressed); + (unsigned long int)vsi->net_stats_offsets.rx_compressed, + (unsigned long int)vsi->net_stats_offsets.tx_compressed); dev_info(&pf->pdev->dev, " tx_restart = %d, tx_busy = %d, rx_buf_failed = %d, rx_page_failed = %d\n", vsi->tx_restart, vsi->tx_busy, @@ -487,6 +487,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) rcu_read_lock(); for (i = 0; i < vsi->num_queue_pairs; i++) { struct i40e_ring *rx_ring = ACCESS_ONCE(vsi->rx_rings[i]); + if (!rx_ring) continue; @@ -527,7 +528,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " rx_rings[%i]: size = %i, dma = 0x%08lx\n", i, rx_ring->size, - (long unsigned int)rx_ring->dma); + (unsigned long int)rx_ring->dma); dev_info(&pf->pdev->dev, " rx_rings[%i]: vsi = %p, q_vector = %p\n", i, rx_ring->vsi, @@ -535,6 +536,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) } for (i = 0; i < vsi->num_queue_pairs; i++) { struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[i]); + if (!tx_ring) continue; @@ -573,7 +575,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " tx_rings[%i]: size = %i, dma = 0x%08lx\n", i, tx_ring->size, - (long unsigned int)tx_ring->dma); + (unsigned long int)tx_ring->dma); dev_info(&pf->pdev->dev, " tx_rings[%i]: vsi = %p, q_vector = %p\n", i, tx_ring->vsi, @@ -743,6 +745,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf) ring = &(hw->aq.asq); for (i = 0; i < ring->count; i++) { struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i); + dev_info(&pf->pdev->dev, " at[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n", i, d->flags, d->opcode, d->datalen, d->retval, @@ -755,6 +758,7 @@ static void i40e_dbg_dump_aq_desc(struct i40e_pf *pf) ring = &(hw->aq.arq); for (i = 0; i < ring->count; i++) { struct i40e_aq_desc *d = I40E_ADMINQ_DESC(*ring, i); + dev_info(&pf->pdev->dev, " ar[%02d] flags=0x%04x op=0x%04x dlen=0x%04x ret=0x%04x cookie_h=0x%08x cookie_l=0x%08x\n", i, d->flags, d->opcode, d->datalen, d->retval, @@ -949,24 +953,6 @@ static void i40e_dbg_dump_veb_all(struct i40e_pf *pf) } } -/** - * i40e_dbg_cmd_fd_ctrl - Enable/disable FD sideband/ATR - * @pf: the PF that would be altered - * @flag: flag that needs enabling or disabling - * @enable: Enable/disable FD SD/ATR - **/ -static void i40e_dbg_cmd_fd_ctrl(struct i40e_pf *pf, u64 flag, bool enable) -{ - if (enable) { - pf->flags |= flag; - } else { - pf->flags &= ~flag; - pf->auto_disable_flags |= flag; - } - dev_info(&pf->pdev->dev, "requesting a PF reset\n"); - i40e_do_reset_safe(pf, BIT(__I40E_PF_RESET_REQUESTED)); -} - #define I40E_MAX_DEBUG_OUT_BUFFER (4096*4) /** * i40e_dbg_command_write - write into command datum @@ -1038,7 +1024,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, "'%s' failed\n", cmd_buf); } else if (strncmp(cmd_buf, "del vsi", 7) == 0) { - sscanf(&cmd_buf[7], "%i", &vsi_seid); + cnt = sscanf(&cmd_buf[7], "%i", &vsi_seid); + if (cnt != 1) { + dev_info(&pf->pdev->dev, + "del vsi: bad command string, cnt=%d\n", + cnt); + goto command_write_done; + } vsi = i40e_dbg_find_vsi(pf, vsi_seid); if (!vsi) { dev_info(&pf->pdev->dev, "del VSI %d: seid not found\n", @@ -1146,7 +1138,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } f = i40e_add_filter(vsi, ma, vlan, false, false); - ret = i40e_sync_vsi_filters(vsi); + ret = i40e_sync_vsi_filters(vsi, true); if (f && !ret) dev_info(&pf->pdev->dev, "add macaddr: %pM vlan=%d added to VSI %d\n", @@ -1183,7 +1175,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } i40e_del_filter(vsi, ma, vlan, false, false); - ret = i40e_sync_vsi_filters(vsi); + ret = i40e_sync_vsi_filters(vsi, true); if (!ret) dev_info(&pf->pdev->dev, "del macaddr: %pM vlan=%d removed from VSI %d\n", @@ -1488,6 +1480,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "read", 4) == 0) { u32 address; u32 value; + cnt = sscanf(&cmd_buf[4], "%i", &address); if (cnt != 1) { dev_info(&pf->pdev->dev, "read <reg>\n"); @@ -1495,9 +1488,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } /* check the range on address */ - if (address >= I40E_MAX_REGISTER) { - dev_info(&pf->pdev->dev, "read reg address 0x%08x too large\n", - address); + if (address > (pf->ioremap_len - sizeof(u32))) { + dev_info(&pf->pdev->dev, "read reg address 0x%08x too large, max=0x%08lx\n", + address, (unsigned long int)(pf->ioremap_len - sizeof(u32))); goto command_write_done; } @@ -1507,6 +1500,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if (strncmp(cmd_buf, "write", 5) == 0) { u32 address, value; + cnt = sscanf(&cmd_buf[5], "%i %i", &address, &value); if (cnt != 2) { dev_info(&pf->pdev->dev, "write <reg> <value>\n"); @@ -1514,9 +1508,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } /* check the range on address */ - if (address >= I40E_MAX_REGISTER) { - dev_info(&pf->pdev->dev, "write reg address 0x%08x too large\n", - address); + if (address > (pf->ioremap_len - sizeof(u32))) { + dev_info(&pf->pdev->dev, "write reg address 0x%08x too large, max=0x%08lx\n", + address, (unsigned long int)(pf->ioremap_len - sizeof(u32))); goto command_write_done; } wr32(&pf->hw, address, value); @@ -1528,6 +1522,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, cnt = sscanf(&cmd_buf[15], "%i", &vsi_seid); if (cnt == 0) { int i; + for (i = 0; i < pf->num_alloc_vsi; i++) i40e_vsi_reset_stats(pf->vsi[i]); dev_info(&pf->pdev->dev, "vsi clear stats called for all vsi's\n"); @@ -1726,8 +1721,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, packet_len, I40E_FDIR_MAX_RAW_PACKET_SIZE); for (i = 0; i < packet_len; i++) { - sscanf(&asc_packet[j], "%2hhx ", - &raw_packet[i]); + cnt = sscanf(&asc_packet[j], "%2hhx ", &raw_packet[i]); + if (!cnt) + break; j += 3; } dev_info(&pf->pdev->dev, "FD raw packet dump\n"); @@ -1745,16 +1741,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp, raw_packet = NULL; kfree(asc_packet); asc_packet = NULL; - } else if (strncmp(cmd_buf, "fd-atr off", 10) == 0) { - i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, false); - } else if (strncmp(cmd_buf, "fd-atr on", 9) == 0) { - i40e_dbg_cmd_fd_ctrl(pf, I40E_FLAG_FD_ATR_ENABLED, true); } else if (strncmp(cmd_buf, "fd current cnt", 14) == 0) { dev_info(&pf->pdev->dev, "FD current total filter count for this interface: %d\n", i40e_get_current_fd_count(pf)); } else if (strncmp(cmd_buf, "lldp", 4) == 0) { if (strncmp(&cmd_buf[5], "stop", 4) == 0) { int ret; + ret = i40e_aq_stop_lldp(&pf->hw, false, NULL); if (ret) { dev_info(&pf->pdev->dev, @@ -1779,6 +1772,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, #endif /* CONFIG_I40E_DCB */ } else if (strncmp(&cmd_buf[5], "start", 5) == 0) { int ret; + ret = i40e_aq_add_rem_control_packet_filter(&pf->hw, pf->hw.mac.addr, I40E_ETH_P_LLDP, 0, @@ -1807,6 +1801,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, u16 llen, rlen; int ret; u8 *buff; + buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1833,6 +1828,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, u16 llen, rlen; int ret; u8 *buff; + buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1858,6 +1854,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, buff = NULL; } else if (strncmp(&cmd_buf[5], "event on", 8) == 0) { int ret; + ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw, true, NULL); if (ret) { @@ -1868,6 +1865,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } } else if (strncmp(&cmd_buf[5], "event off", 9) == 0) { int ret; + ret = i40e_aq_cfg_lldp_mib_change_event(&pf->hw, false, NULL); if (ret) { @@ -1969,8 +1967,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " send indirect aq_cmd <flags> <opcode> <datalen> <retval> <cookie_h> <cookie_l> <param0> <param1> <param2> <param3> <buffer_len>\n"); dev_info(&pf->pdev->dev, " add fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n"); dev_info(&pf->pdev->dev, " rem fd_filter <dest q_index> <flex_off> <pctype> <dest_vsi> <dest_ctl> <fd_status> <cnt_index> <fd_id> <packet_len> <packet>\n"); - dev_info(&pf->pdev->dev, " fd-atr off\n"); - dev_info(&pf->pdev->dev, " fd-atr on\n"); dev_info(&pf->pdev->dev, " fd current cnt"); dev_info(&pf->pdev->dev, " lldp start\n"); dev_info(&pf->pdev->dev, " lldp stop\n"); @@ -2105,6 +2101,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, } } else if (strncmp(i40e_dbg_netdev_ops_buf, "change_mtu", 10) == 0) { int mtu; + cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i %i", &vsi_seid, &mtu); if (cnt != 2) { @@ -2220,7 +2217,6 @@ void i40e_dbg_pf_init(struct i40e_pf *pf) create_failed: dev_info(dev, "debugfs dir/file for %s failed\n", name); debugfs_remove_recursive(pf->i40e_dbg_pf); - return; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h new file mode 100644 index 000000000000..c601ca4a610c --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * + * Intel Ethernet Controller XL710 Family Linux Driver + * Copyright(c) 2013 - 2015 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Contact Information: + * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + ******************************************************************************/ + +#ifndef _I40E_DEVIDS_H_ +#define _I40E_DEVIDS_H_ + +/* Device IDs */ +#define I40E_DEV_ID_SFP_XL710 0x1572 +#define I40E_DEV_ID_QEMU 0x1574 +#define I40E_DEV_ID_KX_A 0x157F +#define I40E_DEV_ID_KX_B 0x1580 +#define I40E_DEV_ID_KX_C 0x1581 +#define I40E_DEV_ID_QSFP_A 0x1583 +#define I40E_DEV_ID_QSFP_B 0x1584 +#define I40E_DEV_ID_QSFP_C 0x1585 +#define I40E_DEV_ID_10G_BASE_T 0x1586 +#define I40E_DEV_ID_20G_KR2 0x1587 +#define I40E_DEV_ID_20G_KR2_A 0x1588 +#define I40E_DEV_ID_10G_BASE_T4 0x1589 +#define I40E_DEV_ID_VF 0x154C +#define I40E_DEV_ID_VF_HV 0x1571 +#define I40E_DEV_ID_SFP_X722 0x37D0 +#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 +#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 +#define I40E_DEV_ID_X722_VF 0x37CD +#define I40E_DEV_ID_X722_VF_HV 0x37D9 + +#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ + (d) == I40E_DEV_ID_QSFP_B || \ + (d) == I40E_DEV_ID_QSFP_C) + +#endif /* _I40E_DEVIDS_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index e972b5ecbf0b..767b1db1005f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -87,11 +87,9 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("rx_broadcast", eth_stats.rx_broadcast), I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast), I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol), + I40E_VSI_STAT("tx_linearize", tx_linearize), }; -static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, - struct ethtool_rxnfc *cmd); - /* These PF_STATs might look like duplicates of some NETDEV_STATs, * but they are separate. This device supports Virtualization, and * as such might have several netdevs supporting VMDq and FCoE going @@ -229,10 +227,12 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "NPAR", + "LinkPolling", + "flow-director-atr", + "veb-stats", }; -#define I40E_PRIV_FLAGS_STR_LEN \ - (sizeof(i40e_priv_flags_strings) / ETH_GSTRING_LEN) +#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) /** * i40e_partition_setting_complaint - generic complaint for MFP restriction @@ -253,7 +253,8 @@ static void i40e_partition_setting_complaint(struct i40e_pf *pf) **/ static void i40e_get_settings_link_up(struct i40e_hw *hw, struct ethtool_cmd *ecmd, - struct net_device *netdev) + struct net_device *netdev, + struct i40e_pf *pf) { struct i40e_link_status *hw_link_info = &hw->phy.link_info; u32 link_speed = hw_link_info->link_speed; @@ -272,65 +273,49 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, case I40E_PHY_TYPE_40GBASE_AOC: ecmd->supported = SUPPORTED_40000baseCR4_Full; break; - case I40E_PHY_TYPE_40GBASE_KR4: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_40000baseKR4_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_40000baseKR4_Full; - break; case I40E_PHY_TYPE_40GBASE_SR4: ecmd->supported = SUPPORTED_40000baseSR4_Full; break; case I40E_PHY_TYPE_40GBASE_LR4: ecmd->supported = SUPPORTED_40000baseLR4_Full; break; - case I40E_PHY_TYPE_20GBASE_KR2: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_20000baseKR2_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_20000baseKR2_Full; - break; - case I40E_PHY_TYPE_10GBASE_KX4: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_10000baseKX4_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_10000baseKX4_Full; - break; - case I40E_PHY_TYPE_10GBASE_KR: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_10000baseKR_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_10000baseKR_Full; - break; case I40E_PHY_TYPE_10GBASE_SR: case I40E_PHY_TYPE_10GBASE_LR: case I40E_PHY_TYPE_1000BASE_SX: case I40E_PHY_TYPE_1000BASE_LX: - ecmd->supported = SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full; + ecmd->supported = SUPPORTED_10000baseT_Full; + if (hw_link_info->module_type[2] & + I40E_MODULE_TYPE_1000BASE_SX || + hw_link_info->module_type[2] & + I40E_MODULE_TYPE_1000BASE_LX) { + ecmd->supported |= SUPPORTED_1000baseT_Full; + if (hw_link_info->requested_speeds & + I40E_LINK_SPEED_1GB) + ecmd->advertising |= ADVERTISED_1000baseT_Full; + } if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; - break; - case I40E_PHY_TYPE_1000BASE_KX: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseKX_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_1000baseKX_Full; break; case I40E_PHY_TYPE_10GBASE_T: case I40E_PHY_TYPE_1000BASE_T: - case I40E_PHY_TYPE_100BASE_TX: ecmd->supported = SUPPORTED_Autoneg | SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; + SUPPORTED_1000baseT_Full; ecmd->advertising = ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) ecmd->advertising |= ADVERTISED_10000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) ecmd->advertising |= ADVERTISED_1000baseT_Full; + break; + case I40E_PHY_TYPE_1000BASE_T_OPTICAL: + ecmd->supported = SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + ecmd->advertising = ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; + break; + case I40E_PHY_TYPE_100BASE_TX: + ecmd->supported = SUPPORTED_Autoneg | + SUPPORTED_100baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) ecmd->advertising |= ADVERTISED_100baseT_Full; break; @@ -350,12 +335,24 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, break; case I40E_PHY_TYPE_SGMII: ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; + SUPPORTED_1000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) ecmd->advertising |= ADVERTISED_1000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - ecmd->advertising |= ADVERTISED_100baseT_Full; + if (pf->hw.mac.type == I40E_MAC_X722) { + ecmd->supported |= SUPPORTED_100baseT_Full; + if (hw_link_info->requested_speeds & + I40E_LINK_SPEED_100MB) + ecmd->advertising |= ADVERTISED_100baseT_Full; + } + break; + /* Backplane is set based on supported phy types in get_settings + * so don't set anything here but don't warn either + */ + case I40E_PHY_TYPE_40GBASE_KR4: + case I40E_PHY_TYPE_20GBASE_KR2: + case I40E_PHY_TYPE_10GBASE_KR: + case I40E_PHY_TYPE_10GBASE_KX4: + case I40E_PHY_TYPE_1000BASE_KX: break; default: /* if we got here and link is up something bad is afoot */ @@ -394,64 +391,73 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, * Reports link settings that can be determined when link is down **/ static void i40e_get_settings_link_down(struct i40e_hw *hw, - struct ethtool_cmd *ecmd) + struct ethtool_cmd *ecmd, + struct i40e_pf *pf) { - struct i40e_link_status *hw_link_info = &hw->phy.link_info; + enum i40e_aq_capabilities_phy_type phy_types = hw->phy.phy_types; /* link is down and the driver needs to fall back on - * device ID to determine what kinds of info to display, - * it's mostly a guess that may change when link is up + * supported phy types to figure out what info to display */ - switch (hw->device_id) { - case I40E_DEV_ID_QSFP_A: - case I40E_DEV_ID_QSFP_B: - case I40E_DEV_ID_QSFP_C: - /* pluggable QSFP */ - ecmd->supported = SUPPORTED_40000baseSR4_Full | - SUPPORTED_40000baseCR4_Full | - SUPPORTED_40000baseLR4_Full; - ecmd->advertising = ADVERTISED_40000baseSR4_Full | - ADVERTISED_40000baseCR4_Full | - ADVERTISED_40000baseLR4_Full; - break; - case I40E_DEV_ID_KX_B: - /* backplane 40G */ - ecmd->supported = SUPPORTED_40000baseKR4_Full; - ecmd->advertising = ADVERTISED_40000baseKR4_Full; - break; - case I40E_DEV_ID_KX_C: - /* backplane 10G */ - ecmd->supported = SUPPORTED_10000baseKR_Full; - ecmd->advertising = ADVERTISED_10000baseKR_Full; - break; - case I40E_DEV_ID_10G_BASE_T: - ecmd->supported = SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full; - /* Figure out what has been requested */ - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) + ecmd->supported = 0x0; + ecmd->advertising = 0x0; + if (phy_types & I40E_CAP_PHY_TYPE_SGMII) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; + if (pf->hw.mac.type == I40E_MAC_X722) { + ecmd->supported |= SUPPORTED_100baseT_Full; ecmd->advertising |= ADVERTISED_100baseT_Full; - break; - case I40E_DEV_ID_20G_KR2: - /* backplane 20G */ - ecmd->supported = SUPPORTED_20000baseKR2_Full; - ecmd->advertising = ADVERTISED_20000baseKR2_Full; - break; - default: - /* all the rest are 10G/1G */ - ecmd->supported = SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full; - /* Figure out what has been requested */ - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; - break; + } + } + if (phy_types & I40E_CAP_PHY_TYPE_XAUI || + phy_types & I40E_CAP_PHY_TYPE_XFI || + phy_types & I40E_CAP_PHY_TYPE_SFI || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC) + ecmd->supported |= SUPPORTED_10000baseT_Full; + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_T || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_10000baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_XLAUI || + phy_types & I40E_CAP_PHY_TYPE_XLPPI || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_AOC) + ecmd->supported |= SUPPORTED_40000baseCR4_Full; + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_40000baseCR4_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_40000baseCR4_Full; + } + if ((phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) && + !(phy_types & I40E_CAP_PHY_TYPE_1000BASE_T)) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_100baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_100baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) { + ecmd->supported |= SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + ecmd->advertising |= ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; } + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) + ecmd->supported |= SUPPORTED_40000baseSR4_Full; + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4) + ecmd->supported |= SUPPORTED_40000baseLR4_Full; /* With no link speed and duplex are unknown */ ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); @@ -475,12 +481,43 @@ static int i40e_get_settings(struct net_device *netdev, bool link_up = hw_link_info->link_info & I40E_AQ_LINK_UP; if (link_up) - i40e_get_settings_link_up(hw, ecmd, netdev); + i40e_get_settings_link_up(hw, ecmd, netdev, pf); else - i40e_get_settings_link_down(hw, ecmd); + i40e_get_settings_link_down(hw, ecmd, pf); /* Now set the settings that don't rely on link being up/down */ + /* For backplane, supported and advertised are only reliant on the + * phy types the NVM specifies are supported. + */ + if (hw->device_id == I40E_DEV_ID_KX_B || + hw->device_id == I40E_DEV_ID_KX_C || + hw->device_id == I40E_DEV_ID_20G_KR2 || + hw->device_id == I40E_DEV_ID_20G_KR2_A) { + ecmd->supported = SUPPORTED_Autoneg; + ecmd->advertising = ADVERTISED_Autoneg; + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4) { + ecmd->supported |= SUPPORTED_40000baseKR4_Full; + ecmd->advertising |= ADVERTISED_40000baseKR4_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2) { + ecmd->supported |= SUPPORTED_20000baseKR2_Full; + ecmd->advertising |= ADVERTISED_20000baseKR2_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { + ecmd->supported |= SUPPORTED_10000baseKR_Full; + ecmd->advertising |= ADVERTISED_10000baseKR_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { + ecmd->supported |= SUPPORTED_10000baseKX4_Full; + ecmd->advertising |= ADVERTISED_10000baseKX4_Full; + } + if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { + ecmd->supported |= SUPPORTED_1000baseKX_Full; + ecmd->advertising |= ADVERTISED_1000baseKX_Full; + } + } + /* Set autoneg settings */ ecmd->autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? AUTONEG_ENABLE : AUTONEG_DISABLE); @@ -580,6 +617,14 @@ static int i40e_set_settings(struct net_device *netdev, hw->phy.link_info.link_info & I40E_AQ_LINK_UP) return -EOPNOTSUPP; + if (hw->device_id == I40E_DEV_ID_KX_B || + hw->device_id == I40E_DEV_ID_KX_C || + hw->device_id == I40E_DEV_ID_20G_KR2 || + hw->device_id == I40E_DEV_ID_20G_KR2_A) { + netdev_info(netdev, "Changing settings is not supported on backplane.\n"); + return -EOPNOTSUPP; + } + /* get our own copy of the bits to check against */ memset(&safe_ecmd, 0, sizeof(struct ethtool_cmd)); i40e_get_settings(netdev, &safe_ecmd); @@ -664,6 +709,13 @@ static int i40e_set_settings(struct net_device *netdev, advertise & ADVERTISED_40000baseLR4_Full) config.link_speed |= I40E_LINK_SPEED_40GB; + /* If speed didn't get set, set it to what it currently is. + * This is needed because if advertise is 0 (as it is when autoneg + * is disabled) then speed won't get set. + */ + if (!config.link_speed) + config.link_speed = abilities.link_speed; + if (change || (abilities.link_speed != config.link_speed)) { /* copy over the rest of the abilities */ config.phy_type = abilities.phy_type; @@ -680,7 +732,7 @@ static int i40e_set_settings(struct net_device *netdev, /* Tell the OS link is going down, the link will go * back up when fw says it is ready asynchronously */ - netdev_info(netdev, "PHY settings change requested, NIC Link is going down.\n"); + i40e_print_link_message(vsi, false); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); } @@ -694,7 +746,7 @@ static int i40e_set_settings(struct net_device *netdev, return -EAGAIN; } - status = i40e_aq_get_link_info(hw, true, NULL, NULL); + status = i40e_update_link_info(hw); if (status) netdev_info(netdev, "Updating link info failed with err %s aq_err %s\n", i40e_stat_str(hw, status), @@ -824,7 +876,7 @@ static int i40e_set_pauseparam(struct net_device *netdev, /* Tell the OS link is going down, the link will go back up when fw * says it is ready asynchronously */ - netdev_info(netdev, "Flow control settings change requested, NIC Link is going down.\n"); + i40e_print_link_message(vsi, false); netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); @@ -948,9 +1000,7 @@ static int i40e_get_eeprom(struct net_device *netdev, cmd = (struct i40e_nvm_access *)eeprom; ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno); - if (ret_val && - ((hw->aq.asq_last_status != I40E_AQ_RC_EACCES) || - (hw->debug_mask & I40E_DEBUG_NVM))) + if (ret_val && (hw->debug_mask & I40E_DEBUG_NVM)) dev_info(&pf->pdev->dev, "NVMUpdate read failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n", ret_val, hw->aq.asq_last_status, errno, @@ -1054,10 +1104,7 @@ static int i40e_set_eeprom(struct net_device *netdev, cmd = (struct i40e_nvm_access *)eeprom; ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno); - if (ret_val && - ((hw->aq.asq_last_status != I40E_AQ_RC_EPERM && - hw->aq.asq_last_status != I40E_AQ_RC_EBUSY) || - (hw->debug_mask & I40E_DEBUG_NVM))) + if (ret_val && (hw->debug_mask & I40E_DEBUG_NVM)) dev_info(&pf->pdev->dev, "NVMUpdate write failed err=%d status=0x%x errno=%d module=%d offset=0x%x size=%d\n", ret_val, hw->aq.asq_last_status, errno, @@ -1077,11 +1124,10 @@ static void i40e_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->driver, i40e_driver_name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, i40e_driver_version_str, sizeof(drvinfo->version)); - strlcpy(drvinfo->fw_version, i40e_fw_version_str(&pf->hw), + strlcpy(drvinfo->fw_version, i40e_nvm_version_str(&pf->hw), sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(pf->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN; } static void i40e_get_ringparam(struct net_device *netdev, @@ -1166,6 +1212,11 @@ static int i40e_set_ringparam(struct net_device *netdev, /* clone ring and setup updated count */ tx_rings[i] = *vsi->tx_rings[i]; tx_rings[i].count = new_tx_count; + /* the desc and bi pointers will be reallocated in the + * setup call + */ + tx_rings[i].desc = NULL; + tx_rings[i].rx_bi = NULL; err = i40e_setup_tx_descriptors(&tx_rings[i]); if (err) { while (i) { @@ -1196,6 +1247,11 @@ static int i40e_set_ringparam(struct net_device *netdev, /* clone ring and setup updated count */ rx_rings[i] = *vsi->rx_rings[i]; rx_rings[i].count = new_rx_count; + /* the desc and bi pointers will be reallocated in the + * setup call + */ + rx_rings[i].desc = NULL; + rx_rings[i].rx_bi = NULL; err = i40e_setup_rx_descriptors(&rx_rings[i]); if (err) { while (i) { @@ -1263,7 +1319,8 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset) if (vsi == pf->vsi[pf->lan_vsi] && pf->hw.partition_id == 1) { int len = I40E_PF_STATS_LEN(netdev); - if (pf->lan_veb != I40E_NO_VEB) + if ((pf->lan_veb != I40E_NO_VEB) && + (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) len += I40E_VEB_STATS_TOTAL; return len; } else { @@ -1336,8 +1393,10 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1) return; - if (pf->lan_veb != I40E_NO_VEB) { + if ((pf->lan_veb != I40E_NO_VEB) && + (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { struct i40e_veb *veb = pf->veb[pf->lan_veb]; + for (j = 0; j < I40E_VEB_STATS_LEN; j++) { p = (char *)veb; p += i40e_gstrings_veb_stats[j].stat_offset; @@ -1409,7 +1468,8 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, if (vsi != pf->vsi[pf->lan_vsi] || pf->hw.partition_id != 1) return; - if (pf->lan_veb != I40E_NO_VEB) { + if ((pf->lan_veb != I40E_NO_VEB) && + (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { for (i = 0; i < I40E_VEB_STATS_LEN; i++) { snprintf(p, ETH_GSTRING_LEN, "veb.%s", i40e_gstrings_veb_stats[i].stat_string); @@ -1504,9 +1564,18 @@ static int i40e_link_test(struct net_device *netdev, u64 *data) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; + i40e_status status; + bool link_up = false; netif_info(pf, hw, netdev, "link test\n"); - if (i40e_get_link_status(&pf->hw)) + status = i40e_get_link_status(&pf->hw, &link_up); + if (status) { + netif_err(pf, drv, netdev, "link query timed out, please retry test\n"); + *data = 1; + return *data; + } + + if (link_up) *data = 0; else *data = 1; @@ -1575,7 +1644,7 @@ static inline bool i40e_active_vfs(struct i40e_pf *pf) int i; for (i = 0; i < pf->num_alloc_vfs; i++) - if (vfs[i].vf_states & I40E_VF_STAT_ACTIVE) + if (test_bit(I40E_VF_STAT_ACTIVE, &vfs[i].vf_states)) return true; return false; } @@ -1782,6 +1851,14 @@ static int i40e_get_coalesce(struct net_device *netdev, ec->rx_coalesce_usecs = vsi->rx_itr_setting & ~I40E_ITR_DYNAMIC; ec->tx_coalesce_usecs = vsi->tx_itr_setting & ~I40E_ITR_DYNAMIC; + /* we use the _usecs_high to store/set the interrupt rate limit + * that the hardware supports, that almost but not quite + * fits the original intent of the ethtool variable, + * the rx_coalesce_usecs_high limits total interrupts + * per second from both tx/rx sources. + */ + ec->rx_coalesce_usecs_high = vsi->int_rate_limit; + ec->tx_coalesce_usecs_high = vsi->int_rate_limit; return 0; } @@ -1800,6 +1877,17 @@ static int i40e_set_coalesce(struct net_device *netdev, if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) vsi->work_limit = ec->tx_max_coalesced_frames_irq; + /* tx_coalesce_usecs_high is ignored, use rx-usecs-high instead */ + if (ec->tx_coalesce_usecs_high != vsi->int_rate_limit) { + netif_info(pf, drv, netdev, "tx-usecs-high is not used, please program rx-usecs-high\n"); + return -EINVAL; + } + + if (ec->rx_coalesce_usecs_high >= INTRL_REG_TO_USEC(I40E_MAX_INTRL)) { + netif_info(pf, drv, netdev, "Invalid value, rx-usecs-high range is 0-235\n"); + return -EINVAL; + } + vector = vsi->base_vector; if ((ec->rx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && (ec->rx_coalesce_usecs <= (I40E_MAX_ITR << 1))) { @@ -1813,6 +1901,8 @@ static int i40e_set_coalesce(struct net_device *netdev, return -EINVAL; } + vsi->int_rate_limit = ec->rx_coalesce_usecs_high; + if ((ec->tx_coalesce_usecs >= (I40E_MIN_ITR << 1)) && (ec->tx_coalesce_usecs <= (I40E_MAX_ITR << 1))) { vsi->tx_itr_setting = ec->tx_coalesce_usecs; @@ -1837,11 +1927,14 @@ static int i40e_set_coalesce(struct net_device *netdev, vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC; for (i = 0; i < vsi->num_q_vectors; i++, vector++) { + u16 intrl = INTRL_USEC_TO_REG(vsi->int_rate_limit); + q_vector = vsi->q_vectors[i]; q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); wr32(hw, I40E_PFINT_ITRN(0, vector - 1), q_vector->rx.itr); q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting); wr32(hw, I40E_PFINT_ITRN(1, vector - 1), q_vector->tx.itr); + wr32(hw, I40E_PFINT_RATEN(vector - 1), intrl); i40e_flush(hw); } @@ -2604,10 +2697,51 @@ static u32 i40e_get_priv_flags(struct net_device *dev) ret_flags |= pf->hw.func_caps.npar_enable ? I40E_PRIV_FLAGS_NPAR_FLAG : 0; + ret_flags |= pf->flags & I40E_FLAG_LINK_POLLING_ENABLED ? + I40E_PRIV_FLAGS_LINKPOLL_FLAG : 0; + ret_flags |= pf->flags & I40E_FLAG_FD_ATR_ENABLED ? + I40E_PRIV_FLAGS_FD_ATR : 0; + ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ? + I40E_PRIV_FLAGS_VEB_STATS : 0; return ret_flags; } +/** + * i40e_set_priv_flags - set private flags + * @dev: network interface device structure + * @flags: bit flags to be set + **/ +static int i40e_set_priv_flags(struct net_device *dev, u32 flags) +{ + struct i40e_netdev_priv *np = netdev_priv(dev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + + if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG) + pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED; + else + pf->flags &= ~I40E_FLAG_LINK_POLLING_ENABLED; + + /* allow the user to control the state of the Flow + * Director ATR (Application Targeted Routing) feature + * of the driver + */ + if (flags & I40E_PRIV_FLAGS_FD_ATR) { + pf->flags |= I40E_FLAG_FD_ATR_ENABLED; + } else { + pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; + pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; + } + + if (flags & I40E_PRIV_FLAGS_VEB_STATS) + pf->flags |= I40E_FLAG_VEB_STATS_ENABLED; + else + pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; + + return 0; +} + static const struct ethtool_ops i40e_ethtool_ops = { .get_settings = i40e_get_settings, .set_settings = i40e_set_settings, @@ -2644,6 +2778,7 @@ static const struct ethtool_ops i40e_ethtool_ops = { .set_channels = i40e_set_channels, .get_ts_info = i40e_get_ts_info, .get_priv_flags = i40e_get_priv_flags, + .set_priv_flags = i40e_set_priv_flags, }; void i40e_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index 5ea75dd537d6..2ec241142e9d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -272,10 +272,8 @@ out: /** * i40e_fcoe_sw_init - sets up the HW for FCoE * @pf: pointer to PF - * - * Returns 0 if FCoE is supported otherwise the error code **/ -int i40e_init_pf_fcoe(struct i40e_pf *pf) +void i40e_init_pf_fcoe(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; u32 val; @@ -286,14 +284,14 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf) pf->fcoe_hmc_filt_num = 0; if (!pf->hw.func_caps.fcoe) { - dev_info(&pf->pdev->dev, "FCoE capability is disabled\n"); - return 0; + dev_dbg(&pf->pdev->dev, "FCoE capability is disabled\n"); + return; } if (!pf->hw.func_caps.dcb) { dev_warn(&pf->pdev->dev, "Hardware is not DCB capable not enabling FCoE.\n"); - return 0; + return; } /* enable FCoE hash filter */ @@ -326,7 +324,6 @@ int i40e_init_pf_fcoe(struct i40e_pf *pf) wr32(hw, I40E_GLFCOE_RCTL, val); dev_info(&pf->pdev->dev, "FCoE is supported.\n"); - return 0; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index fa371a2a40c6..79ae7beeafe5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c @@ -431,9 +431,8 @@ exit_sd_error: pd_idx1 = max(pd_idx, ((j - 1) * I40E_HMC_MAX_BP_COUNT)); pd_lmt1 = min(pd_lmt, (j * I40E_HMC_MAX_BP_COUNT)); - for (i = pd_idx1; i < pd_lmt1; i++) { + for (i = pd_idx1; i < pd_lmt1; i++) i40e_remove_pd_bp(hw, info->hmc_info, i); - } i40e_remove_pd_page(hw, info->hmc_info, (j - 1)); break; case I40E_SD_TYPE_DIRECT: diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index dd44fafd8798..f7ed3131d037 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 3 -#define DRV_VERSION_BUILD 9 +#define DRV_VERSION_BUILD 34 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN @@ -75,10 +75,13 @@ static const struct pci_device_id i40e_pci_tbl[] = { {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0}, + {PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0}, /* required last entry */ {0, } }; @@ -213,10 +216,10 @@ static int i40e_get_lump(struct i40e_pf *pf, struct i40e_lump_tracking *pile, ret = i; pile->search_hint = i + j; break; - } else { - /* not enough, so skip over it and continue looking */ - i += j; } + + /* not enough, so skip over it and continue looking */ + i += j; } return ret; @@ -299,25 +302,69 @@ static void i40e_tx_timeout(struct net_device *netdev) struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + struct i40e_ring *tx_ring = NULL; + unsigned int i, hung_queue = 0; + u32 head, val; pf->tx_timeout_count++; + /* find the stopped queue the same way the stack does */ + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *q; + unsigned long trans_start; + + q = netdev_get_tx_queue(netdev, i); + trans_start = q->trans_start ? : netdev->trans_start; + if (netif_xmit_stopped(q) && + time_after(jiffies, + (trans_start + netdev->watchdog_timeo))) { + hung_queue = i; + break; + } + } + + if (i == netdev->num_tx_queues) { + netdev_info(netdev, "tx_timeout: no netdev hung queue found\n"); + } else { + /* now that we have an index, find the tx_ring struct */ + for (i = 0; i < vsi->num_queue_pairs; i++) { + if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) { + if (hung_queue == + vsi->tx_rings[i]->queue_index) { + tx_ring = vsi->tx_rings[i]; + break; + } + } + } + } + if (time_after(jiffies, (pf->tx_timeout_last_recovery + HZ*20))) - pf->tx_timeout_recovery_level = 1; + pf->tx_timeout_recovery_level = 1; /* reset after some time */ + else if (time_before(jiffies, + (pf->tx_timeout_last_recovery + netdev->watchdog_timeo))) + return; /* don't do any new action before the next timeout */ + + if (tx_ring) { + head = i40e_get_head(tx_ring); + /* Read interrupt register */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + val = rd32(&pf->hw, + I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx + + tx_ring->vsi->base_vector - 1)); + else + val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); + + netdev_info(netdev, "tx_timeout: VSI_seid: %d, Q %d, NTC: 0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x, INT: 0x%x\n", + vsi->seid, hung_queue, tx_ring->next_to_clean, + head, tx_ring->next_to_use, + readl(tx_ring->tail), val); + } + pf->tx_timeout_last_recovery = jiffies; - netdev_info(netdev, "tx_timeout recovery level %d\n", - pf->tx_timeout_recovery_level); + netdev_info(netdev, "tx_timeout recovery level %d, hung_queue %d\n", + pf->tx_timeout_recovery_level, hung_queue); switch (pf->tx_timeout_recovery_level) { - case 0: - /* disable and re-enable queues for the VSI */ - if (in_interrupt()) { - set_bit(__I40E_REINIT_REQUESTED, &pf->state); - set_bit(__I40E_REINIT_REQUESTED, &vsi->state); - } else { - i40e_vsi_reinit_locked(vsi); - } - break; case 1: set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); break; @@ -329,10 +376,9 @@ static void i40e_tx_timeout(struct net_device *netdev) break; default: netdev_err(netdev, "tx_timeout recovery unsuccessful\n"); - set_bit(__I40E_DOWN_REQUESTED, &pf->state); - set_bit(__I40E_DOWN_REQUESTED, &vsi->state); break; } + i40e_service_event_schedule(pf); pf->tx_timeout_recovery_level++; } @@ -431,6 +477,7 @@ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct( stats->tx_errors = vsi_stats->tx_errors; stats->tx_dropped = vsi_stats->tx_dropped; stats->rx_errors = vsi_stats->rx_errors; + stats->rx_dropped = vsi_stats->rx_dropped; stats->rx_crc_errors = vsi_stats->rx_crc_errors; stats->rx_length_errors = vsi_stats->rx_length_errors; @@ -456,11 +503,11 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi) memset(&vsi->eth_stats_offsets, 0, sizeof(vsi->eth_stats_offsets)); if (vsi->rx_rings && vsi->rx_rings[0]) { for (i = 0; i < vsi->num_queue_pairs; i++) { - memset(&vsi->rx_rings[i]->stats, 0 , + memset(&vsi->rx_rings[i]->stats, 0, sizeof(vsi->rx_rings[i]->stats)); - memset(&vsi->rx_rings[i]->rx_stats, 0 , + memset(&vsi->rx_rings[i]->rx_stats, 0, sizeof(vsi->rx_rings[i]->rx_stats)); - memset(&vsi->tx_rings[i]->stats, 0 , + memset(&vsi->tx_rings[i]->stats, 0, sizeof(vsi->tx_rings[i]->stats)); memset(&vsi->tx_rings[i]->tx_stats, 0, sizeof(vsi->tx_rings[i]->tx_stats)); @@ -754,7 +801,6 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf) struct i40e_hw_port_stats *nsd = &pf->stats; struct i40e_hw *hw = &pf->hw; u64 xoff = 0; - u16 i, v; if ((hw->fc.current_mode != I40E_FC_FULL) && (hw->fc.current_mode != I40E_FC_RX_PAUSE)) @@ -769,18 +815,6 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf) if (!(nsd->link_xoff_rx - xoff)) return; - /* Clear the __I40E_HANG_CHECK_ARMED bit for all Tx rings */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - - if (!vsi || !vsi->tx_rings[0]) - continue; - - for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *ring = vsi->tx_rings[i]; - clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state); - } - } } /** @@ -796,7 +830,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) bool xoff[I40E_MAX_TRAFFIC_CLASS] = {false}; struct i40e_dcbx_config *dcb_cfg; struct i40e_hw *hw = &pf->hw; - u16 i, v; + u16 i; u8 tc; dcb_cfg = &hw->local_dcbx_config; @@ -809,6 +843,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { u64 prio_xoff = nsd->priority_xoff_rx[i]; + i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i), pf->stat_offsets_loaded, &osd->priority_xoff_rx[i], @@ -821,23 +856,6 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) tc = dcb_cfg->etscfg.prioritytable[i]; xoff[tc] = true; } - - /* Clear the __I40E_HANG_CHECK_ARMED bit for Tx rings */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - - if (!vsi || !vsi->tx_rings[0]) - continue; - - for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *ring = vsi->tx_rings[i]; - - tc = ring->dcb_tc; - if (xoff[tc]) - clear_bit(__I40E_HANG_CHECK_ARMED, - &ring->state); - } - } } /** @@ -862,6 +880,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) u32 rx_page, rx_buf; u64 bytes, packets; unsigned int start; + u64 tx_linearize; u64 rx_p, rx_b; u64 tx_p, tx_b; u16 q; @@ -880,7 +899,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) */ rx_b = rx_p = 0; tx_b = tx_p = 0; - tx_restart = tx_busy = 0; + tx_restart = tx_busy = tx_linearize = 0; rx_page = 0; rx_buf = 0; rcu_read_lock(); @@ -897,6 +916,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) tx_p += packets; tx_restart += p->tx_stats.restart_queue; tx_busy += p->tx_stats.tx_busy; + tx_linearize += p->tx_stats.tx_linearize; /* Rx queue is part of the same block as Tx queue */ p = &p[1]; @@ -913,6 +933,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) rcu_read_unlock(); vsi->tx_restart = tx_restart; vsi->tx_busy = tx_busy; + vsi->tx_linearize = tx_linearize; vsi->rx_page_failed = rx_page; vsi->rx_buf_failed = rx_buf; @@ -1256,7 +1277,7 @@ bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi) * so we have to go through all the list in order to make sure */ list_for_each_entry(f, &vsi->mac_filter_list, list) { - if (f->vlan >= 0) + if (f->vlan >= 0 || vsi->info.pvid) return true; } @@ -1419,6 +1440,7 @@ void i40e_del_filter(struct i40e_vsi *vsi, } else { /* make sure we don't remove a filter in use by VF or netdev */ int min_f = 0; + min_f += (f->is_vf ? 1 : 0); min_f += (f->is_netdev ? 1 : 0); @@ -1477,6 +1499,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p) if (vsi->type == I40E_VSI_MAIN) { i40e_status ret; + ret = i40e_aq_mac_address_write(&vsi->back->hw, I40E_AQC_WRITE_TYPE_LAA_WOL, addr->sa_data, NULL); @@ -1514,7 +1537,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p) f->is_laa = true; } - i40e_sync_vsi_filters(vsi); + i40e_sync_vsi_filters(vsi, false); ether_addr_copy(netdev->dev_addr, addr->sa_data); return 0; @@ -1709,36 +1732,27 @@ static void i40e_set_rx_mode(struct net_device *netdev) /* remove filter if not in netdev list */ list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) { - bool found = false; if (!f->is_netdev) continue; - if (is_multicast_ether_addr(f->macaddr)) { - netdev_for_each_mc_addr(mca, netdev) { - if (ether_addr_equal(mca->addr, f->macaddr)) { - found = true; - break; - } - } - } else { - netdev_for_each_uc_addr(uca, netdev) { - if (ether_addr_equal(uca->addr, f->macaddr)) { - found = true; - break; - } - } + netdev_for_each_mc_addr(mca, netdev) + if (ether_addr_equal(mca->addr, f->macaddr)) + goto bottom_of_search_loop; - for_each_dev_addr(netdev, ha) { - if (ether_addr_equal(ha->addr, f->macaddr)) { - found = true; - break; - } - } - } - if (!found) - i40e_del_filter( - vsi, f->macaddr, I40E_VLAN_ANY, false, true); + netdev_for_each_uc_addr(uca, netdev) + if (ether_addr_equal(uca->addr, f->macaddr)) + goto bottom_of_search_loop; + + for_each_dev_addr(netdev, ha) + if (ether_addr_equal(ha->addr, f->macaddr)) + goto bottom_of_search_loop; + + /* f->macaddr wasn't found in uc, mc, or ha list so delete it */ + i40e_del_filter(vsi, f->macaddr, I40E_VLAN_ANY, false, true); + +bottom_of_search_loop: + continue; } /* check for other flag changes */ @@ -1751,12 +1765,13 @@ static void i40e_set_rx_mode(struct net_device *netdev) /** * i40e_sync_vsi_filters - Update the VSI filter list to the HW * @vsi: ptr to the VSI + * @grab_rtnl: whether RTNL needs to be grabbed * * Push any outstanding VSI filter changes through the AdminQ. * * Returns 0 or error value **/ -int i40e_sync_vsi_filters(struct i40e_vsi *vsi) +int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl) { struct i40e_mac_filter *f, *ftmp; bool promisc_forced_on = false; @@ -1920,6 +1935,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* check for changes in promiscuous modes */ if (changed_flags & IFF_ALLMULTI) { bool cur_multipromisc; + cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI); ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw, vsi->seid, @@ -1934,6 +1950,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if ((changed_flags & IFF_PROMISC) || promisc_forced_on) { bool cur_promisc; + cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) || test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state)); @@ -1945,7 +1962,11 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) */ if (pf->cur_promisc != cur_promisc) { pf->cur_promisc = cur_promisc; - i40e_do_reset_safe(pf, + if (grab_rtnl) + i40e_do_reset_safe(pf, + BIT(__I40E_PF_RESET_REQUESTED)); + else + i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); } } else { @@ -1996,7 +2017,7 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED)) - i40e_sync_vsi_filters(pf->vsi[v]); + i40e_sync_vsi_filters(pf->vsi[v], true); } } @@ -2203,7 +2224,7 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) return 0; - return i40e_sync_vsi_filters(vsi); + return i40e_sync_vsi_filters(vsi, false); } /** @@ -2275,7 +2296,7 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) return 0; - return i40e_sync_vsi_filters(vsi); + return i40e_sync_vsi_filters(vsi, false); } /** @@ -2609,8 +2630,6 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) wr32(hw, I40E_QTX_CTL(pf_q), qtx_ctl); i40e_flush(hw); - clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state); - /* cache tail off for easier writes later */ ring->tail = hw->hw_addr + I40E_QTX_TAIL(pf_q); @@ -2882,11 +2901,9 @@ static int i40e_vsi_configure(struct i40e_vsi *vsi) static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; - struct i40e_q_vector *q_vector; struct i40e_hw *hw = &pf->hw; u16 vector; int i, q; - u32 val; u32 qp; /* The interrupt indexing is offset by 1 in the PFINT_ITRn @@ -2896,7 +2913,8 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) qp = vsi->base_queue; vector = vsi->base_vector; for (i = 0; i < vsi->num_q_vectors; i++, vector++) { - q_vector = vsi->q_vectors[i]; + struct i40e_q_vector *q_vector = vsi->q_vectors[i]; + q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); q_vector->rx.latency_range = I40E_LOW_LATENCY; wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1), @@ -2905,10 +2923,14 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) q_vector->tx.latency_range = I40E_LOW_LATENCY; wr32(hw, I40E_PFINT_ITRN(I40E_TX_ITR, vector - 1), q_vector->tx.itr); + wr32(hw, I40E_PFINT_RATEN(vector - 1), + INTRL_USEC_TO_REG(vsi->int_rate_limit)); /* Linked list for the queuepairs assigned to this vector */ wr32(hw, I40E_PFINT_LNKLSTN(vector - 1), qp); for (q = 0; q < q_vector->num_ringpairs; q++) { + u32 val; + val = I40E_QINT_RQCTL_CAUSE_ENA_MASK | (I40E_RX_ITR << I40E_QINT_RQCTL_ITR_INDX_SHIFT) | (vector << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | @@ -3046,24 +3068,6 @@ void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf) } /** - * i40e_irq_dynamic_enable - Enable default interrupt generation settings - * @vsi: pointer to a vsi - * @vector: enable a particular Hw Interrupt vector - **/ -void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) -{ - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - u32 val; - - val = I40E_PFINT_DYN_CTLN_INTENA_MASK | - I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | - (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); - wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val); - /* skip the flush */ -} - -/** * i40e_irq_dynamic_disable - Disable default interrupt generation settings * @vsi: pointer to a vsi * @vector: disable a particular Hw Interrupt vector @@ -3136,8 +3140,7 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) q_vector); if (err) { dev_info(&pf->pdev->dev, - "%s: request_irq failed, error: %d\n", - __func__, err); + "MSIX request_irq failed, error: %d\n", err); goto free_queue_irqs; } /* assign the mask for this irq */ @@ -3202,8 +3205,7 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi) int i; if (pf->flags & I40E_FLAG_MSIX_ENABLED) { - for (i = vsi->base_vector; - i < (vsi->num_q_vectors + vsi->base_vector); i++) + for (i = 0; i < vsi->num_q_vectors; i++) i40e_irq_dynamic_enable(vsi, i); } else { i40e_irq_dynamic_enable_icr0(pf); @@ -3265,6 +3267,7 @@ static irqreturn_t i40e_intr(int irq, void *data) /* temporarily disable queue cause for NAPI processing */ u32 qval = rd32(hw, I40E_QINT_RQCTL(0)); + qval &= ~I40E_QINT_RQCTL_CAUSE_ENA_MASK; wr32(hw, I40E_QINT_RQCTL(0), qval); @@ -3434,10 +3437,9 @@ static bool i40e_clean_fdir_tx_irq(struct i40e_ring *tx_ring, int budget) i += tx_ring->count; tx_ring->next_to_clean = i; - if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) { - i40e_irq_dynamic_enable(vsi, - tx_ring->q_vector->v_idx + vsi->base_vector); - } + if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) + i40e_irq_dynamic_enable(vsi, tx_ring->q_vector->v_idx); + return budget > 0; } @@ -3575,14 +3577,12 @@ static void i40e_netpoll(struct net_device *netdev) if (test_bit(__I40E_DOWN, &vsi->state)) return; - pf->flags |= I40E_FLAG_IN_NETPOLL; if (pf->flags & I40E_FLAG_MSIX_ENABLED) { for (i = 0; i < vsi->num_q_vectors; i++) i40e_msix_clean_rings(0, vsi->q_vectors[i]); } else { i40e_intr(pf->pdev->irq, netdev); } - pf->flags &= ~I40E_FLAG_IN_NETPOLL; } #endif @@ -3663,9 +3663,8 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) ret = i40e_pf_txq_wait(pf, pf_q, enable); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Tx ring %d %sable timeout\n", - __func__, vsi->seid, pf_q, - (enable ? "en" : "dis")); + "VSI seid %d Tx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); break; } } @@ -3741,9 +3740,8 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) ret = i40e_pf_rxq_wait(pf, pf_q, enable); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Rx ring %d %sable timeout\n", - __func__, vsi->seid, pf_q, - (enable ? "en" : "dis")); + "VSI seid %d Rx ring %d %sable timeout\n", + vsi->seid, pf_q, (enable ? "en" : "dis")); break; } } @@ -4038,17 +4036,15 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) if ((test_bit(__I40E_PORT_TX_SUSPENDED, &vsi->back->state)) && vsi->type == I40E_VSI_FCOE) { dev_dbg(&vsi->back->pdev->dev, - "%s: VSI seid %d skipping FCoE VSI disable\n", - __func__, vsi->seid); + "VSI seid %d skipping FCoE VSI disable\n", vsi->seid); return; } set_bit(__I40E_NEEDS_RESTART, &vsi->state); - if (vsi->netdev && netif_running(vsi->netdev)) { + if (vsi->netdev && netif_running(vsi->netdev)) vsi->netdev->netdev_ops->ndo_stop(vsi->netdev); - } else { + else i40e_vsi_close(vsi); - } } /** @@ -4113,8 +4109,8 @@ static int i40e_vsi_wait_txq_disabled(struct i40e_vsi *vsi) ret = i40e_pf_txq_wait(pf, pf_q, false); if (ret) { dev_info(&pf->pdev->dev, - "%s: VSI seid %d Tx ring %d disable timeout\n", - __func__, vsi->seid, pf_q); + "VSI seid %d Tx ring %d disable timeout\n", + vsi->seid, pf_q); return ret; } } @@ -4146,6 +4142,108 @@ static int i40e_pf_wait_txq_disabled(struct i40e_pf *pf) } #endif + +/** + * i40e_detect_recover_hung_queue - Function to detect and recover hung_queue + * @q_idx: TX queue number + * @vsi: Pointer to VSI struct + * + * This function checks specified queue for given VSI. Detects hung condition. + * Sets hung bit since it is two step process. Before next run of service task + * if napi_poll runs, it reset 'hung' bit for respective q_vector. If not, + * hung condition remain unchanged and during subsequent run, this function + * issues SW interrupt to recover from hung condition. + **/ +static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) +{ + struct i40e_ring *tx_ring = NULL; + struct i40e_pf *pf; + u32 head, val, tx_pending; + int i; + + pf = vsi->back; + + /* now that we have an index, find the tx_ring struct */ + for (i = 0; i < vsi->num_queue_pairs; i++) { + if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) { + if (q_idx == vsi->tx_rings[i]->queue_index) { + tx_ring = vsi->tx_rings[i]; + break; + } + } + } + + if (!tx_ring) + return; + + /* Read interrupt register */ + if (pf->flags & I40E_FLAG_MSIX_ENABLED) + val = rd32(&pf->hw, + I40E_PFINT_DYN_CTLN(tx_ring->q_vector->v_idx + + tx_ring->vsi->base_vector - 1)); + else + val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); + + head = i40e_get_head(tx_ring); + + tx_pending = i40e_get_tx_pending(tx_ring); + + /* Interrupts are disabled and TX pending is non-zero, + * trigger the SW interrupt (don't wait). Worst case + * there will be one extra interrupt which may result + * into not cleaning any queues because queues are cleaned. + */ + if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) + i40e_force_wb(vsi, tx_ring->q_vector); +} + +/** + * i40e_detect_recover_hung - Function to detect and recover hung_queues + * @pf: pointer to PF struct + * + * LAN VSI has netdev and netdev has TX queues. This function is to check + * each of those TX queues if they are hung, trigger recovery by issuing + * SW interrupt. + **/ +static void i40e_detect_recover_hung(struct i40e_pf *pf) +{ + struct net_device *netdev; + struct i40e_vsi *vsi; + int i; + + /* Only for LAN VSI */ + vsi = pf->vsi[pf->lan_vsi]; + + if (!vsi) + return; + + /* Make sure, VSI state is not DOWN/RECOVERY_PENDING */ + if (test_bit(__I40E_DOWN, &vsi->back->state) || + test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) + return; + + /* Make sure type is MAIN VSI */ + if (vsi->type != I40E_VSI_MAIN) + return; + + netdev = vsi->netdev; + if (!netdev) + return; + + /* Bail out if netif_carrier is not OK */ + if (!netif_carrier_ok(netdev)) + return; + + /* Go thru' TX queues for netdev */ + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *q; + + q = netdev_get_tx_queue(netdev, i); + if (q) + i40e_detect_recover_hung_queue(i, vsi); + } +} + /** * i40e_get_iscsi_tc_map - Return TC map for iSCSI APP * @pf: pointer to PF @@ -4745,11 +4843,14 @@ out: * i40e_print_link_message - print link up or down * @vsi: the VSI for which link needs a message */ -static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) +void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) { - char speed[SPEED_SIZE] = "Unknown"; - char fc[FC_SIZE] = "RX/TX"; + char *speed = "Unknown"; + char *fc = "Unknown"; + if (vsi->current_isup == isup) + return; + vsi->current_isup = isup; if (!isup) { netdev_info(vsi->netdev, "NIC Link is Down\n"); return; @@ -4766,19 +4867,19 @@ static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) switch (vsi->back->hw.phy.link_info.link_speed) { case I40E_LINK_SPEED_40GB: - strlcpy(speed, "40 Gbps", SPEED_SIZE); + speed = "40 G"; break; case I40E_LINK_SPEED_20GB: - strncpy(speed, "20 Gbps", SPEED_SIZE); + speed = "20 G"; break; case I40E_LINK_SPEED_10GB: - strlcpy(speed, "10 Gbps", SPEED_SIZE); + speed = "10 G"; break; case I40E_LINK_SPEED_1GB: - strlcpy(speed, "1000 Mbps", SPEED_SIZE); + speed = "1000 M"; break; case I40E_LINK_SPEED_100MB: - strncpy(speed, "100 Mbps", SPEED_SIZE); + speed = "100 M"; break; default: break; @@ -4786,20 +4887,20 @@ static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) switch (vsi->back->hw.fc.current_mode) { case I40E_FC_FULL: - strlcpy(fc, "RX/TX", FC_SIZE); + fc = "RX/TX"; break; case I40E_FC_TX_PAUSE: - strlcpy(fc, "TX", FC_SIZE); + fc = "TX"; break; case I40E_FC_RX_PAUSE: - strlcpy(fc, "RX", FC_SIZE); + fc = "RX"; break; default: - strlcpy(fc, "None", FC_SIZE); + fc = "None"; break; } - netdev_info(vsi->netdev, "NIC Link is Up %s Full Duplex, Flow Control: %s\n", + netdev_info(vsi->netdev, "NIC Link is Up %sbps Full Duplex, Flow Control: %s\n", speed, fc); } @@ -5218,15 +5319,13 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) "VSI reinit requested\n"); for (v = 0; v < pf->num_alloc_vsi; v++) { struct i40e_vsi *vsi = pf->vsi[v]; + if (vsi != NULL && test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) { i40e_vsi_reinit_locked(pf->vsi[v]); clear_bit(__I40E_REINIT_REQUESTED, &vsi->state); } } - - /* no further action needed, so return now */ - return; } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { int v; @@ -5234,6 +5333,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) dev_info(&pf->pdev->dev, "VSI down requested\n"); for (v = 0; v < pf->num_alloc_vsi; v++) { struct i40e_vsi *vsi = pf->vsi[v]; + if (vsi != NULL && test_bit(__I40E_DOWN_REQUESTED, &vsi->state)) { set_bit(__I40E_DOWN, &vsi->state); @@ -5241,13 +5341,9 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) clear_bit(__I40E_DOWN_REQUESTED, &vsi->state); } } - - /* no further action needed, so return now */ - return; } else { dev_info(&pf->pdev->dev, "bad reset request 0x%08x\n", reset_flags); - return; } } @@ -5303,8 +5399,7 @@ bool i40e_dcb_need_reconfig(struct i40e_pf *pf, dev_dbg(&pf->pdev->dev, "APP Table change detected.\n"); } - dev_dbg(&pf->pdev->dev, "%s: need_reconfig=%d\n", __func__, - need_reconfig); + dev_dbg(&pf->pdev->dev, "dcb need_reconfig=%d\n", need_reconfig); return need_reconfig; } @@ -5331,16 +5426,14 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, /* Ignore if event is not for Nearest Bridge */ type = ((mib->type >> I40E_AQ_LLDP_BRIDGE_TYPE_SHIFT) & I40E_AQ_LLDP_BRIDGE_TYPE_MASK); - dev_dbg(&pf->pdev->dev, - "%s: LLDP event mib bridge type 0x%x\n", __func__, type); + dev_dbg(&pf->pdev->dev, "LLDP event mib bridge type 0x%x\n", type); if (type != I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE) return ret; /* Check MIB Type and return if event for Remote MIB update */ type = mib->type & I40E_AQ_LLDP_MIB_TYPE_MASK; dev_dbg(&pf->pdev->dev, - "%s: LLDP event mib type %s\n", __func__, - type ? "remote" : "local"); + "LLDP event mib type %s\n", type ? "remote" : "local"); if (type == I40E_AQ_LLDP_MIB_REMOTE) { /* Update the remote cached instance and return */ ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE, @@ -5525,7 +5618,9 @@ u32 i40e_get_global_fd_count(struct i40e_pf *pf) **/ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) { + struct i40e_fdir_filter *filter; u32 fcnt_prog, fcnt_avail; + struct hlist_node *node; if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) return; @@ -5554,6 +5649,18 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table now\n"); } } + + /* if hw had a problem adding a filter, delete it */ + if (pf->fd_inv > 0) { + hlist_for_each_entry_safe(filter, node, + &pf->fdir_filter_list, fdir_node) { + if (filter->fd_id == pf->fd_inv) { + hlist_del(&filter->fdir_node); + kfree(filter); + pf->fdir_pf_active_filters--; + } + } + } } #define I40E_MIN_FD_FLUSH_INTERVAL 10 @@ -5573,49 +5680,51 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) if (!(pf->flags & (I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED))) return; - if (time_after(jiffies, pf->fd_flush_timestamp + - (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) { - /* If the flush is happening too quick and we have mostly - * SB rules we should not re-enable ATR for some time. - */ - min_flush_time = pf->fd_flush_timestamp - + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); - fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; + if (!time_after(jiffies, pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_INTERVAL * HZ))) + return; - if (!(time_after(jiffies, min_flush_time)) && - (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); - disable_atr = true; - } + /* If the flush is happening too quick and we have mostly SB rules we + * should not re-enable ATR for some time. + */ + min_flush_time = pf->fd_flush_timestamp + + (I40E_MIN_FD_FLUSH_SB_ATR_UNSTABLE * HZ); + fd_room = pf->fdir_pf_filter_count - pf->fdir_pf_active_filters; - pf->fd_flush_timestamp = jiffies; - pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; - /* flush all filters */ - wr32(&pf->hw, I40E_PFQF_CTL_1, - I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); - i40e_flush(&pf->hw); - pf->fd_flush_cnt++; - pf->fd_add_err = 0; - do { - /* Check FD flush status every 5-6msec */ - usleep_range(5000, 6000); - reg = rd32(&pf->hw, I40E_PFQF_CTL_1); - if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) - break; - } while (flush_wait_retry--); - if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { - dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); - } else { - /* replay sideband filters */ - i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); - if (!disable_atr) - pf->flags |= I40E_FLAG_FD_ATR_ENABLED; - clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); - } + if (!(time_after(jiffies, min_flush_time)) && + (fd_room < I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) { + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "ATR disabled, not enough FD filter space.\n"); + disable_atr = true; } + + pf->fd_flush_timestamp = jiffies; + pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; + /* flush all filters */ + wr32(&pf->hw, I40E_PFQF_CTL_1, + I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); + i40e_flush(&pf->hw); + pf->fd_flush_cnt++; + pf->fd_add_err = 0; + do { + /* Check FD flush status every 5-6msec */ + usleep_range(5000, 6000); + reg = rd32(&pf->hw, I40E_PFQF_CTL_1); + if (!(reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK)) + break; + } while (flush_wait_retry--); + if (reg & I40E_PFQF_CTL_1_CLEARFDTABLE_MASK) { + dev_warn(&pf->pdev->dev, "FD table did not flush, needs more time\n"); + } else { + /* replay sideband filters */ + i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); + if (!disable_atr) + pf->flags |= I40E_FLAG_FD_ATR_ENABLED; + clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); + if (I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "FD Filter table flushed and FD-SB replayed.\n"); + } + } /** @@ -5723,15 +5832,23 @@ static void i40e_veb_link_event(struct i40e_veb *veb, bool link_up) **/ static void i40e_link_event(struct i40e_pf *pf) { - bool new_link, old_link; struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; u8 new_link_speed, old_link_speed; + i40e_status status; + bool new_link, old_link; /* set this to force the get_link_status call to refresh state */ pf->hw.phy.get_link_info = true; old_link = (pf->hw.phy.link_info_old.link_info & I40E_AQ_LINK_UP); - new_link = i40e_get_link_status(&pf->hw); + + status = i40e_get_link_status(&pf->hw, &new_link); + if (status) { + dev_dbg(&pf->pdev->dev, "couldn't get link state, status: %d\n", + status); + return; + } + old_link_speed = pf->hw.phy.link_info_old.link_speed; new_link_speed = pf->hw.phy.link_info.link_speed; @@ -5760,68 +5877,6 @@ static void i40e_link_event(struct i40e_pf *pf) } /** - * i40e_check_hang_subtask - Check for hung queues and dropped interrupts - * @pf: board private structure - * - * Set the per-queue flags to request a check for stuck queues in the irq - * clean functions, then force interrupts to be sure the irq clean is called. - **/ -static void i40e_check_hang_subtask(struct i40e_pf *pf) -{ - int i, v; - - /* If we're down or resetting, just bail */ - if (test_bit(__I40E_DOWN, &pf->state) || - test_bit(__I40E_CONFIG_BUSY, &pf->state)) - return; - - /* for each VSI/netdev - * for each Tx queue - * set the check flag - * for each q_vector - * force an interrupt - */ - for (v = 0; v < pf->num_alloc_vsi; v++) { - struct i40e_vsi *vsi = pf->vsi[v]; - int armed = 0; - - if (!pf->vsi[v] || - test_bit(__I40E_DOWN, &vsi->state) || - (vsi->netdev && !netif_carrier_ok(vsi->netdev))) - continue; - - for (i = 0; i < vsi->num_queue_pairs; i++) { - set_check_for_tx_hang(vsi->tx_rings[i]); - if (test_bit(__I40E_HANG_CHECK_ARMED, - &vsi->tx_rings[i]->state)) - armed++; - } - - if (armed) { - if (!(pf->flags & I40E_FLAG_MSIX_ENABLED)) { - wr32(&vsi->back->hw, I40E_PFINT_DYN_CTL0, - (I40E_PFINT_DYN_CTL0_INTENA_MASK | - I40E_PFINT_DYN_CTL0_SWINT_TRIG_MASK | - I40E_PFINT_DYN_CTL0_ITR_INDX_MASK | - I40E_PFINT_DYN_CTL0_SW_ITR_INDX_ENA_MASK | - I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK)); - } else { - u16 vec = vsi->base_vector - 1; - u32 val = (I40E_PFINT_DYN_CTLN_INTENA_MASK | - I40E_PFINT_DYN_CTLN_SWINT_TRIG_MASK | - I40E_PFINT_DYN_CTLN_ITR_INDX_MASK | - I40E_PFINT_DYN_CTLN_SW_ITR_INDX_ENA_MASK | - I40E_PFINT_DYN_CTLN_SW_ITR_INDX_MASK); - for (i = 0; i < vsi->num_q_vectors; i++, vec++) - wr32(&vsi->back->hw, - I40E_PFINT_DYN_CTLN(vec), val); - } - i40e_flush(&vsi->back->hw); - } - } -} - -/** * i40e_watchdog_subtask - periodic checks not using event driven response * @pf: board private structure **/ @@ -5840,8 +5895,8 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf) return; pf->service_timer_previous = jiffies; - i40e_check_hang_subtask(pf); - i40e_link_event(pf); + if (pf->flags & I40E_FLAG_LINK_POLLING_ENABLED) + i40e_link_event(pf); /* Update the stats for active netdevs so the network stack * can look at updated numbers whenever it cares to @@ -5850,10 +5905,12 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf) if (pf->vsi[i] && pf->vsi[i]->netdev) i40e_update_stats(pf->vsi[i]); - /* Update the stats for the active switching components */ - for (i = 0; i < I40E_MAX_VEB; i++) - if (pf->veb[i]) - i40e_update_veb_stats(pf->veb[i]); + if (pf->flags & I40E_FLAG_VEB_STATS_ENABLED) { + /* Update the stats for the active switching components */ + for (i = 0; i < I40E_MAX_VEB; i++) + if (pf->veb[i]) + i40e_update_veb_stats(pf->veb[i]); + } i40e_ptp_rx_hang(pf->vsi[pf->lan_vsi]); } @@ -6164,8 +6221,9 @@ static void i40e_config_bridge_mode(struct i40e_veb *veb) { struct i40e_pf *pf = veb->pf; - dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n", - veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); + if (pf->hw.debug_mask & I40E_DEBUG_LAN) + dev_info(&pf->pdev->dev, "enabling bridge mode: %s\n", + veb->bridge_mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); if (veb->bridge_mode & BRIDGE_MODE_VEPA) i40e_disable_pf_switch_lb(pf); else @@ -6232,6 +6290,7 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb) if (pf->vsi[v]->veb_idx == veb->idx) { struct i40e_vsi *vsi = pf->vsi[v]; + vsi->uplink_seid = veb->seid; ret = i40e_add_vsi(vsi); if (ret) { @@ -6296,12 +6355,6 @@ static int i40e_get_capabilities(struct i40e_pf *pf) } } while (err); - if (((pf->hw.aq.fw_maj_ver == 2) && (pf->hw.aq.fw_min_ver < 22)) || - (pf->hw.aq.fw_maj_ver < 2)) { - pf->hw.func_caps.num_msix_vectors++; - pf->hw.func_caps.num_msix_vectors_vf++; - } - if (pf->hw.debug_mask & I40E_DEBUG_USER) dev_info(&pf->pdev->dev, "pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n", @@ -6514,9 +6567,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) } #endif /* CONFIG_I40E_DCB */ #ifdef I40E_FCOE - ret = i40e_init_pf_fcoe(pf); - if (ret) - dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", ret); + i40e_init_pf_fcoe(pf); #endif /* do basic switch setup */ @@ -6538,9 +6589,9 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) /* make sure our flow control settings are restored */ ret = i40e_set_fc(&pf->hw, &set_fc_aq_fail, true); if (ret) - dev_info(&pf->pdev->dev, "set fc fail, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + dev_dbg(&pf->pdev->dev, "setting flow control: ret = %s last_status = %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); /* Rebuild the VSIs and VEBs that existed before reset. * They are still in our local switch element arrays, so only @@ -6808,6 +6859,7 @@ static void i40e_service_task(struct work_struct *work) return; } + i40e_detect_recover_hung(pf); i40e_reset_subtask(pf); i40e_handle_mdd_event(pf); i40e_vc_process_vflr_event(pf); @@ -6991,6 +7043,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) vsi->idx = vsi_idx; vsi->rx_itr_setting = pf->rx_itr_default; vsi->tx_itr_setting = pf->tx_itr_default; + vsi->int_rate_limit = 0; vsi->rss_table_size = (vsi->type == I40E_VSI_MAIN) ? pf->rss_table_size : 64; vsi->netdev_registered = false; @@ -7566,7 +7619,7 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed) "Cannot set RSS key, err %s aq_err %s\n", i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - return ret; + goto config_rss_aq_out; } if (vsi->type == I40E_VSI_MAIN) @@ -7580,6 +7633,8 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed) i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); +config_rss_aq_out: + kfree(rss_lut); return ret; } @@ -7854,6 +7909,7 @@ static int i40e_sw_init(struct i40e_pf *pf) /* Set default capability flags */ pf->flags = I40E_FLAG_RX_CSUM_ENABLED | I40E_FLAG_MSI_ENABLED | + I40E_FLAG_LINK_POLLING_ENABLED | I40E_FLAG_MSIX_ENABLED; if (iommu_present(&pci_bus_type)) @@ -7896,12 +7952,12 @@ static int i40e_sw_init(struct i40e_pf *pf) (pf->hw.func_caps.fd_filters_best_effort > 0)) { pf->flags |= I40E_FLAG_FD_ATR_ENABLED; pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE; - if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) { - pf->flags |= I40E_FLAG_FD_SB_ENABLED; - } else { + if (pf->flags & I40E_FLAG_MFP_ENABLED && + pf->hw.num_partitions > 1) dev_info(&pf->pdev->dev, "Flow Director Sideband mode Disabled in MFP mode\n"); - } + else + pf->flags |= I40E_FLAG_FD_SB_ENABLED; pf->fdir_pf_filter_count = pf->hw.func_caps.fd_filters_guaranteed; pf->hw.fdir_shared_filter_count = @@ -7914,9 +7970,7 @@ static int i40e_sw_init(struct i40e_pf *pf) } #ifdef I40E_FCOE - err = i40e_init_pf_fcoe(pf); - if (err) - dev_info(&pf->pdev->dev, "init_pf_fcoe failed: %d\n", err); + i40e_init_pf_fcoe(pf); #endif /* I40E_FCOE */ #ifdef CONFIG_PCI_IOV @@ -7940,6 +7994,9 @@ static int i40e_sw_init(struct i40e_pf *pf) pf->lan_veb = I40E_NO_VEB; pf->lan_vsi = I40E_NO_VSI; + /* By default FW has this off for performance reasons */ + pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; + /* set up queue assignment tracking */ size = sizeof(struct i40e_lump_tracking) + (sizeof(u16) * pf->hw.func_caps.num_tx_qp); @@ -8119,9 +8176,6 @@ static void i40e_del_vxlan_port(struct net_device *netdev, pf->vxlan_ports[idx] = 0; pf->pending_vxlan_bitmap |= BIT_ULL(idx); pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; - - dev_info(&pf->pdev->dev, "deleting vxlan port %d\n", - ntohs(port)); } else { netdev_warn(netdev, "vxlan port %d was not found, not deleting\n", ntohs(port)); @@ -8273,13 +8327,15 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev, * @seq: RTNL message seq # * @dev: the netdev being configured * @filter_mask: unused + * @nlflags: netlink flags passed in * * Return the mode in which the hardware bridge is operating in * i.e VEB or VEPA. **/ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, - u32 filter_mask, int nlflags) + u32 __always_unused filter_mask, + int nlflags) { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; @@ -8773,7 +8829,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) list_for_each_entry_safe(f, ftmp, &vsi->mac_filter_list, list) i40e_del_filter(vsi, f->macaddr, f->vlan, f->is_vf, f->is_netdev); - i40e_sync_vsi_filters(vsi); + i40e_sync_vsi_filters(vsi, false); i40e_vsi_delete(vsi); i40e_vsi_free_q_vectors(vsi); @@ -8998,8 +9054,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, if (veb) { if (vsi->seid != pf->vsi[pf->lan_vsi]->seid) { dev_info(&vsi->back->pdev->dev, - "%s: New VSI creation error, uplink seid of LAN VSI expected.\n", - __func__); + "New VSI creation error, uplink seid of LAN VSI expected.\n"); return NULL; } /* We come up by default in VEPA mode if SRIOV is not @@ -9649,6 +9704,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) } else { /* force a reset of TC and queue layout configurations */ u8 enabled_tc = pf->vsi[pf->lan_vsi]->tc_config.enabled_tc; + pf->vsi[pf->lan_vsi]->tc_config.enabled_tc = 0; pf->vsi[pf->lan_vsi]->seid = pf->main_vsi_seid; i40e_vsi_config_tc(pf->vsi[pf->lan_vsi], enabled_tc); @@ -9672,7 +9728,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) i40e_config_rss(pf); /* fill in link information and enable LSE reporting */ - i40e_aq_get_link_info(&pf->hw, true, NULL, NULL); + i40e_update_link_info(&pf->hw); i40e_link_event(pf); /* Initialize user-specific link properties */ @@ -9790,8 +9846,14 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) } pf->queues_left = queues_left; + dev_dbg(&pf->pdev->dev, + "qs_avail=%d FD SB=%d lan_qs=%d lan_tc0=%d vf=%d*%d vmdq=%d*%d, remaining=%d\n", + pf->hw.func_caps.num_tx_qp, + !!(pf->flags & I40E_FLAG_FD_SB_ENABLED), + pf->num_lan_qps, pf->rss_size, pf->num_req_vfs, pf->num_vf_qps, + pf->num_vmdq_vsis, pf->num_vmdq_qps, queues_left); #ifdef I40E_FCOE - dev_info(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps); + dev_dbg(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps); #endif } @@ -9859,12 +9921,19 @@ static void i40e_print_features(struct i40e_pf *pf) } if (pf->flags & I40E_FLAG_DCB_CAPABLE) buf += sprintf(buf, "DCB "); +#if IS_ENABLED(CONFIG_VXLAN) + buf += sprintf(buf, "VxLAN "); +#endif if (pf->flags & I40E_FLAG_PTP) buf += sprintf(buf, "PTP "); #ifdef I40E_FCOE if (pf->flags & I40E_FLAG_FCOE_ENABLED) buf += sprintf(buf, "FCOE "); #endif + if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) + buf += sprintf(buf, "VEB "); + else + buf += sprintf(buf, "VEPA "); BUG_ON(buf > (string + INFO_STRING_LEN)); dev_info(&pf->pdev->dev, "%s\n", string); @@ -9885,10 +9954,10 @@ static void i40e_print_features(struct i40e_pf *pf) static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct i40e_aq_get_phy_abilities_resp abilities; - unsigned long ioremap_len; struct i40e_pf *pf; struct i40e_hw *hw; static u16 pfs_found; + u16 wol_nvm_bits; u16 link_status; int err = 0; u32 len; @@ -9938,15 +10007,15 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw = &pf->hw; hw->back = pf; - ioremap_len = min_t(unsigned long, pci_resource_len(pdev, 0), - I40E_MAX_CSR_SPACE); + pf->ioremap_len = min_t(int, pci_resource_len(pdev, 0), + I40E_MAX_CSR_SPACE); - hw->hw_addr = ioremap(pci_resource_start(pdev, 0), ioremap_len); + hw->hw_addr = ioremap(pci_resource_start(pdev, 0), pf->ioremap_len); if (!hw->hw_addr) { err = -EIO; dev_info(&pdev->dev, "ioremap(0x%04x, 0x%04x) failed: 0x%x\n", (unsigned int)pci_resource_start(pdev, 0), - (unsigned int)pci_resource_len(pdev, 0), err); + pf->ioremap_len, err); goto err_ioremap; } hw->vendor_id = pdev->vendor; @@ -10004,7 +10073,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->hw.fc.requested_mode = I40E_FC_NONE; err = i40e_init_adminq(hw); - dev_info(&pdev->dev, "%s\n", i40e_fw_version_str(hw)); + + /* provide nvm, fw, api versions */ + dev_info(&pdev->dev, "fw %d.%d.%05d api %d.%d nvm %s\n", + hw->aq.fw_maj_ver, hw->aq.fw_min_ver, hw->aq.fw_build, + hw->aq.api_maj_ver, hw->aq.api_min_ver, + i40e_nvm_version_str(hw)); + if (err) { dev_info(&pdev->dev, "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n"); @@ -10104,10 +10179,13 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_WORK(&pf->service_task, i40e_service_task); clear_bit(__I40E_SERVICE_SCHED, &pf->state); pf->flags |= I40E_FLAG_NEED_LINK_UPDATE; - pf->link_check_timeout = jiffies; - /* WoL defaults to disabled */ - pf->wol_en = false; + /* NVM bit on means WoL disabled for the port */ + i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); + if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1) + pf->wol_en = false; + else + pf->wol_en = true; device_set_wakeup_enable(&pf->pdev->dev, pf->wol_en); /* set up the main switch operations */ @@ -10238,37 +10316,73 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_fcoe_vsi_setup(pf); #endif - /* Get the negotiated link width and speed from PCI config space */ - pcie_capability_read_word(pf->pdev, PCI_EXP_LNKSTA, &link_status); +#define PCI_SPEED_SIZE 8 +#define PCI_WIDTH_SIZE 8 + /* Devices on the IOSF bus do not have this information + * and will report PCI Gen 1 x 1 by default so don't bother + * checking them. + */ + if (!(pf->flags & I40E_FLAG_NO_PCI_LINK_CHECK)) { + char speed[PCI_SPEED_SIZE] = "Unknown"; + char width[PCI_WIDTH_SIZE] = "Unknown"; - i40e_set_pci_config_data(hw, link_status); + /* Get the negotiated link width and speed from PCI config + * space + */ + pcie_capability_read_word(pf->pdev, PCI_EXP_LNKSTA, + &link_status); + + i40e_set_pci_config_data(hw, link_status); + + switch (hw->bus.speed) { + case i40e_bus_speed_8000: + strncpy(speed, "8.0", PCI_SPEED_SIZE); break; + case i40e_bus_speed_5000: + strncpy(speed, "5.0", PCI_SPEED_SIZE); break; + case i40e_bus_speed_2500: + strncpy(speed, "2.5", PCI_SPEED_SIZE); break; + default: + break; + } + switch (hw->bus.width) { + case i40e_bus_width_pcie_x8: + strncpy(width, "8", PCI_WIDTH_SIZE); break; + case i40e_bus_width_pcie_x4: + strncpy(width, "4", PCI_WIDTH_SIZE); break; + case i40e_bus_width_pcie_x2: + strncpy(width, "2", PCI_WIDTH_SIZE); break; + case i40e_bus_width_pcie_x1: + strncpy(width, "1", PCI_WIDTH_SIZE); break; + default: + break; + } - dev_info(&pdev->dev, "PCI-Express: %s %s\n", - (hw->bus.speed == i40e_bus_speed_8000 ? "Speed 8.0GT/s" : - hw->bus.speed == i40e_bus_speed_5000 ? "Speed 5.0GT/s" : - hw->bus.speed == i40e_bus_speed_2500 ? "Speed 2.5GT/s" : - "Unknown"), - (hw->bus.width == i40e_bus_width_pcie_x8 ? "Width x8" : - hw->bus.width == i40e_bus_width_pcie_x4 ? "Width x4" : - hw->bus.width == i40e_bus_width_pcie_x2 ? "Width x2" : - hw->bus.width == i40e_bus_width_pcie_x1 ? "Width x1" : - "Unknown")); + dev_info(&pdev->dev, "PCI-Express: Speed %sGT/s Width x%s\n", + speed, width); - if (hw->bus.width < i40e_bus_width_pcie_x8 || - hw->bus.speed < i40e_bus_speed_8000) { - dev_warn(&pdev->dev, "PCI-Express bandwidth available for this device may be insufficient for optimal performance.\n"); - dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n"); + if (hw->bus.width < i40e_bus_width_pcie_x8 || + hw->bus.speed < i40e_bus_speed_8000) { + dev_warn(&pdev->dev, "PCI-Express bandwidth available for this device may be insufficient for optimal performance.\n"); + dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n"); + } } /* get the requested speeds from the fw */ err = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, NULL); if (err) - dev_info(&pf->pdev->dev, - "get phy capabilities failed, err %s aq_err %s, advertised speed settings may not be correct\n", - i40e_stat_str(&pf->hw, err), - i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + dev_dbg(&pf->pdev->dev, "get requested speeds ret = %s last_status = %s\n", + i40e_stat_str(&pf->hw, err), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); pf->hw.phy.link_info.requested_speeds = abilities.link_speed; + /* get the supported phy types from the fw */ + err = i40e_aq_get_phy_capabilities(hw, false, true, &abilities, NULL); + if (err) + dev_dbg(&pf->pdev->dev, "get supported phy types ret = %s last_status = %s\n", + i40e_stat_str(&pf->hw, err), + i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + pf->hw.phy.phy_types = le32_to_cpu(abilities.phy_type); + /* print a string summarizing features */ i40e_print_features(pf); @@ -10439,7 +10553,7 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev) int err; u32 reg; - dev_info(&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); if (pci_enable_device_mem(pdev)) { dev_info(&pdev->dev, "Cannot re-enable PCI device after reset.\n"); @@ -10479,13 +10593,13 @@ static void i40e_pci_error_resume(struct pci_dev *pdev) { struct i40e_pf *pf = pci_get_drvdata(pdev); - dev_info(&pdev->dev, "%s\n", __func__); + dev_dbg(&pdev->dev, "%s\n", __func__); if (test_bit(__I40E_SUSPENDED, &pf->state)) return; rtnl_lock(); i40e_handle_reset_warning(pf); - rtnl_lock(); + rtnl_unlock(); } /** @@ -10571,9 +10685,7 @@ static int i40e_resume(struct pci_dev *pdev) err = pci_enable_device_mem(pdev); if (err) { - dev_err(&pdev->dev, - "%s: Cannot enable PCI device from suspend\n", - __func__); + dev_err(&pdev->dev, "Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 9b83abc0e774..875a8ccc2611 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -418,6 +418,10 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, bool last_command) { i40e_status ret_code = I40E_ERR_NVM; + struct i40e_asq_cmd_details cmd_details; + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; /* Here we are checking the SR limit only for the flat memory model. * We cannot do it for the module-based model, as we did not acquire @@ -443,7 +447,7 @@ static i40e_status i40e_write_nvm_aq(struct i40e_hw *hw, u8 module_pointer, ret_code = i40e_aq_update_nvm(hw, module_pointer, 2 * offset, /*bytes*/ 2 * words, /*bytes*/ - data, last_command, NULL); + data, last_command, &cmd_details); return ret_code; } @@ -543,11 +547,13 @@ i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw) { i40e_status ret_code = 0; u16 checksum; + __le16 le_sum; ret_code = i40e_calc_nvm_checksum(hw, &checksum); + le_sum = cpu_to_le16(checksum); if (!ret_code) ret_code = i40e_write_nvm_aq(hw, 0x00, I40E_SR_SW_CHECKSUM_WORD, - 1, &checksum, true); + 1, &le_sum, true); return ret_code; } @@ -592,25 +598,31 @@ i40e_validate_nvm_checksum_exit: static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, struct i40e_nvm_access *cmd, u8 *bytes, int *errno); static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno); + int *perrno); static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno); + int *perrno); static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno); + u8 *bytes, int *perrno); +static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); +static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno); static inline u8 i40e_nvmupd_get_module(u32 val) { return (u8)(val & I40E_NVM_MOD_PNT_MASK); @@ -620,7 +632,7 @@ static inline u8 i40e_nvmupd_get_transaction(u32 val) return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT); } -static char *i40e_nvm_update_state_str[] = { +static const char * const i40e_nvm_update_state_str[] = { "I40E_NVMUPD_INVALID", "I40E_NVMUPD_READ_CON", "I40E_NVMUPD_READ_SNT", @@ -634,6 +646,9 @@ static char *i40e_nvm_update_state_str[] = { "I40E_NVMUPD_CSUM_CON", "I40E_NVMUPD_CSUM_SA", "I40E_NVMUPD_CSUM_LCB", + "I40E_NVMUPD_STATUS", + "I40E_NVMUPD_EXEC_AQ", + "I40E_NVMUPD_GET_AQ_RESULT", }; /** @@ -641,30 +656,60 @@ static char *i40e_nvm_update_state_str[] = { * @hw: pointer to hardware structure * @cmd: pointer to nvm update command * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Dispatches command depending on what update state is current **/ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status; + enum i40e_nvmupd_cmd upd_cmd; /* assume success */ - *errno = 0; + *perrno = 0; + + /* early check for status command and debug msgs */ + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); + + i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n", + i40e_nvm_update_state_str[upd_cmd], + hw->nvmupd_state, + hw->aq.nvm_release_on_done); + + if (upd_cmd == I40E_NVMUPD_INVALID) { + *perrno = -EFAULT; + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_validate_command returns %d errno %d\n", + upd_cmd, *perrno); + } + + /* a status request returns immediately rather than + * going into the state machine + */ + if (upd_cmd == I40E_NVMUPD_STATUS) { + bytes[0] = hw->nvmupd_state; + return 0; + } switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT: - status = i40e_nvmupd_state_init(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_STATE_READING: - status = i40e_nvmupd_state_reading(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_STATE_WRITING: - status = i40e_nvmupd_state_writing(hw, cmd, bytes, errno); + status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_STATE_INIT_WAIT: + case I40E_NVMUPD_STATE_WRITE_WAIT: + status = I40E_ERR_NOT_READY; + *perrno = -EBUSY; break; default: @@ -672,7 +717,7 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: no such state %d\n", hw->nvmupd_state); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -683,28 +728,28 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Process legitimate commands of the Init state and conditionally set next * state. Reject all other commands. **/ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); switch (upd_cmd) { case I40E_NVMUPD_READ_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); i40e_release_nvm(hw); } break; @@ -712,10 +757,10 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, case I40E_NVMUPD_READ_SNT: status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); if (status) i40e_release_nvm(hw); else @@ -726,70 +771,83 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, case I40E_NVMUPD_WRITE_ERA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_erase(hw, cmd, errno); - if (status) + status = i40e_nvmupd_nvm_erase(hw, cmd, perrno); + if (status) { i40e_release_nvm(hw); - else + } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } } break; case I40E_NVMUPD_WRITE_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); - if (status) + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { i40e_release_nvm(hw); - else + } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } } break; case I40E_NVMUPD_WRITE_SNT: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); if (status) i40e_release_nvm(hw); else - hw->nvmupd_state = I40E_NVMUPD_STATE_WRITING; + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; } break; case I40E_NVMUPD_CSUM_SA: status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE); if (status) { - *errno = i40e_aq_rc_to_posix(status, + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } else { status = i40e_update_nvm_checksum(hw); if (status) { - *errno = hw->aq.asq_last_status ? + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; i40e_release_nvm(hw); } else { hw->aq.nvm_release_on_done = true; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; } } break; + case I40E_NVMUPD_EXEC_AQ: + status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno); + break; + + case I40E_NVMUPD_GET_AQ_RESULT: + status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno); + break; + default: i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: bad cmd %s in init state\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_ERR_NVM; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -800,28 +858,28 @@ static i40e_status i40e_nvmupd_state_init(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * NVM ownership is already held. Process legitimate commands and set any * change in state; reject all other commands. **/ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { - i40e_status status; + i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); switch (upd_cmd) { case I40E_NVMUPD_READ_SA: case I40E_NVMUPD_READ_CON: - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); break; case I40E_NVMUPD_READ_LCB: - status = i40e_nvmupd_nvm_read(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno); i40e_release_nvm(hw); hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; break; @@ -831,7 +889,7 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, "NVMUPD: bad cmd %s in reading state.\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } return status; @@ -842,55 +900,68 @@ static i40e_status i40e_nvmupd_state_reading(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * NVM ownership is already held. Process legitimate commands and set any * change in state; reject all other commands **/ static i40e_status i40e_nvmupd_state_writing(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { - i40e_status status; + i40e_status status = 0; enum i40e_nvmupd_cmd upd_cmd; bool retry_attempt = false; - upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno); + upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno); retry: switch (upd_cmd) { case I40E_NVMUPD_WRITE_CON: - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (!status) + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; break; case I40E_NVMUPD_WRITE_LCB: - status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno); - if (!status) + status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno); + if (status) { + *perrno = hw->aq.asq_last_status ? + i40e_aq_rc_to_posix(status, + hw->aq.asq_last_status) : + -EIO; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { hw->aq.nvm_release_on_done = true; - hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } break; case I40E_NVMUPD_CSUM_CON: status = i40e_update_nvm_checksum(hw); if (status) { - *errno = hw->aq.asq_last_status ? + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { + hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT; } break; case I40E_NVMUPD_CSUM_LCB: status = i40e_update_nvm_checksum(hw); - if (status) - *errno = hw->aq.asq_last_status ? + if (status) { + *perrno = hw->aq.asq_last_status ? i40e_aq_rc_to_posix(status, hw->aq.asq_last_status) : -EIO; - else + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + } else { hw->aq.nvm_release_on_done = true; - hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; + hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT; + } break; default: @@ -898,7 +969,7 @@ retry: "NVMUPD: bad cmd %s in writing state.\n", i40e_nvm_update_state_str[upd_cmd]); status = I40E_NOT_SUPPORTED; - *errno = -ESRCH; + *perrno = -ESRCH; break; } @@ -941,21 +1012,22 @@ retry: * i40e_nvmupd_validate_command - Validate given command * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * Return one of the valid command types or I40E_NVMUPD_INVALID **/ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno) + int *perrno) { enum i40e_nvmupd_cmd upd_cmd; - u8 transaction; + u8 module, transaction; /* anything that doesn't match a recognized case is an error */ upd_cmd = I40E_NVMUPD_INVALID; transaction = i40e_nvmupd_get_transaction(cmd->config); + module = i40e_nvmupd_get_module(cmd->config); /* limits on data size */ if ((cmd->data_size < 1) || @@ -963,7 +1035,7 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_validate_command data_size %d\n", cmd->data_size); - *errno = -EFAULT; + *perrno = -EFAULT; return I40E_NVMUPD_INVALID; } @@ -982,6 +1054,12 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, case I40E_NVM_SA: upd_cmd = I40E_NVMUPD_READ_SA; break; + case I40E_NVM_EXEC: + if (module == 0xf) + upd_cmd = I40E_NVMUPD_STATUS; + else if (module == 0) + upd_cmd = I40E_NVMUPD_GET_AQ_RESULT; + break; } break; @@ -1011,21 +1089,155 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, case (I40E_NVM_CSUM|I40E_NVM_LCB): upd_cmd = I40E_NVMUPD_CSUM_LCB; break; + case I40E_NVM_EXEC: + if (module == 0) + upd_cmd = I40E_NVMUPD_EXEC_AQ; + break; } break; } - i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n", - i40e_nvm_update_state_str[upd_cmd], - hw->nvmupd_state, - hw->aq.nvm_release_on_done); - if (upd_cmd == I40E_NVMUPD_INVALID) { - *errno = -EFAULT; + return upd_cmd; +} + +/** + * i40e_nvmupd_exec_aq - Run an AQ command + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static i40e_status i40e_nvmupd_exec_aq(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + struct i40e_asq_cmd_details cmd_details; + i40e_status status; + struct i40e_aq_desc *aq_desc; + u32 buff_size = 0; + u8 *buff = NULL; + u32 aq_desc_len; + u32 aq_data_len; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + + aq_desc_len = sizeof(struct i40e_aq_desc); + memset(&hw->nvm_wb_desc, 0, aq_desc_len); + + /* get the aq descriptor */ + if (cmd->data_size < aq_desc_len) { i40e_debug(hw, I40E_DEBUG_NVM, - "i40e_nvmupd_validate_command returns %d errno %d\n", - upd_cmd, *errno); + "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n", + cmd->data_size, aq_desc_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; } - return upd_cmd; + aq_desc = (struct i40e_aq_desc *)bytes; + + /* if data buffer needed, make sure it's ready */ + aq_data_len = cmd->data_size - aq_desc_len; + buff_size = max_t(u32, aq_data_len, le16_to_cpu(aq_desc->datalen)); + if (buff_size) { + if (!hw->nvm_buff.va) { + status = i40e_allocate_virt_mem(hw, &hw->nvm_buff, + hw->aq.asq_buf_size); + if (status) + i40e_debug(hw, I40E_DEBUG_NVM, + "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n", + status); + } + + if (hw->nvm_buff.va) { + buff = hw->nvm_buff.va; + memcpy(buff, &bytes[aq_desc_len], aq_data_len); + } + } + + /* and away we go! */ + status = i40e_asq_send_command(hw, aq_desc, buff, + buff_size, &cmd_details); + if (status) { + i40e_debug(hw, I40E_DEBUG_NVM, + "i40e_nvmupd_exec_aq err %s aq_err %s\n", + i40e_stat_str(hw, status), + i40e_aq_str(hw, hw->aq.asq_last_status)); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + } + + return status; +} + +/** + * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq + * @hw: pointer to hardware structure + * @cmd: pointer to nvm update command buffer + * @bytes: pointer to the data buffer + * @perrno: pointer to return error code + * + * cmd structure contains identifiers and data buffer + **/ +static i40e_status i40e_nvmupd_get_aq_result(struct i40e_hw *hw, + struct i40e_nvm_access *cmd, + u8 *bytes, int *perrno) +{ + u32 aq_total_len; + u32 aq_desc_len; + int remainder; + u8 *buff; + + i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__); + + aq_desc_len = sizeof(struct i40e_aq_desc); + aq_total_len = aq_desc_len + le16_to_cpu(hw->nvm_wb_desc.datalen); + + /* check offset range */ + if (cmd->offset > aq_total_len) { + i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n", + __func__, cmd->offset, aq_total_len); + *perrno = -EINVAL; + return I40E_ERR_PARAM; + } + + /* check copylength range */ + if (cmd->data_size > (aq_total_len - cmd->offset)) { + int new_len = aq_total_len - cmd->offset; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n", + __func__, cmd->data_size, new_len); + cmd->data_size = new_len; + } + + remainder = cmd->data_size; + if (cmd->offset < aq_desc_len) { + u32 len = aq_desc_len - cmd->offset; + + len = min(len, cmd->data_size); + i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n", + __func__, cmd->offset, cmd->offset + len); + + buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset; + memcpy(bytes, buff, len); + + bytes += len; + remainder -= len; + buff = hw->nvm_buff.va; + } else { + buff = hw->nvm_buff.va + (cmd->offset - aq_desc_len); + } + + if (remainder > 0) { + int start_byte = buff - (u8 *)hw->nvm_buff.va; + + i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n", + __func__, start_byte, start_byte + remainder); + memcpy(bytes, buff, remainder); + } + + return 0; } /** @@ -1033,14 +1245,15 @@ static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * cmd structure contains identifiers and data buffer **/ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { + struct i40e_asq_cmd_details cmd_details; i40e_status status; u8 module, transaction; bool last; @@ -1049,8 +1262,11 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, module = i40e_nvmupd_get_module(cmd->config); last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size, - bytes, last, NULL); + bytes, last, &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n", @@ -1058,7 +1274,7 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_read status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; @@ -1068,23 +1284,28 @@ static i40e_status i40e_nvmupd_nvm_read(struct i40e_hw *hw, * i40e_nvmupd_nvm_erase - Erase an NVM module * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - int *errno) + int *perrno) { i40e_status status = 0; + struct i40e_asq_cmd_details cmd_details; u8 module, transaction; bool last; transaction = i40e_nvmupd_get_transaction(cmd->config); module = i40e_nvmupd_get_module(cmd->config); last = (transaction & I40E_NVM_LCB); + + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size, - last, NULL); + last, &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n", @@ -1092,7 +1313,7 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_erase status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; @@ -1103,15 +1324,16 @@ static i40e_status i40e_nvmupd_nvm_erase(struct i40e_hw *hw, * @hw: pointer to hardware structure * @cmd: pointer to nvm update command buffer * @bytes: pointer to the data buffer - * @errno: pointer to return error code + * @perrno: pointer to return error code * * module, offset, data_size and data are in cmd structure **/ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, struct i40e_nvm_access *cmd, - u8 *bytes, int *errno) + u8 *bytes, int *perrno) { i40e_status status = 0; + struct i40e_asq_cmd_details cmd_details; u8 module, transaction; bool last; @@ -1119,8 +1341,12 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, module = i40e_nvmupd_get_module(cmd->config); last = (transaction & I40E_NVM_LCB); + memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; + status = i40e_aq_update_nvm(hw, module, cmd->offset, - (u16)cmd->data_size, bytes, last, NULL); + (u16)cmd->data_size, bytes, last, + &cmd_details); if (status) { i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n", @@ -1128,7 +1354,7 @@ static i40e_status i40e_nvmupd_nvm_write(struct i40e_hw *hw, i40e_debug(hw, I40E_DEBUG_NVM, "i40e_nvmupd_nvm_write status %d aq %d\n", status, hw->aq.asq_last_status); - *errno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); + *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status); } return status; diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index dcb72a8ee8e5..10bf2ba8bc15 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -58,8 +58,8 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void i40e_idle_aq(struct i40e_hw *hw); bool i40e_check_asq_alive(struct i40e_hw *hw); i40e_status i40e_aq_queue_shutdown(struct i40e_hw *hw, bool unloading); -char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); -char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err); +const char *i40e_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); +const char *i40e_stat_str(struct i40e_hw *hw, i40e_status stat_err); i40e_status i40e_aq_get_rss_lut(struct i40e_hw *hw, u16 seid, bool pf_lut, u8 *lut, u16 lut_size); @@ -258,7 +258,8 @@ i40e_status i40e_init_shared_code(struct i40e_hw *hw); i40e_status i40e_pf_reset(struct i40e_hw *hw); void i40e_clear_hw(struct i40e_hw *hw); void i40e_clear_pxe_mode(struct i40e_hw *hw); -bool i40e_get_link_status(struct i40e_hw *hw); +i40e_status i40e_get_link_status(struct i40e_hw *hw, bool *link_up); +i40e_status i40e_update_link_info(struct i40e_hw *hw); i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr); i40e_status i40e_read_bw_from_alt_ram(struct i40e_hw *hw, u32 *max_bw, u32 *min_bw, bool *min_valid, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 8c40d6ea15fd..565ca7c835bc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -618,9 +618,8 @@ static long i40e_ptp_create_clock(struct i40e_pf *pf) /* Attempt to register the clock before enabling the hardware. */ pf->ptp_clock = ptp_clock_register(&pf->ptp_caps, &pf->pdev->dev); - if (IS_ERR(pf->ptp_clock)) { + if (IS_ERR(pf->ptp_clock)) return PTR_ERR(pf->ptp_clock); - } /* clear the hwtstamp settings here during clock create, instead of * during regular init, so that we can maintain settings across a @@ -675,8 +674,8 @@ void i40e_ptp_init(struct i40e_pf *pf) struct timespec64 ts; u32 regval; - dev_info(&pf->pdev->dev, "%s: added PHC on %s\n", __func__, - netdev->name); + if (pf->hw.debug_mask & I40E_DEBUG_LAN) + dev_info(&pf->pdev->dev, "PHC enabled\n"); pf->flags |= I40E_FLAG_PTP; /* Ensure the clocks are running. */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 738aca68f665..512707c2898e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -465,10 +465,11 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, I40E_RX_PROG_STATUS_DESC_QW1_ERROR_SHIFT; if (error == BIT(I40E_RX_PROG_STATUS_DESC_FD_TBL_FULL_SHIFT)) { + pf->fd_inv = le32_to_cpu(rx_desc->wb.qword0.hi_dword.fd_id); if ((rx_desc->wb.qword0.hi_dword.fd_id != 0) || (I40E_DEBUG_FD & pf->hw.debug_mask)) dev_warn(&pdev->dev, "ntuple filter loc = %d, could not be added\n", - rx_desc->wb.qword0.hi_dword.fd_id); + pf->fd_inv); /* Check if the programming error is for ATR. * If so, auto disable ATR and set a state for @@ -601,27 +602,13 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring) } /** - * i40e_get_head - Retrieve head from head writeback - * @tx_ring: tx ring to fetch head of - * - * Returns value of Tx ring head based on value stored - * in head write-back location - **/ -static inline u32 i40e_get_head(struct i40e_ring *tx_ring) -{ - void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; - - return le32_to_cpu(*(volatile __le32 *)head); -} - -/** * i40e_get_tx_pending - how many tx descriptors not processed * @tx_ring: the ring of descriptors * * Since there is no access to the ring head register * in XL710, we need to use our local copies **/ -static u32 i40e_get_tx_pending(struct i40e_ring *ring) +u32 i40e_get_tx_pending(struct i40e_ring *ring) { u32 head, tail; @@ -635,50 +622,6 @@ static u32 i40e_get_tx_pending(struct i40e_ring *ring) return 0; } -/** - * i40e_check_tx_hang - Is there a hang in the Tx queue - * @tx_ring: the ring of descriptors - **/ -static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) -{ - u32 tx_done = tx_ring->stats.packets; - u32 tx_done_old = tx_ring->tx_stats.tx_done_old; - u32 tx_pending = i40e_get_tx_pending(tx_ring); - struct i40e_pf *pf = tx_ring->vsi->back; - bool ret = false; - - clear_check_for_tx_hang(tx_ring); - - /* Check for a hung queue, but be thorough. This verifies - * that a transmit has been completed since the previous - * check AND there is at least one packet pending. The - * ARMED bit is set to indicate a potential hang. The - * bit is cleared if a pause frame is received to remove - * false hang detection due to PFC or 802.3x frames. By - * requiring this to fail twice we avoid races with - * PFC clearing the ARMED bit and conditions where we - * run the check_tx_hang logic with a transmit completion - * pending but without time to complete it yet. - */ - if ((tx_done_old == tx_done) && tx_pending) { - /* make sure it is true for two checks in a row */ - ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED, - &tx_ring->state); - } else if (tx_done_old == tx_done && - (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) { - if (I40E_DEBUG_FLOW & pf->hw.debug_mask) - dev_info(tx_ring->dev, "HW needs some more descs to do a cacheline flush. tx_pending %d, queue %d", - tx_pending, tx_ring->queue_index); - pf->tx_sluggish_count++; - } else { - /* update completed stats and disarm the hang check */ - tx_ring->tx_stats.tx_done_old = tx_done; - clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); - } - - return ret; -} - #define WB_STRIDE 0x3 /** @@ -784,42 +727,21 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) tx_ring->q_vector->tx.total_bytes += total_bytes; tx_ring->q_vector->tx.total_packets += total_packets; - /* check to see if there are any non-cache aligned descriptors - * waiting to be written back, and kick the hardware to force - * them to be written back in case of napi polling - */ - if (budget && - !((i & WB_STRIDE) == WB_STRIDE) && - !test_bit(__I40E_DOWN, &tx_ring->vsi->state) && - (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) - tx_ring->arm_wb = true; - else - tx_ring->arm_wb = false; - - if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) { - /* schedule immediate reset if we believe we hung */ - dev_info(tx_ring->dev, "Detected Tx Unit Hang\n" - " VSI <%d>\n" - " Tx Queue <%d>\n" - " next_to_use <%x>\n" - " next_to_clean <%x>\n", - tx_ring->vsi->seid, - tx_ring->queue_index, - tx_ring->next_to_use, i); - - netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); - - dev_info(tx_ring->dev, - "tx hang detected on queue %d, reset requested\n", - tx_ring->queue_index); - - /* do not fire the reset immediately, wait for the stack to - * decide we are truly stuck, also prevents every queue from - * simultaneously requesting a reset + if (tx_ring->flags & I40E_TXR_FLAGS_WB_ON_ITR) { + unsigned int j = 0; + + /* check to see if there are < 4 descriptors + * waiting to be written back, then kick the hardware to force + * them to be written back in case we stay in NAPI. + * In this mode on X722 we do not enable Interrupt. */ + j = i40e_get_tx_pending(tx_ring); - /* the adapter is about to reset, no point in enabling polling */ - budget = 1; + if (budget && + ((j / (WB_STRIDE + 1)) == 0) && (j != 0) && + !test_bit(__I40E_DOWN, &tx_ring->vsi->state) && + (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) + tx_ring->arm_wb = true; } netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev, @@ -851,7 +773,7 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) * @q_vector: the vector on which to force writeback * **/ -static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) +void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) { u16 flags = q_vector->tx.ring[0].flags; @@ -1002,6 +924,8 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring) if (!dev) return -ENOMEM; + /* warn if we are about to overwrite the pointer */ + WARN_ON(tx_ring->tx_bi); bi_size = sizeof(struct i40e_tx_buffer) * tx_ring->count; tx_ring->tx_bi = kzalloc(bi_size, GFP_KERNEL); if (!tx_ring->tx_bi) @@ -1162,6 +1086,8 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) struct device *dev = rx_ring->dev; int bi_size; + /* warn if we are about to overwrite the pointer */ + WARN_ON(rx_ring->rx_bi); bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count; rx_ring->rx_bi = kzalloc(bi_size, GFP_KERNEL); if (!rx_ring->rx_bi) @@ -1342,16 +1268,11 @@ static void i40e_receive_skb(struct i40e_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag) { struct i40e_q_vector *q_vector = rx_ring->q_vector; - struct i40e_vsi *vsi = rx_ring->vsi; - u64 flags = vsi->back->flags; if (vlan_tag & VLAN_VID_MASK) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); - if (flags & I40E_FLAG_IN_NETPOLL) - netif_rx(skb); - else - napi_gro_receive(&q_vector->napi, skb); + napi_gro_receive(&q_vector->napi, skb); } /** @@ -1518,7 +1439,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - const int current_node = numa_node_id(); + const int current_node = numa_mem_id(); struct i40e_vsi *vsi = rx_ring->vsi; u16 i = rx_ring->next_to_clean; union i40e_rx_desc *rx_desc; @@ -1596,6 +1517,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) cleaned_count++; if (rx_hbo || rx_sph) { int len; + if (rx_hbo) len = I40E_RX_HDR_SIZE; else @@ -1781,9 +1703,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) /* ERR_MASK will only have valid bits if EOP set */ if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { dev_kfree_skb_any(skb); - /* TODO: shouldn't we increment a counter indicating the - * drop? - */ continue; } @@ -1862,8 +1781,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, if (!test_bit(__I40E_DOWN, &vsi->state)) wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val); } else { - i40e_irq_dynamic_enable(vsi, - q_vector->v_idx + vsi->base_vector); + i40e_irq_dynamic_enable(vsi, q_vector->v_idx); } if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) { old_itr = q_vector->tx.itr; @@ -1885,8 +1803,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, wr32(hw, I40E_PFINT_DYN_CTLN(q_vector->v_idx + vsi->base_vector - 1), val); } else { - i40e_irq_dynamic_enable(vsi, - q_vector->v_idx + vsi->base_vector); + i40e_irq_dynamic_enable(vsi, q_vector->v_idx); } } @@ -1908,7 +1825,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) bool clean_complete = true; bool arm_wb = false; int budget_per_ring; - int cleaned; + int work_done = 0; if (test_bit(__I40E_DOWN, &vsi->state)) { napi_complete(napi); @@ -1921,24 +1838,34 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); arm_wb |= ring->arm_wb; + ring->arm_wb = false; } + /* Handle case where we are called by netpoll with a budget of 0 */ + if (budget <= 0) + goto tx_only; + /* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early. */ budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { + int cleaned; + if (ring_is_ps_enabled(ring)) cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring); else cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); + + work_done += cleaned; /* if we didn't clean as many as budgeted, we must be done */ clean_complete &= (budget_per_ring != cleaned); } /* If work not completed, return budget and polling will return */ if (!clean_complete) { +tx_only: if (arm_wb) i40e_force_wb(vsi, q_vector); return budget; @@ -1948,7 +1875,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) q_vector->arm_wb_state = false; /* Work is done so exit the polling mode and re-enable the interrupt */ - napi_complete(napi); + napi_complete_done(napi, work_done); if (vsi->back->flags & I40E_FLAG_MSIX_ENABLED) { i40e_update_enable_itr(vsi, q_vector); } else { /* Legacy mode */ @@ -2156,6 +2083,7 @@ static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { struct vlan_hdr *vhdr, _vhdr; + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr); if (!vhdr) return -EINVAL; @@ -2324,6 +2252,9 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING; *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL; break; + case IPPROTO_GRE: + l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING; + break; default: return; } @@ -2581,6 +2512,9 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, u32 td_tag = 0; dma_addr_t dma; u16 gso_segs; + u16 desc_count = 0; + bool tail_bump = true; + bool do_rs = false; if (tx_flags & I40E_TX_FLAGS_HW_VLAN) { td_cmd |= I40E_TX_DESC_CMD_IL2TAG1; @@ -2621,6 +2555,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; + desc_count++; + if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); i = 0; @@ -2640,6 +2576,8 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; + desc_count++; + if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); i = 0; @@ -2654,34 +2592,6 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_bi = &tx_ring->tx_bi[i]; } - /* Place RS bit on last descriptor of any packet that spans across the - * 4th descriptor (WB_STRIDE aka 0x3) in a 64B cacheline. - */ - if (((i & WB_STRIDE) != WB_STRIDE) && - (first <= &tx_ring->tx_bi[i]) && - (first >= &tx_ring->tx_bi[i & ~WB_STRIDE])) { - tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)I40E_TX_DESC_CMD_EOP << - I40E_TXD_QW1_CMD_SHIFT); - } else { - tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)I40E_TXD_CMD << - I40E_TXD_QW1_CMD_SHIFT); - } - - netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev, - tx_ring->queue_index), - first->bytecount); - - /* Force memory writes to complete before letting h/w - * know there are new descriptors to fetch. (Only - * applicable for weak-ordered memory model archs, - * such as IA-64). - */ - wmb(); - /* set next_to_watch value indicating a packet is present */ first->next_to_watch = tx_desc; @@ -2691,15 +2601,72 @@ static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_ring->next_to_use = i; + netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index), + first->bytecount); i40e_maybe_stop_tx(tx_ring, DESC_NEEDED); + + /* Algorithm to optimize tail and RS bit setting: + * if xmit_more is supported + * if xmit_more is true + * do not update tail and do not mark RS bit. + * if xmit_more is false and last xmit_more was false + * if every packet spanned less than 4 desc + * then set RS bit on 4th packet and update tail + * on every packet + * else + * update tail and set RS bit on every packet. + * if xmit_more is false and last_xmit_more was true + * update tail and set RS bit. + * + * Optimization: wmb to be issued only in case of tail update. + * Also optimize the Descriptor WB path for RS bit with the same + * algorithm. + * + * Note: If there are less than 4 packets + * pending and interrupts were disabled the service task will + * trigger a force WB. + */ + if (skb->xmit_more && + !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index))) { + tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; + tail_bump = false; + } else if (!skb->xmit_more && + !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index)) && + (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) && + (tx_ring->packet_stride < WB_STRIDE) && + (desc_count < WB_STRIDE)) { + tx_ring->packet_stride++; + } else { + tx_ring->packet_stride = 0; + tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; + do_rs = true; + } + if (do_rs) + tx_ring->packet_stride = 0; + + tx_desc->cmd_type_offset_bsz = + build_ctob(td_cmd, td_offset, size, td_tag) | + cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD : + I40E_TX_DESC_CMD_EOP) << + I40E_TXD_QW1_CMD_SHIFT); + /* notify HW of packet */ - if (!skb->xmit_more || - netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, - tx_ring->queue_index))) - writel(i, tx_ring->tail); - else + if (!tail_bump) prefetchw(tx_desc + 1); + if (tail_bump) { + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). + */ + wmb(); + writel(i, tx_ring->tail); + } + return; dma_error: @@ -2776,6 +2743,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u8 hdr_len = 0; int tsyn; int tso; + if (0 == i40e_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; @@ -2808,10 +2776,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, if (tsyn) tx_flags |= I40E_TX_FLAGS_TSYN; - if (i40e_chk_linearize(skb, tx_flags)) + if (i40e_chk_linearize(skb, tx_flags)) { if (skb_linearize(skb)) goto out_drop; - + tx_ring->tx_stats.tx_linearize++; + } skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index f1385a1989fa..7c0ed84b296d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -35,6 +35,7 @@ #define I40E_ITR_20K 0x0019 #define I40E_ITR_8K 0x003E #define I40E_ITR_4K 0x007A +#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */ #define I40E_ITR_RX_DEF I40E_ITR_8K #define I40E_ITR_TX_DEF I40E_ITR_4K #define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */ @@ -44,6 +45,15 @@ #define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1) #define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC)) #define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1) +/* 0x40 is the enable bit for interrupt rate limiting, and must be set if + * the value of the rate limit is non-zero + */ +#define INTRL_ENA BIT(6) +#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2) +#define INTRL_USEC_TO_REG(set) ((set) ? ((set) >> 2) | INTRL_ENA : 0) +#define I40E_INTRL_8K 125 /* 8000 ints/sec */ +#define I40E_INTRL_62K 16 /* 62500 ints/sec */ +#define I40E_INTRL_83K 12 /* 83333 ints/sec */ #define I40E_QUEUE_END_OF_LIST 0x7FF @@ -79,12 +89,12 @@ enum i40e_dyn_idx_t { BIT_ULL(I40E_FILTER_PCTYPE_L2_PAYLOAD)) #define I40E_DEFAULT_RSS_HENA_EXPANDED (I40E_DEFAULT_RSS_HENA | \ - BIT(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \ - BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \ - BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \ - BIT(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \ - BIT(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ - BIT(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) + BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ + BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) #define i40e_pf_get_default_rss_hena(pf) \ (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ @@ -165,6 +175,7 @@ struct i40e_tx_buffer { }; unsigned int bytecount; unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); DEFINE_DMA_UNMAP_LEN(len); u32 tx_flags; @@ -188,6 +199,7 @@ struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; u64 tx_done_old; + u64 tx_linearize; }; struct i40e_rx_queue_stats { @@ -199,8 +211,6 @@ struct i40e_rx_queue_stats { enum i40e_ring_state_t { __I40E_TX_FDIR_INIT_DONE, __I40E_TX_XPS_INIT_DONE, - __I40E_TX_DETECT_HANG, - __I40E_HANG_CHECK_ARMED, __I40E_RX_PS_ENABLED, __I40E_RX_16BYTE_DESC_ENABLED, }; @@ -211,12 +221,6 @@ enum i40e_ring_state_t { set_bit(__I40E_RX_PS_ENABLED, &(ring)->state) #define clear_ring_ps_enabled(ring) \ clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define check_for_tx_hang(ring) \ - test_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define set_check_for_tx_hang(ring) \ - set_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define clear_check_for_tx_hang(ring) \ - clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state) #define ring_is_16byte_desc_enabled(ring) \ test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) #define set_ring_16byte_desc_enabled(ring) \ @@ -264,10 +268,12 @@ struct i40e_ring { bool ring_active; /* is ring online or not */ bool arm_wb; /* do something to arm write back */ + u8 packet_stride; u16 flags; #define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) #define I40E_TXR_FLAGS_OUTER_UDP_CSUM BIT(1) +#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2) /* stats structs */ struct i40e_queue_stats stats; @@ -326,4 +332,20 @@ int i40e_xmit_descriptor_count(struct sk_buff *skb, struct i40e_ring *tx_ring); int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, struct i40e_ring *tx_ring, u32 *flags); #endif +void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector); +u32 i40e_get_tx_pending(struct i40e_ring *ring); + +/** + * i40e_get_head - Retrieve head from head writeback + * @tx_ring: tx ring to fetch head of + * + * Returns value of Tx ring head based on value stored + * in head write-back location + **/ +static inline u32 i40e_get_head(struct i40e_ring *tx_ring) +{ + void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; + + return le32_to_cpu(*(volatile __le32 *)head); +} #endif /* _I40E_TXRX_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 4842239ee777..9c4a4573c28f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -33,29 +33,7 @@ #include "i40e_adminq.h" #include "i40e_hmc.h" #include "i40e_lan_hmc.h" - -/* Device IDs */ -#define I40E_DEV_ID_SFP_XL710 0x1572 -#define I40E_DEV_ID_QEMU 0x1574 -#define I40E_DEV_ID_KX_A 0x157F -#define I40E_DEV_ID_KX_B 0x1580 -#define I40E_DEV_ID_KX_C 0x1581 -#define I40E_DEV_ID_QSFP_A 0x1583 -#define I40E_DEV_ID_QSFP_B 0x1584 -#define I40E_DEV_ID_QSFP_C 0x1585 -#define I40E_DEV_ID_10G_BASE_T 0x1586 -#define I40E_DEV_ID_20G_KR2 0x1587 -#define I40E_DEV_ID_VF 0x154C -#define I40E_DEV_ID_VF_HV 0x1571 -#define I40E_DEV_ID_SFP_X722 0x37D0 -#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 -#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 -#define I40E_DEV_ID_X722_VF 0x37CD -#define I40E_DEV_ID_X722_VF_HV 0x37D9 - -#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ - (d) == I40E_DEV_ID_QSFP_B || \ - (d) == I40E_DEV_ID_QSFP_C) +#include "i40e_devids.h" /* I40E_MASK is a macro used on 32 bit registers */ #define I40E_MASK(mask, shift) (mask << shift) @@ -158,14 +136,14 @@ enum i40e_set_fc_aq_failures { }; enum i40e_vsi_type { - I40E_VSI_MAIN = 0, - I40E_VSI_VMDQ1, - I40E_VSI_VMDQ2, - I40E_VSI_CTRL, - I40E_VSI_FCOE, - I40E_VSI_MIRROR, - I40E_VSI_SRIOV, - I40E_VSI_FDIR, + I40E_VSI_MAIN = 0, + I40E_VSI_VMDQ1 = 1, + I40E_VSI_VMDQ2 = 2, + I40E_VSI_CTRL = 3, + I40E_VSI_FCOE = 4, + I40E_VSI_MIRROR = 5, + I40E_VSI_SRIOV = 6, + I40E_VSI_FDIR = 7, I40E_VSI_TYPE_UNKNOWN }; @@ -189,16 +167,65 @@ struct i40e_link_status { bool crc_enable; u8 pacing; u8 requested_speeds; + u8 module_type[3]; + /* 1st byte: module identifier */ +#define I40E_MODULE_TYPE_SFP 0x03 +#define I40E_MODULE_TYPE_QSFP 0x0D + /* 2nd byte: ethernet compliance codes for 10/40G */ +#define I40E_MODULE_TYPE_40G_ACTIVE 0x01 +#define I40E_MODULE_TYPE_40G_LR4 0x02 +#define I40E_MODULE_TYPE_40G_SR4 0x04 +#define I40E_MODULE_TYPE_40G_CR4 0x08 +#define I40E_MODULE_TYPE_10G_BASE_SR 0x10 +#define I40E_MODULE_TYPE_10G_BASE_LR 0x20 +#define I40E_MODULE_TYPE_10G_BASE_LRM 0x40 +#define I40E_MODULE_TYPE_10G_BASE_ER 0x80 + /* 3rd byte: ethernet compliance codes for 1G */ +#define I40E_MODULE_TYPE_1000BASE_SX 0x01 +#define I40E_MODULE_TYPE_1000BASE_LX 0x02 +#define I40E_MODULE_TYPE_1000BASE_CX 0x04 +#define I40E_MODULE_TYPE_1000BASE_T 0x08 +}; + +enum i40e_aq_capabilities_phy_type { + I40E_CAP_PHY_TYPE_SGMII = BIT(I40E_PHY_TYPE_SGMII), + I40E_CAP_PHY_TYPE_1000BASE_KX = BIT(I40E_PHY_TYPE_1000BASE_KX), + I40E_CAP_PHY_TYPE_10GBASE_KX4 = BIT(I40E_PHY_TYPE_10GBASE_KX4), + I40E_CAP_PHY_TYPE_10GBASE_KR = BIT(I40E_PHY_TYPE_10GBASE_KR), + I40E_CAP_PHY_TYPE_40GBASE_KR4 = BIT(I40E_PHY_TYPE_40GBASE_KR4), + I40E_CAP_PHY_TYPE_XAUI = BIT(I40E_PHY_TYPE_XAUI), + I40E_CAP_PHY_TYPE_XFI = BIT(I40E_PHY_TYPE_XFI), + I40E_CAP_PHY_TYPE_SFI = BIT(I40E_PHY_TYPE_SFI), + I40E_CAP_PHY_TYPE_XLAUI = BIT(I40E_PHY_TYPE_XLAUI), + I40E_CAP_PHY_TYPE_XLPPI = BIT(I40E_PHY_TYPE_XLPPI), + I40E_CAP_PHY_TYPE_40GBASE_CR4_CU = BIT(I40E_PHY_TYPE_40GBASE_CR4_CU), + I40E_CAP_PHY_TYPE_10GBASE_CR1_CU = BIT(I40E_PHY_TYPE_10GBASE_CR1_CU), + I40E_CAP_PHY_TYPE_10GBASE_AOC = BIT(I40E_PHY_TYPE_10GBASE_AOC), + I40E_CAP_PHY_TYPE_40GBASE_AOC = BIT(I40E_PHY_TYPE_40GBASE_AOC), + I40E_CAP_PHY_TYPE_100BASE_TX = BIT(I40E_PHY_TYPE_100BASE_TX), + I40E_CAP_PHY_TYPE_1000BASE_T = BIT(I40E_PHY_TYPE_1000BASE_T), + I40E_CAP_PHY_TYPE_10GBASE_T = BIT(I40E_PHY_TYPE_10GBASE_T), + I40E_CAP_PHY_TYPE_10GBASE_SR = BIT(I40E_PHY_TYPE_10GBASE_SR), + I40E_CAP_PHY_TYPE_10GBASE_LR = BIT(I40E_PHY_TYPE_10GBASE_LR), + I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU = BIT(I40E_PHY_TYPE_10GBASE_SFPP_CU), + I40E_CAP_PHY_TYPE_10GBASE_CR1 = BIT(I40E_PHY_TYPE_10GBASE_CR1), + I40E_CAP_PHY_TYPE_40GBASE_CR4 = BIT(I40E_PHY_TYPE_40GBASE_CR4), + I40E_CAP_PHY_TYPE_40GBASE_SR4 = BIT(I40E_PHY_TYPE_40GBASE_SR4), + I40E_CAP_PHY_TYPE_40GBASE_LR4 = BIT(I40E_PHY_TYPE_40GBASE_LR4), + I40E_CAP_PHY_TYPE_1000BASE_SX = BIT(I40E_PHY_TYPE_1000BASE_SX), + I40E_CAP_PHY_TYPE_1000BASE_LX = BIT(I40E_PHY_TYPE_1000BASE_LX), + I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL = + BIT(I40E_PHY_TYPE_1000BASE_T_OPTICAL), + I40E_CAP_PHY_TYPE_20GBASE_KR2 = BIT(I40E_PHY_TYPE_20GBASE_KR2) }; struct i40e_phy_info { struct i40e_link_status link_info; struct i40e_link_status link_info_old; - u32 autoneg_advertised; - u32 phy_id; - u32 module_type; bool get_link_info; enum i40e_media_type media_type; + /* all the phy types the NVM is capable of */ + enum i40e_aq_capabilities_phy_type phy_types; }; #define I40E_HW_CAP_MAX_GPIO 30 @@ -287,6 +314,7 @@ struct i40e_nvm_info { bool blank_nvm_mode; /* is NVM empty (no FW present)*/ u16 version; /* NVM package version */ u32 eetrack; /* NVM data version */ + u32 oem_ver; /* OEM version info */ }; /* definitions used in NVM update support */ @@ -305,12 +333,17 @@ enum i40e_nvmupd_cmd { I40E_NVMUPD_CSUM_CON, I40E_NVMUPD_CSUM_SA, I40E_NVMUPD_CSUM_LCB, + I40E_NVMUPD_STATUS, + I40E_NVMUPD_EXEC_AQ, + I40E_NVMUPD_GET_AQ_RESULT, }; enum i40e_nvmupd_state { I40E_NVMUPD_STATE_INIT, I40E_NVMUPD_STATE_READING, - I40E_NVMUPD_STATE_WRITING + I40E_NVMUPD_STATE_WRITING, + I40E_NVMUPD_STATE_INIT_WAIT, + I40E_NVMUPD_STATE_WRITE_WAIT, }; /* nvm_access definition and its masks/shifts need to be accessible to @@ -329,6 +362,7 @@ enum i40e_nvmupd_state { #define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) #define I40E_NVM_ERA 0x4 #define I40E_NVM_CSUM 0x8 +#define I40E_NVM_EXEC 0xf #define I40E_NVM_ADAPT_SHIFT 16 #define I40E_NVM_ADAPT_MASK (0xffff << I40E_NVM_ADAPT_SHIFT) @@ -409,6 +443,8 @@ struct i40e_fc_info { #define I40E_APP_PROTOID_FIP 0x8914 #define I40E_APP_SEL_ETHTYPE 0x1 #define I40E_APP_SEL_TCPIP 0x2 +#define I40E_CEE_APP_SEL_ETHTYPE 0x0 +#define I40E_CEE_APP_SEL_TCPIP 0x1 /* CEE or IEEE 802.1Qaz ETS Configuration data */ struct i40e_dcb_ets_config { @@ -439,6 +475,8 @@ struct i40e_dcbx_config { u8 dcbx_mode; #define I40E_DCBX_MODE_CEE 0x1 #define I40E_DCBX_MODE_IEEE 0x2 + u8 app_mode; +#define I40E_DCBX_APPS_NON_WILLING 0x1 u32 numapps; u32 tlv_status; /* CEE mode TLV status */ struct i40e_dcb_ets_config etscfg; @@ -492,6 +530,8 @@ struct i40e_hw { /* state of nvm update process */ enum i40e_nvmupd_state nvmupd_state; + struct i40e_aq_desc nvm_wb_desc; + struct i40e_virt_mem nvm_buff; /* HMC info */ struct i40e_hmc_info hmc; /* HMC info struct */ @@ -500,8 +540,9 @@ struct i40e_hw { u16 dcbx_status; /* DCBX info */ - struct i40e_dcbx_config local_dcbx_config; - struct i40e_dcbx_config remote_dcbx_config; + struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */ + struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ + struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ /* debug mask */ u32 debug_mask; @@ -1193,6 +1234,8 @@ struct i40e_hw_port_stats { #define I40E_SR_EMP_MODULE_PTR 0x0F #define I40E_SR_PBA_FLAGS 0x15 #define I40E_SR_PBA_BLOCK_PTR 0x16 +#define I40E_SR_BOOT_CONFIG_PTR 0x17 +#define I40E_NVM_OEM_VER_OFF 0x83 #define I40E_SR_NVM_DEV_STARTER_VERSION 0x18 #define I40E_SR_NVM_WAKE_ON_LAN 0x19 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27 diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h index 0f8d4156f8b1..ae879826084b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h @@ -81,7 +81,6 @@ enum i40e_virtchnl_ops { I40E_VIRTCHNL_OP_GET_STATS = 15, I40E_VIRTCHNL_OP_FCOE = 16, I40E_VIRTCHNL_OP_EVENT = 17, - I40E_VIRTCHNL_OP_CONFIG_RSS = 18, }; /* Virtual channel message descriptor. This overlays the admin queue @@ -151,6 +150,7 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_FCOE 0x00000004 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ 0x00000008 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG 0x00000010 +#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index d99c116032f3..a35a204b4eeb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -536,6 +536,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) } if (type == I40E_VSI_SRIOV) { u8 brdcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + vf->lan_vsi_idx = vsi->idx; vf->lan_vsi_id = vsi->id; /* If the port VLAN has been configured and then the @@ -561,7 +562,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) } /* program mac filter */ - ret = i40e_sync_vsi_filters(vsi); + ret = i40e_sync_vsi_filters(vsi, false); if (ret) dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); @@ -605,6 +606,7 @@ static void i40e_enable_vf_mappings(struct i40e_vf *vf) /* map PF queues to VF queues */ for (j = 0; j < pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; j++) { u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j); + reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK); wr32(hw, I40E_VPLAN_QTABLE(total_queue_pairs, vf->vf_id), reg); total_queue_pairs++; @@ -701,6 +703,7 @@ static void i40e_free_vf_res(struct i40e_vf *vf) */ vf->num_queue_pairs = 0; vf->vf_states = 0; + clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); } /** @@ -839,11 +842,11 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) complete_reset: /* reallocate VF resources to reset the VSI state */ i40e_free_vf_res(vf); - i40e_alloc_vf_res(vf); - i40e_enable_vf_mappings(vf); - set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); - clear_bit(I40E_VF_STAT_DISABLED, &vf->vf_states); - + if (!i40e_alloc_vf_res(vf)) { + i40e_enable_vf_mappings(vf); + set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); + clear_bit(I40E_VF_STAT_DISABLED, &vf->vf_states); + } /* tell the VF the reset is done */ wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE); i40e_flush(hw); @@ -872,6 +875,11 @@ void i40e_free_vfs(struct i40e_pf *pf) i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx], false); + for (i = 0; i < pf->num_alloc_vfs; i++) + if (test_bit(I40E_VF_STAT_INIT, &pf->vf[i].vf_states)) + i40e_vsi_control_rings(pf->vsi[pf->vf[i].lan_vsi_idx], + false); + /* Disable IOV before freeing resources. This lets any VF drivers * running in the host get themselves cleaned up before we yank * the carpet out from underneath their feet. @@ -957,8 +965,6 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs) /* VF resources get allocated during reset */ i40e_reset_vf(&vfs[i], false); - /* enable VF vplan_qtable mappings */ - i40e_enable_vf_mappings(&vfs[i]); } pf->num_alloc_vfs = num_alloc_vfs; @@ -986,24 +992,26 @@ static int i40e_pci_sriov_enable(struct pci_dev *pdev, int num_vfs) int pre_existing_vfs = pci_num_vf(pdev); int err = 0; - if (pf->state & __I40E_TESTING) { + if (test_bit(__I40E_TESTING, &pf->state)) { dev_warn(&pdev->dev, "Cannot enable SR-IOV virtual functions while the device is undergoing diagnostic testing\n"); err = -EPERM; goto err_out; } - dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); if (pre_existing_vfs && pre_existing_vfs != num_vfs) i40e_free_vfs(pf); else if (pre_existing_vfs && pre_existing_vfs == num_vfs) goto out; if (num_vfs > pf->num_req_vfs) { + dev_warn(&pdev->dev, "Unable to enable %d VFs. Limited to %d VFs due to device resource constraints.\n", + num_vfs, pf->num_req_vfs); err = -EPERM; goto err_out; } + dev_info(&pdev->dev, "Allocating %d VFs.\n", num_vfs); err = i40e_alloc_vfs(pf, num_vfs); if (err) { dev_warn(&pdev->dev, "Failed to enable SR-IOV: %d\n", err); @@ -1094,6 +1102,8 @@ static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode, } } else { vf->num_valid_msgs++; + /* reset the invalid counter, if a valid message is received. */ + vf->num_invalid_msgs = 0; } aq_ret = i40e_aq_send_msg_to_vf(hw, abs_vf_id, v_opcode, v_retval, @@ -1195,16 +1205,22 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) } else { vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG; } + + if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING) + vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING; + vfres->num_vsis = num_vsis; vfres->num_queue_pairs = vf->num_queue_pairs; vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf; if (vf->lan_vsi_idx) { vfres->vsi_res[i].vsi_id = vf->lan_vsi_id; vfres->vsi_res[i].vsi_type = I40E_VSI_SRIOV; - vfres->vsi_res[i].num_queue_pairs = - pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; - memcpy(vfres->vsi_res[i].default_mac_addr, - vf->default_lan_addr.addr, ETH_ALEN); + vfres->vsi_res[i].num_queue_pairs = vsi->alloc_queue_pairs; + /* VFs only use TC 0 */ + vfres->vsi_res[i].qset_handle + = le16_to_cpu(vsi->info.qs_handle[0]); + ether_addr_copy(vfres->vsi_res[i].default_mac_addr, + vf->default_lan_addr.addr); i++; } set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); @@ -1605,7 +1621,7 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) } /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi)) + if (i40e_sync_vsi_filters(vsi, false)) dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n"); error_param: @@ -1656,7 +1672,7 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) I40E_VLAN_ANY, true, false); /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi)) + if (i40e_sync_vsi_filters(vsi, false)) dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n"); error_param: @@ -1708,6 +1724,7 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) for (i = 0; i < vfl->num_elements; i++) { /* add new VLAN filter */ int ret = i40e_vsi_add_vlan(vsi, vfl->vlan_id[i]); + if (ret) dev_err(&pf->pdev->dev, "Unable to add VF vlan filter %d, error %d\n", @@ -1759,6 +1776,7 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) for (i = 0; i < vfl->num_elements; i++) { int ret = i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]); + if (ret) dev_err(&pf->pdev->dev, "Unable to delete VF vlan filter %d, error %d\n", @@ -1870,7 +1888,6 @@ static int i40e_vc_validate_vf_msg(struct i40e_vf *vf, u32 v_opcode, case I40E_VIRTCHNL_OP_UNKNOWN: default: return -EPERM; - break; } /* few more checks */ if ((valid_len != msglen) || (err_msg_format)) { @@ -2062,7 +2079,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id); /* program mac filter */ - if (i40e_sync_vsi_filters(vsi)) { + if (i40e_sync_vsi_filters(vsi, false)) { dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); ret = -EIO; goto error_param; @@ -2089,6 +2106,7 @@ error_param: int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos) { + u16 vlanprio = vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT); struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; struct i40e_vsi *vsi; @@ -2116,8 +2134,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, goto error_pvid; } - if (le16_to_cpu(vsi->info.pvid) == - (vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT))) + if (le16_to_cpu(vsi->info.pvid) == vlanprio) /* duplicate request, so just return success */ goto error_pvid; @@ -2141,7 +2158,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, * MAC addresses deleted. */ if ((!(vlan_id || qos) || - (vlan_id | qos) != le16_to_cpu(vsi->info.pvid)) && + vlanprio != le16_to_cpu(vsi->info.pvid)) && vsi->info.pvid) ret = i40e_vsi_add_vlan(vsi, I40E_VLAN_ANY); @@ -2156,8 +2173,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, } } if (vlan_id || qos) - ret = i40e_vsi_add_pvid(vsi, - vlan_id | (qos << I40E_VLAN_PRIORITY_SHIFT)); + ret = i40e_vsi_add_pvid(vsi, vlanprio); else i40e_vsi_remove_pvid(vsi); @@ -2310,7 +2326,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, ivi->vf = vf_id; - memcpy(&ivi->mac, vf->default_lan_addr.addr, ETH_ALEN); + ether_addr_copy(ivi->mac, vf->default_lan_addr.addr); ivi->max_tx_rate = vf->tx_rate; ivi->min_tx_rate = 0; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 736f6f08b4f2..da44995def42 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -29,8 +29,6 @@ #include "i40e.h" -#define I40E_MAX_MACVLAN_FILTERS 256 -#define I40E_MAX_VLAN_FILTERS 256 #define I40E_MAX_VLANID 4095 #define I40E_VIRTCHNL_SUPPORTED_QTYPES 2 @@ -98,7 +96,8 @@ struct i40e_vf { u8 num_queue_pairs; /* num of qps assigned to VF vsis */ u64 num_mdd_events; /* num of mdd events detected */ - u64 num_invalid_msgs; /* num of malformed or invalid msgs detected */ + /* num of continuous malformed or invalid msgs detected */ + u64 num_invalid_msgs; u64 num_valid_msgs; /* num of valid msgs detected */ unsigned long vf_caps; /* vf's adv. capabilities */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index a23ebfd5cd25..fd123ca60761 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -469,8 +469,12 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.asq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_asq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.asq.head, 0); @@ -479,16 +483,13 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw) wr32(hw, hw->aq.asq.bal, 0); wr32(hw, hw->aq.asq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.asq_mutex); - hw->aq.asq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_asq_bufs(hw); +shutdown_asq_out: mutex_unlock(&hw->aq.asq_mutex); - return ret_code; } @@ -502,8 +503,12 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) { i40e_status ret_code = 0; - if (hw->aq.arq.count == 0) - return I40E_ERR_NOT_READY; + mutex_lock(&hw->aq.arq_mutex); + + if (hw->aq.arq.count == 0) { + ret_code = I40E_ERR_NOT_READY; + goto shutdown_arq_out; + } /* Stop firmware AdminQ processing */ wr32(hw, hw->aq.arq.head, 0); @@ -512,16 +517,13 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw) wr32(hw, hw->aq.arq.bal, 0); wr32(hw, hw->aq.arq.bah, 0); - /* make sure lock is available */ - mutex_lock(&hw->aq.arq_mutex); - hw->aq.arq.count = 0; /* to indicate uninitialized queue */ /* free ring buffers */ i40e_free_arq_bufs(hw); +shutdown_arq_out: mutex_unlock(&hw->aq.arq_mutex); - return ret_code; } @@ -596,6 +598,9 @@ i40e_status i40evf_shutdown_adminq(struct i40e_hw *hw) /* destroy the locks */ + if (hw->nvm_buff.va) + i40e_free_virt_mem(hw, &hw->nvm_buff); + return ret_code; } @@ -617,8 +622,7 @@ static u16 i40e_clean_asq(struct i40e_hw *hw) details = I40E_ADMINQ_DETAILS(*asq, ntc); while (rd32(hw, hw->aq.asq.head) != ntc) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "%s: ntc %d head %d.\n", __func__, ntc, - rd32(hw, hw->aq.asq.head)); + "ntc %d head %d.\n", ntc, rd32(hw, hw->aq.asq.head)); if (details->callback) { I40E_ADMINQ_CALLBACK cb_func = @@ -682,19 +686,23 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, u16 retval = 0; u32 val = 0; - val = rd32(hw, hw->aq.asq.head); - if (val >= hw->aq.num_asq_entries) { + mutex_lock(&hw->aq.asq_mutex); + + if (hw->aq.asq.count == 0) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: head overrun at %d\n", val); + "AQTX: Admin queue not initialized.\n"); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } - if (hw->aq.asq.count == 0) { + hw->aq.asq_last_status = I40E_AQ_RC_OK; + + val = rd32(hw, hw->aq.asq.head); + if (val >= hw->aq.num_asq_entries) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, - "AQTX: Admin queue not initialized.\n"); + "AQTX: head overrun at %d\n", val); status = I40E_ERR_QUEUE_EMPTY; - goto asq_send_command_exit; + goto asq_send_command_error; } details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use); @@ -719,8 +727,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, desc->flags &= ~cpu_to_le16(details->flags_dis); desc->flags |= cpu_to_le16(details->flags_ena); - mutex_lock(&hw->aq.asq_mutex); - if (buff_size > hw->aq.asq_buf_size) { i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, @@ -830,6 +836,10 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, i40evf_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff, buff_size); + /* save writeback aq if requested */ + if (details->wb_desc) + *details->wb_desc = *desc_on_ring; + /* update the error if time out occurred */ if ((!cmd_completed) && (!details->async && !details->postpone)) { @@ -841,7 +851,6 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, asq_send_command_error: mutex_unlock(&hw->aq.asq_mutex); -asq_send_command_exit: return status; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h index ef43d68f67b3..a3eae5d9a2bd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h @@ -69,6 +69,7 @@ struct i40e_asq_cmd_details { u16 flags_dis; bool async; bool postpone; + struct i40e_aq_desc *wb_desc; }; #define I40E_ADMINQ_DETAILS(R, i) \ @@ -108,9 +109,10 @@ struct i40e_adminq_info { /** * i40e_aq_rc_to_posix - convert errors to user-land codes - * aq_rc: AdminQ error code to convert + * aq_ret: AdminQ handler error code can override aq_rc + * aq_rc: AdminQ firmware error code to convert **/ -static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) +static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) { int aq_to_posix[] = { 0, /* I40E_AQ_RC_OK */ @@ -142,8 +144,9 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT) return -EAGAIN; - if (aq_rc >= ARRAY_SIZE(aq_to_posix)) + if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0])))) return -ERANGE; + return aq_to_posix[aq_rc]; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index c8022092d369..fcb9ef34cc7a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -1719,11 +1719,13 @@ struct i40e_aqc_get_link_status { u8 phy_type; /* i40e_aq_phy_type */ u8 link_speed; /* i40e_aq_link_speed */ u8 link_info; -#define I40E_AQ_LINK_UP 0x01 +#define I40E_AQ_LINK_UP 0x01 /* obsolete */ +#define I40E_AQ_LINK_UP_FUNCTION 0x01 #define I40E_AQ_LINK_FAULT 0x02 #define I40E_AQ_LINK_FAULT_TX 0x04 #define I40E_AQ_LINK_FAULT_RX 0x08 #define I40E_AQ_LINK_FAULT_REMOTE 0x10 +#define I40E_AQ_LINK_UP_PORT 0x20 #define I40E_AQ_MEDIA_AVAILABLE 0x40 #define I40E_AQ_SIGNAL_DETECT 0x80 u8 an_info; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index d45d0ae6bd3b..7435fb396dcd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -51,7 +51,9 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: case I40E_DEV_ID_10G_BASE_T: + case I40E_DEV_ID_10G_BASE_T4: case I40E_DEV_ID_20G_KR2: + case I40E_DEV_ID_20G_KR2_A: hw->mac.type = I40E_MAC_XL710; break; case I40E_DEV_ID_SFP_X722: @@ -85,7 +87,7 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) * @hw: pointer to the HW structure * @aq_err: the AQ error code to convert **/ -char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) +const char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) { switch (aq_err) { case I40E_AQ_RC_OK: @@ -145,7 +147,7 @@ char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err) * @hw: pointer to the HW structure * @stat_err: the status error code to convert **/ -char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err) +const char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err) { switch (stat_err) { case 0: @@ -990,10 +992,10 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw, I40E_VIRTCHNL_VF_OFFLOAD_FCOE) ? 1 : 0; for (i = 0; i < msg->num_vsis; i++) { if (vsi_res->vsi_type == I40E_VSI_SRIOV) { - memcpy(hw->mac.perm_addr, vsi_res->default_mac_addr, - ETH_ALEN); - memcpy(hw->mac.addr, vsi_res->default_mac_addr, - ETH_ALEN); + ether_addr_copy(hw->mac.perm_addr, + vsi_res->default_mac_addr); + ether_addr_copy(hw->mac.addr, + vsi_res->default_mac_addr); } vsi_res++; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h new file mode 100644 index 000000000000..e6a39c9862e8 --- /dev/null +++ b/drivers/net/ethernet/intel/i40evf/i40e_devids.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * + * Intel Ethernet Controller XL710 Family Linux Virtual Function Driver + * Copyright(c) 2013 - 2015 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Contact Information: + * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + ******************************************************************************/ + +#ifndef _I40E_DEVIDS_H_ +#define _I40E_DEVIDS_H_ + +/* Device IDs */ +#define I40E_DEV_ID_SFP_XL710 0x1572 +#define I40E_DEV_ID_QEMU 0x1574 +#define I40E_DEV_ID_KX_A 0x157F +#define I40E_DEV_ID_KX_B 0x1580 +#define I40E_DEV_ID_KX_C 0x1581 +#define I40E_DEV_ID_QSFP_A 0x1583 +#define I40E_DEV_ID_QSFP_B 0x1584 +#define I40E_DEV_ID_QSFP_C 0x1585 +#define I40E_DEV_ID_10G_BASE_T 0x1586 +#define I40E_DEV_ID_20G_KR2 0x1587 +#define I40E_DEV_ID_20G_KR2_A 0x1588 +#define I40E_DEV_ID_10G_BASE_T4 0x1589 +#define I40E_DEV_ID_VF 0x154C +#define I40E_DEV_ID_VF_HV 0x1571 +#define I40E_DEV_ID_SFP_X722 0x37D0 +#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 +#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 +#define I40E_DEV_ID_X722_VF 0x37CD +#define I40E_DEV_ID_X722_VF_HV 0x37D9 + +#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ + (d) == I40E_DEV_ID_QSFP_B || \ + (d) == I40E_DEV_ID_QSFP_C) + +#endif /* _I40E_DEVIDS_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h index 55ae4b0f8192..8ed0edfbc125 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h @@ -60,8 +60,8 @@ void i40e_idle_aq(struct i40e_hw *hw); void i40evf_resume_aq(struct i40e_hw *hw); bool i40evf_check_asq_alive(struct i40e_hw *hw); i40e_status i40evf_aq_queue_shutdown(struct i40e_hw *hw, bool unloading); -char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); -char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err); +const char *i40evf_aq_str(struct i40e_hw *hw, enum i40e_admin_queue_err aq_err); +const char *i40evf_stat_str(struct i40e_hw *hw, i40e_status stat_err); i40e_status i40evf_aq_get_rss_lut(struct i40e_hw *hw, u16 seid, bool pf_lut, u8 *lut, u16 lut_size); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 7e91d825c760..97493a4164eb 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -140,65 +140,6 @@ static inline u32 i40e_get_head(struct i40e_ring *tx_ring) return le32_to_cpu(*(volatile __le32 *)head); } -/** - * i40e_get_tx_pending - how many tx descriptors not processed - * @tx_ring: the ring of descriptors - * - * Since there is no access to the ring head register - * in XL710, we need to use our local copies - **/ -static u32 i40e_get_tx_pending(struct i40e_ring *ring) -{ - u32 head, tail; - - head = i40e_get_head(ring); - tail = readl(ring->tail); - - if (head != tail) - return (head < tail) ? - tail - head : (tail + ring->count - head); - - return 0; -} - -/** - * i40e_check_tx_hang - Is there a hang in the Tx queue - * @tx_ring: the ring of descriptors - **/ -static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) -{ - u32 tx_done = tx_ring->stats.packets; - u32 tx_done_old = tx_ring->tx_stats.tx_done_old; - u32 tx_pending = i40e_get_tx_pending(tx_ring); - bool ret = false; - - clear_check_for_tx_hang(tx_ring); - - /* Check for a hung queue, but be thorough. This verifies - * that a transmit has been completed since the previous - * check AND there is at least one packet pending. The - * ARMED bit is set to indicate a potential hang. The - * bit is cleared if a pause frame is received to remove - * false hang detection due to PFC or 802.3x frames. By - * requiring this to fail twice we avoid races with - * PFC clearing the ARMED bit and conditions where we - * run the check_tx_hang logic with a transmit completion - * pending but without time to complete it yet. - */ - if ((tx_done_old == tx_done) && tx_pending) { - /* make sure it is true for two checks in a row */ - ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED, - &tx_ring->state); - } else if (tx_done_old == tx_done && - (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) { - /* update completed stats and disarm the hang check */ - tx_ring->tx_stats.tx_done_old = tx_done; - clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); - } - - return ret; -} - #define WB_STRIDE 0x3 /** @@ -304,36 +245,15 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) tx_ring->q_vector->tx.total_bytes += total_bytes; tx_ring->q_vector->tx.total_packets += total_packets; + /* check to see if there are any non-cache aligned descriptors + * waiting to be written back, and kick the hardware to force + * them to be written back in case of napi polling + */ if (budget && !((i & WB_STRIDE) == WB_STRIDE) && !test_bit(__I40E_DOWN, &tx_ring->vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; - else - tx_ring->arm_wb = false; - - if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) { - /* schedule immediate reset if we believe we hung */ - dev_info(tx_ring->dev, "Detected Tx Unit Hang\n" - " VSI <%d>\n" - " Tx Queue <%d>\n" - " next_to_use <%x>\n" - " next_to_clean <%x>\n", - tx_ring->vsi->seid, - tx_ring->queue_index, - tx_ring->next_to_use, i); - - netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); - - dev_info(tx_ring->dev, - "tx hang detected on queue %d, resetting adapter\n", - tx_ring->queue_index); - - tx_ring->netdev->netdev_ops->ndo_tx_timeout(tx_ring->netdev); - - /* the adapter is about to reset, no point in enabling stuff */ - return true; - } netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev, tx_ring->queue_index), @@ -355,16 +275,16 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) } } - return budget > 0; + return !!budget; } /** - * i40e_force_wb -Arm hardware to do a wb on noncache aligned descriptors + * i40evf_force_wb -Arm hardware to do a wb on noncache aligned descriptors * @vsi: the VSI we care about * @q_vector: the vector on which to force writeback * **/ -static void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) +static void i40evf_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) { u16 flags = q_vector->tx.ring[0].flags; @@ -822,16 +742,11 @@ static void i40e_receive_skb(struct i40e_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag) { struct i40e_q_vector *q_vector = rx_ring->q_vector; - struct i40e_vsi *vsi = rx_ring->vsi; - u64 flags = vsi->back->flags; if (vlan_tag & VLAN_VID_MASK) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); - if (flags & I40E_FLAG_IN_NETPOLL) - netif_rx(skb); - else - napi_gro_receive(&q_vector->napi, skb); + napi_gro_receive(&q_vector->napi, skb); } /** @@ -997,7 +912,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - const int current_node = numa_node_id(); + const int current_node = numa_mem_id(); struct i40e_vsi *vsi = rx_ring->vsi; u16 i = rx_ring->next_to_clean; union i40e_rx_desc *rx_desc; @@ -1067,6 +982,7 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) cleaned_count++; if (rx_hbo || rx_sph) { int len; + if (rx_hbo) len = I40E_RX_HDR_SIZE; else @@ -1240,9 +1156,6 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) /* ERR_MASK will only have valid bits if EOP set */ if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { dev_kfree_skb_any(skb); - /* TODO: shouldn't we increment a counter indicating the - * drop? - */ continue; } @@ -1353,7 +1266,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) bool clean_complete = true; bool arm_wb = false; int budget_per_ring; - int cleaned; + int work_done = 0; if (test_bit(__I40E_DOWN, &vsi->state)) { napi_complete(napi); @@ -1366,26 +1279,36 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); arm_wb |= ring->arm_wb; + ring->arm_wb = false; } + /* Handle case where we are called by netpoll with a budget of 0 */ + if (budget <= 0) + goto tx_only; + /* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early. */ budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { + int cleaned; + if (ring_is_ps_enabled(ring)) cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring); else cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); + + work_done += cleaned; /* if we didn't clean as many as budgeted, we must be done */ clean_complete &= (budget_per_ring != cleaned); } /* If work not completed, return budget and polling will return */ if (!clean_complete) { +tx_only: if (arm_wb) - i40e_force_wb(vsi, q_vector); + i40evf_force_wb(vsi, q_vector); return budget; } @@ -1393,7 +1316,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) q_vector->arm_wb_state = false; /* Work is done so exit the polling mode and re-enable the interrupt */ - napi_complete(napi); + napi_complete_done(napi, work_done); i40e_update_enable_itr(vsi, q_vector); return 0; } @@ -1437,6 +1360,7 @@ static inline int i40evf_tx_prepare_vlan_flags(struct sk_buff *skb, /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { struct vlan_hdr *vhdr, _vhdr; + vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(_vhdr), &_vhdr); if (!vhdr) return -EINVAL; @@ -1979,6 +1903,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u32 td_cmd = 0; u8 hdr_len = 0; int tso; + if (0 == i40evf_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; @@ -2006,10 +1931,11 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, else if (tso) tx_flags |= I40E_TX_FLAGS_TSO; - if (i40e_chk_linearize(skb, tx_flags)) + if (i40e_chk_linearize(skb, tx_flags)) { if (skb_linearize(skb)) goto out_drop; - + tx_ring->tx_stats.tx_linearize++; + } skb_tx_timestamp(skb); /* always enable CRC insertion offload */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 9a30f5d8c089..c4f5a4eb2907 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -35,6 +35,7 @@ #define I40E_ITR_20K 0x0019 #define I40E_ITR_8K 0x003E #define I40E_ITR_4K 0x007A +#define I40E_MAX_INTRL 0x3B /* reg uses 4 usec resolution */ #define I40E_ITR_RX_DEF I40E_ITR_8K #define I40E_ITR_TX_DEF I40E_ITR_4K #define I40E_ITR_DYNAMIC 0x8000 /* use top bit as a flag */ @@ -44,6 +45,15 @@ #define ITR_TO_REG(setting) ((setting & ~I40E_ITR_DYNAMIC) >> 1) #define ITR_IS_DYNAMIC(setting) (!!(setting & I40E_ITR_DYNAMIC)) #define ITR_REG_TO_USEC(itr_reg) (itr_reg << 1) +/* 0x40 is the enable bit for interrupt rate limiting, and must be set if + * the value of the rate limit is non-zero + */ +#define INTRL_ENA BIT(6) +#define INTRL_REG_TO_USEC(intrl) ((intrl & ~INTRL_ENA) << 2) +#define INTRL_USEC_TO_REG(set) ((set) ? ((set) >> 2) | INTRL_ENA : 0) +#define I40E_INTRL_8K 125 /* 8000 ints/sec */ +#define I40E_INTRL_62K 16 /* 62500 ints/sec */ +#define I40E_INTRL_83K 12 /* 83333 ints/sec */ #define I40E_QUEUE_END_OF_LIST 0x7FF @@ -164,6 +174,7 @@ struct i40e_tx_buffer { }; unsigned int bytecount; unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); DEFINE_DMA_UNMAP_LEN(len); u32 tx_flags; @@ -187,6 +198,7 @@ struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; u64 tx_done_old; + u64 tx_linearize; }; struct i40e_rx_queue_stats { @@ -198,8 +210,6 @@ struct i40e_rx_queue_stats { enum i40e_ring_state_t { __I40E_TX_FDIR_INIT_DONE, __I40E_TX_XPS_INIT_DONE, - __I40E_TX_DETECT_HANG, - __I40E_HANG_CHECK_ARMED, __I40E_RX_PS_ENABLED, __I40E_RX_16BYTE_DESC_ENABLED, }; @@ -210,12 +220,6 @@ enum i40e_ring_state_t { set_bit(__I40E_RX_PS_ENABLED, &(ring)->state) #define clear_ring_ps_enabled(ring) \ clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define check_for_tx_hang(ring) \ - test_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define set_check_for_tx_hang(ring) \ - set_bit(__I40E_TX_DETECT_HANG, &(ring)->state) -#define clear_check_for_tx_hang(ring) \ - clear_bit(__I40E_TX_DETECT_HANG, &(ring)->state) #define ring_is_16byte_desc_enabled(ring) \ test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) #define set_ring_16byte_desc_enabled(ring) \ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 24a2693869a1..85af3b48effc 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -33,29 +33,7 @@ #include "i40e_adminq.h" #include "i40e_hmc.h" #include "i40e_lan_hmc.h" - -/* Device IDs */ -#define I40E_DEV_ID_SFP_XL710 0x1572 -#define I40E_DEV_ID_QEMU 0x1574 -#define I40E_DEV_ID_KX_A 0x157F -#define I40E_DEV_ID_KX_B 0x1580 -#define I40E_DEV_ID_KX_C 0x1581 -#define I40E_DEV_ID_QSFP_A 0x1583 -#define I40E_DEV_ID_QSFP_B 0x1584 -#define I40E_DEV_ID_QSFP_C 0x1585 -#define I40E_DEV_ID_10G_BASE_T 0x1586 -#define I40E_DEV_ID_20G_KR2 0x1587 -#define I40E_DEV_ID_VF 0x154C -#define I40E_DEV_ID_VF_HV 0x1571 -#define I40E_DEV_ID_SFP_X722 0x37D0 -#define I40E_DEV_ID_1G_BASE_T_X722 0x37D1 -#define I40E_DEV_ID_10G_BASE_T_X722 0x37D2 -#define I40E_DEV_ID_X722_VF 0x37CD -#define I40E_DEV_ID_X722_VF_HV 0x37D9 - -#define i40e_is_40G_device(d) ((d) == I40E_DEV_ID_QSFP_A || \ - (d) == I40E_DEV_ID_QSFP_B || \ - (d) == I40E_DEV_ID_QSFP_C) +#include "i40e_devids.h" /* I40E_MASK is a macro used on 32 bit registers */ #define I40E_MASK(mask, shift) (mask << shift) @@ -158,14 +136,14 @@ enum i40e_set_fc_aq_failures { }; enum i40e_vsi_type { - I40E_VSI_MAIN = 0, - I40E_VSI_VMDQ1, - I40E_VSI_VMDQ2, - I40E_VSI_CTRL, - I40E_VSI_FCOE, - I40E_VSI_MIRROR, - I40E_VSI_SRIOV, - I40E_VSI_FDIR, + I40E_VSI_MAIN = 0, + I40E_VSI_VMDQ1 = 1, + I40E_VSI_VMDQ2 = 2, + I40E_VSI_CTRL = 3, + I40E_VSI_FCOE = 4, + I40E_VSI_MIRROR = 5, + I40E_VSI_SRIOV = 6, + I40E_VSI_FDIR = 7, I40E_VSI_TYPE_UNKNOWN }; @@ -189,16 +167,65 @@ struct i40e_link_status { bool crc_enable; u8 pacing; u8 requested_speeds; + u8 module_type[3]; + /* 1st byte: module identifier */ +#define I40E_MODULE_TYPE_SFP 0x03 +#define I40E_MODULE_TYPE_QSFP 0x0D + /* 2nd byte: ethernet compliance codes for 10/40G */ +#define I40E_MODULE_TYPE_40G_ACTIVE 0x01 +#define I40E_MODULE_TYPE_40G_LR4 0x02 +#define I40E_MODULE_TYPE_40G_SR4 0x04 +#define I40E_MODULE_TYPE_40G_CR4 0x08 +#define I40E_MODULE_TYPE_10G_BASE_SR 0x10 +#define I40E_MODULE_TYPE_10G_BASE_LR 0x20 +#define I40E_MODULE_TYPE_10G_BASE_LRM 0x40 +#define I40E_MODULE_TYPE_10G_BASE_ER 0x80 + /* 3rd byte: ethernet compliance codes for 1G */ +#define I40E_MODULE_TYPE_1000BASE_SX 0x01 +#define I40E_MODULE_TYPE_1000BASE_LX 0x02 +#define I40E_MODULE_TYPE_1000BASE_CX 0x04 +#define I40E_MODULE_TYPE_1000BASE_T 0x08 +}; + +enum i40e_aq_capabilities_phy_type { + I40E_CAP_PHY_TYPE_SGMII = BIT(I40E_PHY_TYPE_SGMII), + I40E_CAP_PHY_TYPE_1000BASE_KX = BIT(I40E_PHY_TYPE_1000BASE_KX), + I40E_CAP_PHY_TYPE_10GBASE_KX4 = BIT(I40E_PHY_TYPE_10GBASE_KX4), + I40E_CAP_PHY_TYPE_10GBASE_KR = BIT(I40E_PHY_TYPE_10GBASE_KR), + I40E_CAP_PHY_TYPE_40GBASE_KR4 = BIT(I40E_PHY_TYPE_40GBASE_KR4), + I40E_CAP_PHY_TYPE_XAUI = BIT(I40E_PHY_TYPE_XAUI), + I40E_CAP_PHY_TYPE_XFI = BIT(I40E_PHY_TYPE_XFI), + I40E_CAP_PHY_TYPE_SFI = BIT(I40E_PHY_TYPE_SFI), + I40E_CAP_PHY_TYPE_XLAUI = BIT(I40E_PHY_TYPE_XLAUI), + I40E_CAP_PHY_TYPE_XLPPI = BIT(I40E_PHY_TYPE_XLPPI), + I40E_CAP_PHY_TYPE_40GBASE_CR4_CU = BIT(I40E_PHY_TYPE_40GBASE_CR4_CU), + I40E_CAP_PHY_TYPE_10GBASE_CR1_CU = BIT(I40E_PHY_TYPE_10GBASE_CR1_CU), + I40E_CAP_PHY_TYPE_10GBASE_AOC = BIT(I40E_PHY_TYPE_10GBASE_AOC), + I40E_CAP_PHY_TYPE_40GBASE_AOC = BIT(I40E_PHY_TYPE_40GBASE_AOC), + I40E_CAP_PHY_TYPE_100BASE_TX = BIT(I40E_PHY_TYPE_100BASE_TX), + I40E_CAP_PHY_TYPE_1000BASE_T = BIT(I40E_PHY_TYPE_1000BASE_T), + I40E_CAP_PHY_TYPE_10GBASE_T = BIT(I40E_PHY_TYPE_10GBASE_T), + I40E_CAP_PHY_TYPE_10GBASE_SR = BIT(I40E_PHY_TYPE_10GBASE_SR), + I40E_CAP_PHY_TYPE_10GBASE_LR = BIT(I40E_PHY_TYPE_10GBASE_LR), + I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU = BIT(I40E_PHY_TYPE_10GBASE_SFPP_CU), + I40E_CAP_PHY_TYPE_10GBASE_CR1 = BIT(I40E_PHY_TYPE_10GBASE_CR1), + I40E_CAP_PHY_TYPE_40GBASE_CR4 = BIT(I40E_PHY_TYPE_40GBASE_CR4), + I40E_CAP_PHY_TYPE_40GBASE_SR4 = BIT(I40E_PHY_TYPE_40GBASE_SR4), + I40E_CAP_PHY_TYPE_40GBASE_LR4 = BIT(I40E_PHY_TYPE_40GBASE_LR4), + I40E_CAP_PHY_TYPE_1000BASE_SX = BIT(I40E_PHY_TYPE_1000BASE_SX), + I40E_CAP_PHY_TYPE_1000BASE_LX = BIT(I40E_PHY_TYPE_1000BASE_LX), + I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL = + BIT(I40E_PHY_TYPE_1000BASE_T_OPTICAL), + I40E_CAP_PHY_TYPE_20GBASE_KR2 = BIT(I40E_PHY_TYPE_20GBASE_KR2) }; struct i40e_phy_info { struct i40e_link_status link_info; struct i40e_link_status link_info_old; - u32 autoneg_advertised; - u32 phy_id; - u32 module_type; bool get_link_info; enum i40e_media_type media_type; + /* all the phy types the NVM is capable of */ + enum i40e_aq_capabilities_phy_type phy_types; }; #define I40E_HW_CAP_MAX_GPIO 30 @@ -286,6 +313,7 @@ struct i40e_nvm_info { bool blank_nvm_mode; /* is NVM empty (no FW present)*/ u16 version; /* NVM package version */ u32 eetrack; /* NVM data version */ + u32 oem_ver; /* OEM version info */ }; /* definitions used in NVM update support */ @@ -304,12 +332,17 @@ enum i40e_nvmupd_cmd { I40E_NVMUPD_CSUM_CON, I40E_NVMUPD_CSUM_SA, I40E_NVMUPD_CSUM_LCB, + I40E_NVMUPD_STATUS, + I40E_NVMUPD_EXEC_AQ, + I40E_NVMUPD_GET_AQ_RESULT, }; enum i40e_nvmupd_state { I40E_NVMUPD_STATE_INIT, I40E_NVMUPD_STATE_READING, - I40E_NVMUPD_STATE_WRITING + I40E_NVMUPD_STATE_WRITING, + I40E_NVMUPD_STATE_INIT_WAIT, + I40E_NVMUPD_STATE_WRITE_WAIT, }; /* nvm_access definition and its masks/shifts need to be accessible to @@ -328,6 +361,7 @@ enum i40e_nvmupd_state { #define I40E_NVM_SA (I40E_NVM_SNT | I40E_NVM_LCB) #define I40E_NVM_ERA 0x4 #define I40E_NVM_CSUM 0x8 +#define I40E_NVM_EXEC 0xf #define I40E_NVM_ADAPT_SHIFT 16 #define I40E_NVM_ADAPT_MASK (0xffff << I40E_NVM_ADAPT_SHIFT) @@ -486,6 +520,8 @@ struct i40e_hw { /* state of nvm update process */ enum i40e_nvmupd_state nvmupd_state; + struct i40e_aq_desc nvm_wb_desc; + struct i40e_virt_mem nvm_buff; /* HMC info */ struct i40e_hmc_info hmc; /* HMC info struct */ @@ -494,8 +530,9 @@ struct i40e_hw { u16 dcbx_status; /* DCBX info */ - struct i40e_dcbx_config local_dcbx_config; - struct i40e_dcbx_config remote_dcbx_config; + struct i40e_dcbx_config local_dcbx_config; /* Oper/Local Cfg */ + struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */ + struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */ /* debug mask */ u32 debug_mask; @@ -1162,6 +1199,7 @@ struct i40e_hw_port_stats { /* Checksum and Shadow RAM pointers */ #define I40E_SR_NVM_CONTROL_WORD 0x00 #define I40E_SR_EMP_MODULE_PTR 0x0F +#define I40E_NVM_OEM_VER_OFF 0x83 #define I40E_SR_NVM_DEV_STARTER_VERSION 0x18 #define I40E_SR_NVM_WAKE_ON_LAN 0x19 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index e6db20e8a395..9f7b279b9d9c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -81,7 +81,6 @@ enum i40e_virtchnl_ops { I40E_VIRTCHNL_OP_GET_STATS = 15, I40E_VIRTCHNL_OP_FCOE = 16, I40E_VIRTCHNL_OP_EVENT = 17, - I40E_VIRTCHNL_OP_CONFIG_RSS = 18, }; /* Virtual channel message descriptor. This overlays the admin queue @@ -151,6 +150,7 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_FCOE 0x00000004 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ 0x00000008 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG 0x00000010 +#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 3817cbbf45e6..22841c619f37 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -48,10 +48,6 @@ #define DEFAULT_DEBUG_LEVEL_SHIFT 3 #define PFX "i40evf: " -#define DPRINTK(nlevel, klevel, fmt, args...) \ - ((void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ - printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \ - __func__ , ## args))) /* dummy struct to make common code less painful */ struct i40e_vsi { @@ -70,6 +66,7 @@ struct i40e_vsi { */ u16 rx_itr_setting; u16 tx_itr_setting; + u16 qs_handle; }; /* How many Rx Buffers do we bundle into one write to the hardware ? */ @@ -90,7 +87,7 @@ struct i40e_vsi { #define I40EVF_MAX_RXBUFFER 16384 /* largest size for single descriptor */ #define I40EVF_MAX_AQ_BUF_SIZE 4096 #define I40EVF_AQ_LEN 32 -#define I40EVF_AQ_MAX_ERR 10 /* times to try before resetting AQ */ +#define I40EVF_AQ_MAX_ERR 20 /* times to try before resetting AQ */ #define MAXIMUM_ETHERNET_VLAN_SIZE (VLAN_ETH_FRAME_LEN + ETH_FCS_LEN) @@ -214,7 +211,6 @@ struct i40evf_adapter { #define I40EVF_FLAG_RX_1BUF_CAPABLE BIT(1) #define I40EVF_FLAG_RX_PS_CAPABLE BIT(2) #define I40EVF_FLAG_RX_PS_ENABLED BIT(3) -#define I40EVF_FLAG_IN_NETPOLL BIT(4) #define I40EVF_FLAG_IMIR_ENABLED BIT(5) #define I40EVF_FLAG_MQ_CAPABLE BIT(6) #define I40EVF_FLAG_NEED_LINK_UPDATE BIT(7) @@ -223,10 +219,10 @@ struct i40evf_adapter { #define I40EVF_FLAG_RESET_NEEDED BIT(10) #define I40EVF_FLAG_WB_ON_ITR_CAPABLE BIT(11) #define I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE BIT(12) +#define I40EVF_FLAG_ADDR_SET_BY_PF BIT(13) /* duplicates for common code */ #define I40E_FLAG_FDIR_ATR_ENABLED 0 #define I40E_FLAG_DCB_ENABLED 0 -#define I40E_FLAG_IN_NETPOLL I40EVF_FLAG_IN_NETPOLL #define I40E_FLAG_RX_CSUM_ENABLED I40EVF_FLAG_RX_CSUM_ENABLED #define I40E_FLAG_WB_ON_ITR_CAPABLE I40EVF_FLAG_WB_ON_ITR_CAPABLE #define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index e85849b9ff98..4c4340cc4f45 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.3.5" +#define DRV_VERSION "1.3.21" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; @@ -444,6 +444,29 @@ out: return err; } +#ifdef CONFIG_NET_POLL_CONTROLLER +/** + * i40evf_netpoll - A Polling 'interrupt' handler + * @netdev: network interface device structure + * + * This is used by netconsole to send skbs without having to re-enable + * interrupts. It's not called while the normal interrupt routine is executing. + **/ +static void i40evf_netpoll(struct net_device *netdev) +{ + struct i40evf_adapter *adapter = netdev_priv(netdev); + int q_vectors = adapter->num_msix_vectors - NONQ_VECS; + int i; + + /* if interface is down do nothing */ + if (test_bit(__I40E_DOWN, &adapter->vsi.state)) + return; + + for (i = 0; i < q_vectors; i++) + i40evf_msix_clean_rings(0, adapter->q_vector[i]); +} + +#endif /** * i40evf_request_traffic_irqs - Initialize MSI-X interrupts * @adapter: board private structure @@ -489,8 +512,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) q_vector); if (err) { dev_info(&adapter->pdev->dev, - "%s: request_irq failed, error: %d\n", - __func__, err); + "Request_irq failed, error: %d\n", err); goto free_queue_irqs; } /* assign the mask for this irq */ @@ -731,6 +753,8 @@ static int i40evf_vlan_rx_add_vid(struct net_device *netdev, { struct i40evf_adapter *adapter = netdev_priv(netdev); + if (!VLAN_ALLOWED(adapter)) + return -EIO; if (i40evf_add_vlan(adapter, vid) == NULL) return -ENOMEM; return 0; @@ -746,8 +770,11 @@ static int i40evf_vlan_rx_kill_vid(struct net_device *netdev, { struct i40evf_adapter *adapter = netdev_priv(netdev); - i40evf_del_vlan(adapter, vid); - return 0; + if (VLAN_ALLOWED(adapter)) { + i40evf_del_vlan(adapter, vid); + return 0; + } + return -EIO; } /** @@ -837,6 +864,15 @@ static int i40evf_set_mac(struct net_device *netdev, void *p) if (ether_addr_equal(netdev->dev_addr, addr->sa_data)) return 0; + if (adapter->flags & I40EVF_FLAG_ADDR_SET_BY_PF) + return -EPERM; + + f = i40evf_find_filter(adapter, hw->mac.addr); + if (f) { + f->remove = true; + adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; + } + f = i40evf_add_filter(adapter, addr->sa_data); if (f) { ether_addr_copy(hw->mac.addr, addr->sa_data); @@ -856,6 +892,7 @@ static void i40evf_set_rx_mode(struct net_device *netdev) struct i40evf_mac_filter *f, *ftmp; struct netdev_hw_addr *uca; struct netdev_hw_addr *mca; + struct netdev_hw_addr *ha; int count = 50; /* add addr if not already in the filter list */ @@ -877,29 +914,27 @@ static void i40evf_set_rx_mode(struct net_device *netdev) } /* remove filter if not in netdev list */ list_for_each_entry_safe(f, ftmp, &adapter->mac_filter_list, list) { - bool found = false; - - if (is_multicast_ether_addr(f->macaddr)) { - netdev_for_each_mc_addr(mca, netdev) { - if (ether_addr_equal(mca->addr, f->macaddr)) { - found = true; - break; - } - } - } else { - netdev_for_each_uc_addr(uca, netdev) { - if (ether_addr_equal(uca->addr, f->macaddr)) { - found = true; - break; - } - } - if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr)) - found = true; - } - if (!found) { - f->remove = true; - adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; - } + netdev_for_each_mc_addr(mca, netdev) + if (ether_addr_equal(mca->addr, f->macaddr)) + goto bottom_of_search_loop; + + netdev_for_each_uc_addr(uca, netdev) + if (ether_addr_equal(uca->addr, f->macaddr)) + goto bottom_of_search_loop; + + for_each_dev_addr(netdev, ha) + if (ether_addr_equal(ha->addr, f->macaddr)) + goto bottom_of_search_loop; + + if (ether_addr_equal(f->macaddr, adapter->hw.mac.addr)) + goto bottom_of_search_loop; + + /* f->macaddr wasn't found in uc, mc, or ha list so delete it */ + f->remove = true; + adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; + +bottom_of_search_loop: + continue; } clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); } @@ -1111,6 +1146,8 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter) tx_ring->netdev = adapter->netdev; tx_ring->dev = &adapter->pdev->dev; tx_ring->count = adapter->tx_desc_count; + if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR; adapter->tx_rings[i] = tx_ring; rx_ring = &tx_ring[1]; @@ -1165,7 +1202,7 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter) for (vector = 0; vector < v_budget; vector++) adapter->msix_entries[vector].entry = vector; - i40evf_acquire_msix_vectors(adapter, v_budget); + err = i40evf_acquire_msix_vectors(adapter, v_budget); out: adapter->netdev->real_num_tx_queues = pairs; @@ -1421,16 +1458,16 @@ static void i40evf_watchdog_task(struct work_struct *work) struct i40evf_adapter, watchdog_task); struct i40e_hw *hw = &adapter->hw; - uint32_t rstat_val; + u32 reg_val; if (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section)) goto restart_watchdog; if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if ((rstat_val == I40E_VFR_VFACTIVE) || - (rstat_val == I40E_VFR_COMPLETED)) { + reg_val = rd32(hw, I40E_VFGEN_RSTAT) & + I40E_VFGEN_RSTAT_VFR_STATE_MASK; + if ((reg_val == I40E_VFR_VFACTIVE) || + (reg_val == I40E_VFR_COMPLETED)) { /* A chance for redemption! */ dev_err(&adapter->pdev->dev, "Hardware came out of reset. Attempting reinit.\n"); adapter->state = __I40EVF_STARTUP; @@ -1455,11 +1492,8 @@ static void i40evf_watchdog_task(struct work_struct *work) goto watchdog_done; /* check for reset */ - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) && - (rstat_val != I40E_VFR_VFACTIVE) && - (rstat_val != I40E_VFR_COMPLETED)) { + reg_val = rd32(hw, I40E_VF_ARQLEN1) & I40E_VF_ARQLEN1_ARQENABLE_MASK; + if (!(adapter->flags & I40EVF_FLAG_RESET_PENDING) && !reg_val) { adapter->state = __I40EVF_RESETTING; adapter->flags |= I40EVF_FLAG_RESET_PENDING; dev_err(&adapter->pdev->dev, "Hardware reset detected\n"); @@ -1574,7 +1608,7 @@ static void i40evf_reset_task(struct work_struct *work) struct net_device *netdev = adapter->netdev; struct i40e_hw *hw = &adapter->hw; struct i40evf_mac_filter *f; - uint32_t rstat_val; + u32 reg_val; int i = 0, err; while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, @@ -1595,12 +1629,11 @@ static void i40evf_reset_task(struct work_struct *work) /* poll until we see the reset actually happen */ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if ((rstat_val != I40E_VFR_VFACTIVE) && - (rstat_val != I40E_VFR_COMPLETED)) + reg_val = rd32(hw, I40E_VF_ARQLEN1) & + I40E_VF_ARQLEN1_ARQENABLE_MASK; + if (!reg_val) break; - usleep_range(500, 1000); + usleep_range(5000, 10000); } if (i == I40EVF_RESET_WAIT_COUNT) { dev_info(&adapter->pdev->dev, "Never saw reset\n"); @@ -1609,21 +1642,21 @@ static void i40evf_reset_task(struct work_struct *work) /* wait until the reset is complete and the PF is responding to us */ for (i = 0; i < I40EVF_RESET_WAIT_COUNT; i++) { - rstat_val = rd32(hw, I40E_VFGEN_RSTAT) & - I40E_VFGEN_RSTAT_VFR_STATE_MASK; - if (rstat_val == I40E_VFR_VFACTIVE) + reg_val = rd32(hw, I40E_VFGEN_RSTAT) & + I40E_VFGEN_RSTAT_VFR_STATE_MASK; + if (reg_val == I40E_VFR_VFACTIVE) break; msleep(I40EVF_RESET_WAIT_MS); } /* extra wait to make sure minimum wait is met */ msleep(I40EVF_RESET_WAIT_MS); if (i == I40EVF_RESET_WAIT_COUNT) { - struct i40evf_mac_filter *f, *ftmp; + struct i40evf_mac_filter *ftmp; struct i40evf_vlan_filter *fv, *fvtmp; /* reset never finished */ dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n", - rstat_val); + reg_val); adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; if (netif_running(adapter->netdev)) { @@ -1853,8 +1886,7 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter) if (!err) continue; dev_err(&adapter->pdev->dev, - "%s: Allocation for Tx Queue %u failed\n", - __func__, i); + "Allocation for Tx Queue %u failed\n", i); break; } @@ -1881,8 +1913,7 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter) if (!err) continue; dev_err(&adapter->pdev->dev, - "%s: Allocation for Rx Queue %u failed\n", - __func__, i); + "Allocation for Rx Queue %u failed\n", i); break; } return err; @@ -2041,6 +2072,9 @@ static const struct net_device_ops i40evf_netdev_ops = { .ndo_tx_timeout = i40evf_tx_timeout, .ndo_vlan_rx_add_vid = i40evf_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = i40evf_vlan_rx_kill_vid, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = i40evf_netpoll, +#endif }; /** @@ -2089,7 +2123,10 @@ int i40evf_process_config(struct i40evf_adapter *adapter) if (adapter->vf_res->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_VLAN) { - netdev->vlan_features = netdev->features; + netdev->vlan_features = netdev->features & + ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_FILTER); netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; @@ -2118,6 +2155,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) adapter->vsi.tx_itr_setting = (I40E_ITR_DYNAMIC | ITR_REG_TO_USEC(I40E_ITR_TX_DEF)); adapter->vsi.netdev = adapter->netdev; + adapter->vsi.qs_handle = adapter->vsi_res->qset_handle; return 0; } @@ -2246,10 +2284,13 @@ static void i40evf_init_task(struct work_struct *work) if (!is_valid_ether_addr(adapter->hw.mac.addr)) { dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n", adapter->hw.mac.addr); - random_ether_addr(adapter->hw.mac.addr); + eth_hw_addr_random(netdev); + ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr); + } else { + adapter->flags |= I40EVF_FLAG_ADDR_SET_BY_PF; + ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr); + ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr); } - ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr); - ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr); init_timer(&adapter->watchdog_timer); adapter->watchdog_timer.function = &i40evf_watchdog_timer; @@ -2265,6 +2306,9 @@ static void i40evf_init_task(struct work_struct *work) if (err) goto err_sw_init; i40evf_map_rings_to_vectors(adapter); + if (adapter->vf_res->vf_offload_flags & + I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) + adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE; if (!RSS_AQ(adapter)) i40evf_configure_rss(adapter); err = i40evf_request_misc_irq(adapter); @@ -2300,8 +2344,7 @@ static void i40evf_init_task(struct work_struct *work) } return; restart: - schedule_delayed_work(&adapter->init_task, - msecs_to_jiffies(50)); + schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(30)); return; err_register: @@ -2318,7 +2361,7 @@ err: adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; return; /* do not reschedule */ } - schedule_delayed_work(&adapter->init_task, HZ * 3); + schedule_delayed_work(&adapter->init_task, HZ); } /** @@ -2434,7 +2477,8 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_WORK(&adapter->adminq_task, i40evf_adminq_task); INIT_WORK(&adapter->watchdog_task, i40evf_watchdog_task); INIT_DELAYED_WORK(&adapter->init_task, i40evf_init_task); - schedule_delayed_work(&adapter->init_task, 10); + schedule_delayed_work(&adapter->init_task, + msecs_to_jiffies(5 * (pdev->devfn & 0x07))); return 0; @@ -2510,6 +2554,7 @@ static int i40evf_resume(struct pci_dev *pdev) rtnl_lock(); err = i40evf_set_interrupt_capability(adapter); if (err) { + rtnl_unlock(); dev_err(&pdev->dev, "Cannot enable MSI-X interrupts.\n"); return err; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index d4eb1a5e7d42..32e620e1eb5c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -156,7 +156,8 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter) caps = I40E_VIRTCHNL_VF_OFFLOAD_L2 | I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ | I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG | - I40E_VIRTCHNL_VF_OFFLOAD_VLAN; + I40E_VIRTCHNL_VF_OFFLOAD_VLAN | + I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES; adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG; if (PF_IS_V11(adapter)) @@ -234,8 +235,8 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot configure queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES; @@ -288,8 +289,8 @@ void i40evf_enable_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot enable queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_ENABLE_QUEUES; @@ -313,8 +314,8 @@ void i40evf_disable_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot disable queues, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_DISABLE_QUEUES; @@ -341,8 +342,8 @@ void i40evf_map_queues(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot map queues to vectors, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_IRQ_MAP; @@ -393,8 +394,8 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot add filters, command %d pending\n", + adapter->current_op); return; } list_for_each_entry(f, &adapter->mac_filter_list, list) { @@ -410,8 +411,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_ether_addr_list) + (count * sizeof(struct i40e_virtchnl_ether_addr)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many add MAC changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); @@ -453,8 +453,8 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot remove filters, command %d pending\n", + adapter->current_op); return; } list_for_each_entry(f, &adapter->mac_filter_list, list) { @@ -470,8 +470,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_ether_addr_list) + (count * sizeof(struct i40e_virtchnl_ether_addr)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many MAC address changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many delete MAC changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); @@ -513,8 +512,8 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot add VLANs, command %d pending\n", + adapter->current_op); return; } @@ -531,8 +530,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_vlan_filter_list) + (count * sizeof(u16)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); @@ -572,8 +570,8 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot remove VLANs, command %d pending\n", + adapter->current_op); return; } @@ -590,8 +588,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_vlan_filter_list) + (count * sizeof(u16)); if (len > I40EVF_MAX_AQ_BUF_SIZE) { - dev_warn(&adapter->pdev->dev, "%s: Too many VLAN changes in one request\n", - __func__); + dev_warn(&adapter->pdev->dev, "Too many delete VLAN changes in one request\n"); count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); @@ -629,8 +626,8 @@ void i40evf_set_promiscuous(struct i40evf_adapter *adapter, int flags) if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ - dev_err(&adapter->pdev->dev, "%s: command %d pending\n", - __func__, adapter->current_op); + dev_err(&adapter->pdev->dev, "Cannot set promiscuous mode, command %d pending\n", + adapter->current_op); return; } adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE; @@ -720,17 +717,16 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, } break; default: - dev_err(&adapter->pdev->dev, - "%s: Unknown event %d from pf\n", - __func__, vpe->event); + dev_err(&adapter->pdev->dev, "Unknown event %d from PF\n", + vpe->event); break; } return; } if (v_retval) { - dev_err(&adapter->pdev->dev, "%s: PF returned error %d (%s) to our request %d\n", - __func__, v_retval, - i40evf_stat_str(&adapter->hw, v_retval), v_opcode); + dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n", + v_retval, i40evf_stat_str(&adapter->hw, v_retval), + v_opcode); } switch (v_opcode) { case I40E_VIRTCHNL_OP_GET_STATS: { @@ -756,6 +752,8 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, sizeof(struct i40e_virtchnl_vsi_resource); memcpy(adapter->vf_res, msg, min(msglen, len)); i40e_vf_parse_hw_config(&adapter->hw, adapter->vf_res); + /* restore current mac address */ + ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr); i40evf_process_config(adapter); } break; diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 212d668dabb3..1a2f1cc44b28 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -444,8 +444,8 @@ struct igb_adapter { struct ptp_pin_desc sdp_config[IGB_N_SDP]; struct { - struct timespec start; - struct timespec period; + struct timespec64 start; + struct timespec64 period; } perout[IGB_N_PEROUT]; char fw_version[32]; diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 74262768b09b..2529bc625de4 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -842,10 +842,6 @@ static void igb_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = IGB_STATS_LEN; - drvinfo->testinfo_len = IGB_TEST_LEN; - drvinfo->regdump_len = igb_get_regs_len(netdev); - drvinfo->eedump_len = igb_get_eeprom_len(netdev); } static void igb_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index e174fbbdba40..ea7b09887245 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -151,7 +151,7 @@ static void igb_setup_dca(struct igb_adapter *); #endif /* CONFIG_IGB_DCA */ static int igb_poll(struct napi_struct *, int); static bool igb_clean_tx_irq(struct igb_q_vector *); -static bool igb_clean_rx_irq(struct igb_q_vector *, int); +static int igb_clean_rx_irq(struct igb_q_vector *, int); static int igb_ioctl(struct net_device *, struct ifreq *, int cmd); static void igb_tx_timeout(struct net_device *); static void igb_reset_task(struct work_struct *); @@ -2986,6 +2986,9 @@ static int igb_sw_init(struct igb_adapter *adapter) } #endif /* CONFIG_PCI_IOV */ + /* Assume MSI-X interrupts, will be checked during IRQ allocation */ + adapter->flags |= IGB_FLAG_HAS_MSIX; + igb_probe_vfs(adapter); igb_init_queue_configuration(adapter); @@ -5389,7 +5392,7 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct ptp_clock_event event; - struct timespec ts; + struct timespec64 ts; u32 ack = 0, tsauxc, sec, nsec, tsicr = rd32(E1000_TSICR); if (tsicr & TSINTR_SYS_WRAP) { @@ -5409,10 +5412,11 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) if (tsicr & TSINTR_TT0) { spin_lock(&adapter->tmreg_lock); - ts = timespec_add(adapter->perout[0].start, - adapter->perout[0].period); + ts = timespec64_add(adapter->perout[0].start, + adapter->perout[0].period); + /* u32 conversion of tv_sec is safe until y2106 */ wr32(E1000_TRGTTIML0, ts.tv_nsec); - wr32(E1000_TRGTTIMH0, ts.tv_sec); + wr32(E1000_TRGTTIMH0, (u32)ts.tv_sec); tsauxc = rd32(E1000_TSAUXC); tsauxc |= TSAUXC_EN_TT0; wr32(E1000_TSAUXC, tsauxc); @@ -5423,10 +5427,10 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) if (tsicr & TSINTR_TT1) { spin_lock(&adapter->tmreg_lock); - ts = timespec_add(adapter->perout[1].start, - adapter->perout[1].period); + ts = timespec64_add(adapter->perout[1].start, + adapter->perout[1].period); wr32(E1000_TRGTTIML1, ts.tv_nsec); - wr32(E1000_TRGTTIMH1, ts.tv_sec); + wr32(E1000_TRGTTIMH1, (u32)ts.tv_sec); tsauxc = rd32(E1000_TSAUXC); tsauxc |= TSAUXC_EN_TT1; wr32(E1000_TSAUXC, tsauxc); @@ -6360,6 +6364,7 @@ static int igb_poll(struct napi_struct *napi, int budget) struct igb_q_vector, napi); bool clean_complete = true; + int work_done = 0; #ifdef CONFIG_IGB_DCA if (q_vector->adapter->flags & IGB_FLAG_DCA_ENABLED) @@ -6368,15 +6373,19 @@ static int igb_poll(struct napi_struct *napi, int budget) if (q_vector->tx.ring) clean_complete = igb_clean_tx_irq(q_vector); - if (q_vector->rx.ring) - clean_complete &= igb_clean_rx_irq(q_vector, budget); + if (q_vector->rx.ring) { + int cleaned = igb_clean_rx_irq(q_vector, budget); + + work_done += cleaned; + clean_complete &= (cleaned < budget); + } /* If all work not completed, return budget and keep polling */ if (!clean_complete) return budget; /* If not enough Rx work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); igb_ring_irq_enable(q_vector); return 0; @@ -6900,7 +6909,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring, skb->protocol = eth_type_trans(skb, rx_ring->netdev); } -static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) +static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) { struct igb_ring *rx_ring = q_vector->rx.ring; struct sk_buff *skb = rx_ring->skb; @@ -6974,7 +6983,7 @@ static bool igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) if (cleaned_count) igb_alloc_rx_buffers(rx_ring, cleaned_count); - return total_packets < budget; + return total_packets; } static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 5982f28d521a..c44df87c38de 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -143,7 +143,7 @@ static void igb_ptp_write_i210(struct igb_adapter *adapter, * sub-nanosecond resolution. */ wr32(E1000_SYSTIML, ts->tv_nsec); - wr32(E1000_SYSTIMH, ts->tv_sec); + wr32(E1000_SYSTIMH, (u32)ts->tv_sec); } /** @@ -479,7 +479,7 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, struct e1000_hw *hw = &igb->hw; u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout; unsigned long flags; - struct timespec ts; + struct timespec64 ts; int use_freq = 0, pin = -1; s64 ns; @@ -523,14 +523,14 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, } ts.tv_sec = rq->perout.period.sec; ts.tv_nsec = rq->perout.period.nsec; - ns = timespec_to_ns(&ts); + ns = timespec64_to_ns(&ts); ns = ns >> 1; if (on && ns <= 70000000LL) { if (ns < 8LL) return -EINVAL; use_freq = 1; } - ts = ns_to_timespec(ns); + ts = ns_to_timespec64(ns); if (rq->perout.index == 1) { if (use_freq) { tsauxc_mask = TSAUXC_EN_CLK1 | TSAUXC_ST1; diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index c6996feb1cb4..b74ce53d7b52 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -196,8 +196,6 @@ static void igbvf_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = igbvf_get_regs_len(netdev); - drvinfo->eedump_len = igbvf_get_eeprom_len(netdev); } static void igbvf_get_ringparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 686fa7184179..297af801f051 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1211,7 +1211,7 @@ static int igbvf_poll(struct napi_struct *napi, int budget) /* If not enough Rx work done, exit the polling mode */ if (work_done < budget) { - napi_complete(napi); + napi_complete_done(napi, work_done); if (adapter->requested_itr & 3) igbvf_set_itr(adapter); @@ -2615,6 +2615,7 @@ static const struct net_device_ops igbvf_netdev_ops = { .ndo_poll_controller = igbvf_netpoll, #endif .ndo_set_features = igbvf_set_features, + .ndo_features_check = passthru_features_check, }; /** diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c index b311e9e710d2..d2b29b490ae0 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c @@ -479,9 +479,6 @@ ixgb_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = IXGB_STATS_LEN; - drvinfo->regdump_len = ixgb_get_regs_len(netdev); - drvinfo->eedump_len = ixgb_get_eeprom_len(netdev); } static void diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index edf1fb913209..dda0f678339a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -539,8 +539,7 @@ struct hwmon_buff { #define IXGBE_MIN_RSC_ITR 24 #define IXGBE_100K_ITR 40 #define IXGBE_20K_ITR 200 -#define IXGBE_10K_ITR 400 -#define IXGBE_8K_ITR 500 +#define IXGBE_12K_ITR 336 /* ixgbe_test_staterr - tests bits in Rx descriptor status and error fields */ static inline __le32 ixgbe_test_staterr(union ixgbe_adv_rx_desc *rx_desc, @@ -595,6 +594,7 @@ struct ixgbe_mac_addr { /* default to trying for four seconds */ #define IXGBE_TRY_LINK_TIMEOUT (4 * HZ) +#define IXGBE_SFP_POLL_JIFFIES (2 * HZ) /* SFP poll every 2 seconds */ /* board specific private data structure */ struct ixgbe_adapter { @@ -708,6 +708,7 @@ struct ixgbe_adapter { u32 link_speed; bool link_up; + unsigned long sfp_poll_time; unsigned long link_check_timeout; struct timer_list service_timer; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index dd7062fed61a..a39afcf03e2c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -44,9 +44,8 @@ static void ixgbe_disable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); static void ixgbe_enable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw); -static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete); +static void +ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *, ixgbe_link_speed); static s32 ixgbe_setup_mac_link_smartspeed(struct ixgbe_hw *hw, ixgbe_link_speed speed, bool autoneg_wait_to_complete); @@ -109,6 +108,9 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw) if (hw->phy.multispeed_fiber) { /* Set up dual speed SFP+ support */ mac->ops.setup_link = &ixgbe_setup_mac_link_multispeed_fiber; + mac->ops.setup_mac_link = ixgbe_setup_mac_link_82599; + mac->ops.set_rate_select_speed = + ixgbe_set_hard_rate_select_speed; } else { if ((mac->ops.get_media_type(hw) == ixgbe_media_type_backplane) && @@ -646,176 +648,32 @@ static void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw) } /** - * ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed - * @hw: pointer to hardware structure - * @speed: new link speed - * @autoneg_wait_to_complete: true when waiting for completion is needed + * ixgbe_set_hard_rate_select_speed - Set module link speed + * @hw: pointer to hardware structure + * @speed: link speed to set * - * Set the link speed in the AUTOC register and restarts link. - **/ -static s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, - ixgbe_link_speed speed, - bool autoneg_wait_to_complete) + * Set module link speed via RS0/RS1 rate select pins. + */ +static void +ixgbe_set_hard_rate_select_speed(struct ixgbe_hw *hw, ixgbe_link_speed speed) { - s32 status = 0; - ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; - ixgbe_link_speed highest_link_speed = IXGBE_LINK_SPEED_UNKNOWN; - u32 speedcnt = 0; u32 esdp_reg = IXGBE_READ_REG(hw, IXGBE_ESDP); - u32 i = 0; - bool link_up = false; - bool autoneg = false; - - /* Mask off requested but non-supported speeds */ - status = hw->mac.ops.get_link_capabilities(hw, &link_speed, - &autoneg); - if (status != 0) - return status; - - speed &= link_speed; - - /* - * Try each speed one by one, highest priority first. We do this in - * software because 10gb fiber doesn't support speed autonegotiation. - */ - if (speed & IXGBE_LINK_SPEED_10GB_FULL) { - speedcnt++; - highest_link_speed = IXGBE_LINK_SPEED_10GB_FULL; - - /* If we already have link at this speed, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, &link_up, - false); - if (status != 0) - return status; - - if ((link_speed == IXGBE_LINK_SPEED_10GB_FULL) && link_up) - goto out; - - /* Set the module link speed */ - switch (hw->phy.media_type) { - case ixgbe_media_type_fiber: - esdp_reg |= (IXGBE_ESDP_SDP5_DIR | IXGBE_ESDP_SDP5); - IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg); - IXGBE_WRITE_FLUSH(hw); - break; - case ixgbe_media_type_fiber_qsfp: - /* QSFP module automatically detects MAC link speed */ - break; - default: - hw_dbg(hw, "Unexpected media type.\n"); - break; - } - - /* Allow module to change analog characteristics (1G->10G) */ - msleep(40); - - status = ixgbe_setup_mac_link_82599(hw, - IXGBE_LINK_SPEED_10GB_FULL, - autoneg_wait_to_complete); - if (status != 0) - return status; - - /* Flap the tx laser if it has not already been done */ - if (hw->mac.ops.flap_tx_laser) - hw->mac.ops.flap_tx_laser(hw); - - /* - * Wait for the controller to acquire link. Per IEEE 802.3ap, - * Section 73.10.2, we may have to wait up to 500ms if KR is - * attempted. 82599 uses the same timing for 10g SFI. - */ - for (i = 0; i < 5; i++) { - /* Wait for the link partner to also set speed */ - msleep(100); - - /* If we have link, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, - &link_up, false); - if (status != 0) - return status; - - if (link_up) - goto out; - } - } - - if (speed & IXGBE_LINK_SPEED_1GB_FULL) { - speedcnt++; - if (highest_link_speed == IXGBE_LINK_SPEED_UNKNOWN) - highest_link_speed = IXGBE_LINK_SPEED_1GB_FULL; - - /* If we already have link at this speed, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, &link_up, - false); - if (status != 0) - return status; - - if ((link_speed == IXGBE_LINK_SPEED_1GB_FULL) && link_up) - goto out; - - /* Set the module link speed */ - switch (hw->phy.media_type) { - case ixgbe_media_type_fiber: - esdp_reg &= ~IXGBE_ESDP_SDP5; - esdp_reg |= IXGBE_ESDP_SDP5_DIR; - IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg); - IXGBE_WRITE_FLUSH(hw); - break; - case ixgbe_media_type_fiber_qsfp: - /* QSFP module automatically detects MAC link speed */ - break; - default: - hw_dbg(hw, "Unexpected media type.\n"); - break; - } - - /* Allow module to change analog characteristics (10G->1G) */ - msleep(40); - - status = ixgbe_setup_mac_link_82599(hw, - IXGBE_LINK_SPEED_1GB_FULL, - autoneg_wait_to_complete); - if (status != 0) - return status; - - /* Flap the tx laser if it has not already been done */ - if (hw->mac.ops.flap_tx_laser) - hw->mac.ops.flap_tx_laser(hw); - /* Wait for the link partner to also set speed */ - msleep(100); - - /* If we have link, just jump out */ - status = hw->mac.ops.check_link(hw, &link_speed, &link_up, - false); - if (status != 0) - return status; - - if (link_up) - goto out; + switch (speed) { + case IXGBE_LINK_SPEED_10GB_FULL: + esdp_reg |= (IXGBE_ESDP_SDP5_DIR | IXGBE_ESDP_SDP5); + break; + case IXGBE_LINK_SPEED_1GB_FULL: + esdp_reg &= ~IXGBE_ESDP_SDP5; + esdp_reg |= IXGBE_ESDP_SDP5_DIR; + break; + default: + hw_dbg(hw, "Invalid fixed module speed\n"); + return; } - /* - * We didn't get link. Configure back to the highest speed we tried, - * (if there was more than one). We call ourselves back with just the - * single highest speed that the user requested. - */ - if (speedcnt > 1) - status = ixgbe_setup_mac_link_multispeed_fiber(hw, - highest_link_speed, - autoneg_wait_to_complete); - -out: - /* Set autoneg_advertised value based on input link speed */ - hw->phy.autoneg_advertised = 0; - - if (speed & IXGBE_LINK_SPEED_10GB_FULL) - hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL; - - if (speed & IXGBE_LINK_SPEED_1GB_FULL) - hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; - - return status; + IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg); + IXGBE_WRITE_FLUSH(hw); } /** @@ -1766,6 +1624,16 @@ s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, IXGBE_WRITE_REG(hw, IXGBE_FDIRTCPM, ~fdirtcpm); IXGBE_WRITE_REG(hw, IXGBE_FDIRUDPM, ~fdirtcpm); + /* also use it for SCTP */ + switch (hw->mac.type) { + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + IXGBE_WRITE_REG(hw, IXGBE_FDIRSCTPM, ~fdirtcpm); + break; + default: + break; + } + /* store source and destination IP masks (big-enian) */ IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIP4M, ~input_mask->formatted.src_ip[0]); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 3f56a8080118..ce61b36b94f1 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -297,13 +297,13 @@ s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw) /* Setup flow control */ ret_val = ixgbe_setup_fc(hw); - if (!ret_val) - return 0; + if (ret_val) + return ret_val; /* Clear adapter stopped flag */ hw->adapter_stopped = false; - return ret_val; + return 0; } /** @@ -2164,10 +2164,11 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw) /* * In order to prevent Tx hangs when the internal Tx * switch is enabled we must set the high water mark - * to the maximum FCRTH value. This allows the Tx - * switch to function even under heavy Rx workloads. + * to the Rx packet buffer size - 24KB. This allows + * the Tx switch to function even under heavy Rx + * workloads. */ - fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32; + fcrth = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 24576; } IXGBE_WRITE_REG(hw, IXGBE_FCRTH_82599(i), fcrth); @@ -2476,6 +2477,9 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n"); hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; + if (hw->mac.type >= ixgbe_mac_X550) + return 0; + /* * Before proceeding, make sure that the PCIe block does not have * transactions pending. @@ -3920,3 +3924,213 @@ bool ixgbe_mng_present(struct ixgbe_hw *hw) fwsm &= IXGBE_FWSM_MODE_MASK; return fwsm == IXGBE_FWSM_FW_MODE_PT; } + +/** + * ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed + * @hw: pointer to hardware structure + * @speed: new link speed + * @autoneg_wait_to_complete: true when waiting for completion is needed + * + * Set the link speed in the MAC and/or PHY register and restarts link. + */ +s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg_wait_to_complete) +{ + ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN; + ixgbe_link_speed highest_link_speed = IXGBE_LINK_SPEED_UNKNOWN; + s32 status = 0; + u32 speedcnt = 0; + u32 i = 0; + bool autoneg, link_up = false; + + /* Mask off requested but non-supported speeds */ + status = hw->mac.ops.get_link_capabilities(hw, &link_speed, &autoneg); + if (status) + return status; + + speed &= link_speed; + + /* Try each speed one by one, highest priority first. We do this in + * software because 10Gb fiber doesn't support speed autonegotiation. + */ + if (speed & IXGBE_LINK_SPEED_10GB_FULL) { + speedcnt++; + highest_link_speed = IXGBE_LINK_SPEED_10GB_FULL; + + /* If we already have link at this speed, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, &link_up, + false); + if (status) + return status; + + if (link_speed == IXGBE_LINK_SPEED_10GB_FULL && link_up) + goto out; + + /* Set the module link speed */ + switch (hw->phy.media_type) { + case ixgbe_media_type_fiber: + hw->mac.ops.set_rate_select_speed(hw, + IXGBE_LINK_SPEED_10GB_FULL); + break; + case ixgbe_media_type_fiber_qsfp: + /* QSFP module automatically detects MAC link speed */ + break; + default: + hw_dbg(hw, "Unexpected media type\n"); + break; + } + + /* Allow module to change analog characteristics (1G->10G) */ + msleep(40); + + status = hw->mac.ops.setup_mac_link(hw, + IXGBE_LINK_SPEED_10GB_FULL, + autoneg_wait_to_complete); + if (status) + return status; + + /* Flap the Tx laser if it has not already been done */ + if (hw->mac.ops.flap_tx_laser) + hw->mac.ops.flap_tx_laser(hw); + + /* Wait for the controller to acquire link. Per IEEE 802.3ap, + * Section 73.10.2, we may have to wait up to 500ms if KR is + * attempted. 82599 uses the same timing for 10g SFI. + */ + for (i = 0; i < 5; i++) { + /* Wait for the link partner to also set speed */ + msleep(100); + + /* If we have link, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, + &link_up, false); + if (status) + return status; + + if (link_up) + goto out; + } + } + + if (speed & IXGBE_LINK_SPEED_1GB_FULL) { + speedcnt++; + if (highest_link_speed == IXGBE_LINK_SPEED_UNKNOWN) + highest_link_speed = IXGBE_LINK_SPEED_1GB_FULL; + + /* If we already have link at this speed, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, &link_up, + false); + if (status) + return status; + + if (link_speed == IXGBE_LINK_SPEED_1GB_FULL && link_up) + goto out; + + /* Set the module link speed */ + switch (hw->phy.media_type) { + case ixgbe_media_type_fiber: + hw->mac.ops.set_rate_select_speed(hw, + IXGBE_LINK_SPEED_1GB_FULL); + break; + case ixgbe_media_type_fiber_qsfp: + /* QSFP module automatically detects link speed */ + break; + default: + hw_dbg(hw, "Unexpected media type\n"); + break; + } + + /* Allow module to change analog characteristics (10G->1G) */ + msleep(40); + + status = hw->mac.ops.setup_mac_link(hw, + IXGBE_LINK_SPEED_1GB_FULL, + autoneg_wait_to_complete); + if (status) + return status; + + /* Flap the Tx laser if it has not already been done */ + if (hw->mac.ops.flap_tx_laser) + hw->mac.ops.flap_tx_laser(hw); + + /* Wait for the link partner to also set speed */ + msleep(100); + + /* If we have link, just jump out */ + status = hw->mac.ops.check_link(hw, &link_speed, &link_up, + false); + if (status) + return status; + + if (link_up) + goto out; + } + + /* We didn't get link. Configure back to the highest speed we tried, + * (if there was more than one). We call ourselves back with just the + * single highest speed that the user requested. + */ + if (speedcnt > 1) + status = ixgbe_setup_mac_link_multispeed_fiber(hw, + highest_link_speed, + autoneg_wait_to_complete); + +out: + /* Set autoneg_advertised value based on input link speed */ + hw->phy.autoneg_advertised = 0; + + if (speed & IXGBE_LINK_SPEED_10GB_FULL) + hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL; + + if (speed & IXGBE_LINK_SPEED_1GB_FULL) + hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL; + + return status; +} + +/** + * ixgbe_set_soft_rate_select_speed - Set module link speed + * @hw: pointer to hardware structure + * @speed: link speed to set + * + * Set module link speed via the soft rate select. + */ +void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw, + ixgbe_link_speed speed) +{ + s32 status; + u8 rs, eeprom_data; + + switch (speed) { + case IXGBE_LINK_SPEED_10GB_FULL: + /* one bit mask same as setting on */ + rs = IXGBE_SFF_SOFT_RS_SELECT_10G; + break; + case IXGBE_LINK_SPEED_1GB_FULL: + rs = IXGBE_SFF_SOFT_RS_SELECT_1G; + break; + default: + hw_dbg(hw, "Invalid fixed module speed\n"); + return; + } + + /* Set RS0 */ + status = hw->phy.ops.read_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB, + IXGBE_I2C_EEPROM_DEV_ADDR2, + &eeprom_data); + if (status) { + hw_dbg(hw, "Failed to read Rx Rate Select RS0\n"); + return; + } + + eeprom_data = (eeprom_data & ~IXGBE_SFF_SOFT_RS_SELECT_MASK) | rs; + + status = hw->phy.ops.write_i2c_byte(hw, IXGBE_SFF_SFF_8472_OSCB, + IXGBE_I2C_EEPROM_DEV_ADDR2, + eeprom_data); + if (status) { + hw_dbg(hw, "Failed to write Rx Rate Select RS0\n"); + return; + } +} diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index 2f779f35dc4f..a0044e4a8b90 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -135,6 +135,11 @@ s32 ixgbe_get_thermal_sensor_data_generic(struct ixgbe_hw *hw); s32 ixgbe_init_thermal_sensor_thresh_generic(struct ixgbe_hw *hw); void ixgbe_disable_rx_generic(struct ixgbe_hw *hw); void ixgbe_enable_rx_generic(struct ixgbe_hw *hw); +s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + bool autoneg_wait_to_complete); +void ixgbe_set_soft_rate_select_speed(struct ixgbe_hw *hw, + ixgbe_link_speed speed); #define IXGBE_FAILED_READ_REG 0xffffffffU #define IXGBE_FAILED_READ_CFG_DWORD 0xffffffffU diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c index 3b932fe64ab6..23277ab153b6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c @@ -259,7 +259,13 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) fcrtl = (hw->fc.low_water[i] << 10) | IXGBE_FCRTL_XONE; IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), fcrtl); } else { - reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 32; + /* In order to prevent Tx hangs when the internal Tx + * switch is enabled we must set the high water mark + * to the Rx packet buffer size - 24KB. This allows + * the Tx switch to function even under heavy Rx + * workloads. + */ + reg = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i)) - 24576; IXGBE_WRITE_REG(hw, IXGBE_FCRTL_82599(i), 0); } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index ab2edc8e7703..d681273bd39d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -943,9 +943,6 @@ static void ixgbe_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = IXGBE_STATS_LEN; - drvinfo->testinfo_len = IXGBE_TEST_LEN; - drvinfo->regdump_len = ixgbe_get_regs_len(netdev); } static void ixgbe_get_ringparam(struct net_device *netdev, @@ -2286,7 +2283,7 @@ static int ixgbe_set_coalesce(struct net_device *netdev, adapter->tx_itr_setting = ec->tx_coalesce_usecs; if (adapter->tx_itr_setting == 1) - tx_itr_param = IXGBE_10K_ITR; + tx_itr_param = IXGBE_12K_ITR; else tx_itr_param = adapter->tx_itr_setting; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index 68e1e757ecef..f3168bcc7d87 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -866,7 +866,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, if (txr_count && !rxr_count) { /* tx only vector */ if (adapter->tx_itr_setting == 1) - q_vector->itr = IXGBE_10K_ITR; + q_vector->itr = IXGBE_12K_ITR; else q_vector->itr = adapter->tx_itr_setting; } else { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 63b2cfe9416b..9f8a7fd7a195 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -79,7 +79,7 @@ char ixgbe_default_device_descr[] = static char ixgbe_default_device_descr[] = "Intel(R) 10 Gigabit Network Connection"; #endif -#define DRV_VERSION "4.0.1-k" +#define DRV_VERSION "4.2.1-k" const char ixgbe_driver_version[] = DRV_VERSION; static const char ixgbe_copyright[] = "Copyright (c) 1999-2015 Intel Corporation."; @@ -137,6 +137,7 @@ static const struct pci_device_id ixgbe_pci_tbl[] = { {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KX4), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KR), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_10G_T), board_X550EM_x}, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_SFP), board_X550EM_x}, /* required last entry */ {0, } }; @@ -1244,9 +1245,12 @@ static void ixgbe_update_tx_dca(struct ixgbe_adapter *adapter, int cpu) { struct ixgbe_hw *hw = &adapter->hw; - u32 txctrl = dca3_get_tag(tx_ring->dev, cpu); + u32 txctrl = 0; u16 reg_offset; + if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) + txctrl = dca3_get_tag(tx_ring->dev, cpu); + switch (hw->mac.type) { case ixgbe_mac_82598EB: reg_offset = IXGBE_DCA_TXCTRL(tx_ring->reg_idx); @@ -1278,9 +1282,11 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter, int cpu) { struct ixgbe_hw *hw = &adapter->hw; - u32 rxctrl = dca3_get_tag(rx_ring->dev, cpu); + u32 rxctrl = 0; u8 reg_idx = rx_ring->reg_idx; + if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) + rxctrl = dca3_get_tag(rx_ring->dev, cpu); switch (hw->mac.type) { case ixgbe_mac_82599EB: @@ -1297,6 +1303,7 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter, * which will cause the DCA tag to be cleared. */ rxctrl |= IXGBE_DCA_RXCTRL_DESC_RRO_EN | + IXGBE_DCA_RXCTRL_DATA_DCA_EN | IXGBE_DCA_RXCTRL_DESC_DCA_EN; IXGBE_WRITE_REG(hw, IXGBE_DCA_RXCTRL(reg_idx), rxctrl); @@ -1326,11 +1333,13 @@ static void ixgbe_setup_dca(struct ixgbe_adapter *adapter) { int i; - if (!(adapter->flags & IXGBE_FLAG_DCA_ENABLED)) - return; - /* always use CB2 mode, difference is masked in the CB driver */ - IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 2); + if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_MODE_CB2); + else + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_DISABLE); for (i = 0; i < adapter->num_q_vectors; i++) { adapter->q_vector[i]->cpu = -1; @@ -1353,7 +1362,8 @@ static int __ixgbe_notify_dca(struct device *dev, void *data) break; if (dca_add_requester(dev) == 0) { adapter->flags |= IXGBE_FLAG_DCA_ENABLED; - ixgbe_setup_dca(adapter); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_MODE_CB2); break; } /* Fall Through since DCA is disabled. */ @@ -1361,7 +1371,8 @@ static int __ixgbe_notify_dca(struct device *dev, void *data) if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) { dca_remove_requester(dev); adapter->flags &= ~IXGBE_FLAG_DCA_ENABLED; - IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 1); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_DISABLE); } break; } @@ -2261,7 +2272,7 @@ static void ixgbe_update_itr(struct ixgbe_q_vector *q_vector, /* simple throttlerate management * 0-10MB/s lowest (100000 ints/s) * 10-20MB/s low (20000 ints/s) - * 20-1249MB/s bulk (8000 ints/s) + * 20-1249MB/s bulk (12000 ints/s) */ /* what was last interrupt timeslice? */ timepassed_us = q_vector->itr >> 2; @@ -2350,7 +2361,7 @@ static void ixgbe_set_itr(struct ixgbe_q_vector *q_vector) new_itr = IXGBE_20K_ITR; break; case bulk_latency: - new_itr = IXGBE_8K_ITR; + new_itr = IXGBE_12K_ITR; break; default: break; @@ -2495,17 +2506,27 @@ static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw) static void ixgbe_check_sfp_event(struct ixgbe_adapter *adapter, u32 eicr) { struct ixgbe_hw *hw = &adapter->hw; + u32 eicr_mask = IXGBE_EICR_GPI_SDP2(hw); + + if (!ixgbe_is_sfp(hw)) + return; - if (eicr & IXGBE_EICR_GPI_SDP2(hw)) { + /* Later MAC's use different SDP */ + if (hw->mac.type >= ixgbe_mac_X540) + eicr_mask = IXGBE_EICR_GPI_SDP0_X540; + + if (eicr & eicr_mask) { /* Clear the interrupt */ - IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP2(hw)); + IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr_mask); if (!test_bit(__IXGBE_DOWN, &adapter->state)) { adapter->flags2 |= IXGBE_FLAG2_SFP_NEEDS_RESET; + adapter->sfp_poll_time = 0; ixgbe_service_event_schedule(adapter); } } - if (eicr & IXGBE_EICR_GPI_SDP1(hw)) { + if (adapter->hw.mac.type == ixgbe_mac_82599EB && + (eicr & IXGBE_EICR_GPI_SDP1(hw))) { /* Clear the interrupt */ IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1(hw)); if (!test_bit(__IXGBE_DOWN, &adapter->state)) { @@ -2622,6 +2643,8 @@ static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter, bool queues, case ixgbe_mac_X540: case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: + if (adapter->hw.device_id == IXGBE_DEV_ID_X550EM_X_SFP) + mask |= IXGBE_EIMS_GPI_SDP0(&adapter->hw); if (adapter->hw.phy.type == ixgbe_phy_x550em_ext_t) mask |= IXGBE_EICR_GPI_SDP0_X540; mask |= IXGBE_EIMS_ECC; @@ -2752,7 +2775,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget) container_of(napi, struct ixgbe_q_vector, napi); struct ixgbe_adapter *adapter = q_vector->adapter; struct ixgbe_ring *ring; - int per_ring_budget; + int per_ring_budget, work_done = 0; bool clean_complete = true; #ifdef CONFIG_IXGBE_DCA @@ -2773,9 +2796,13 @@ int ixgbe_poll(struct napi_struct *napi, int budget) else per_ring_budget = budget; - ixgbe_for_each_ring(ring, q_vector->rx) - clean_complete &= (ixgbe_clean_rx_irq(q_vector, ring, - per_ring_budget) < per_ring_budget); + ixgbe_for_each_ring(ring, q_vector->rx) { + int cleaned = ixgbe_clean_rx_irq(q_vector, ring, + per_ring_budget); + + work_done += cleaned; + clean_complete &= (cleaned < per_ring_budget); + } ixgbe_qv_unlock_napi(q_vector); /* If all work not completed, return budget and keep polling */ @@ -2783,7 +2810,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget) return budget; /* all work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); if (adapter->rx_itr_setting & 1) ixgbe_set_itr(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) @@ -3700,14 +3727,20 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) hw->mac.ops.set_mac_anti_spoofing(hw, (adapter->num_vfs != 0), adapter->num_vfs); - /* Ensure LLDP is set for Ethertype Antispoofing if we will be + /* Ensure LLDP and FC is set for Ethertype Antispoofing if we will be * calling set_ethertype_anti_spoofing for each VF in loop below */ - if (hw->mac.ops.set_ethertype_anti_spoofing) + if (hw->mac.ops.set_ethertype_anti_spoofing) { IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_LLDP), - (IXGBE_ETQF_FILTER_EN | /* enable filter */ - IXGBE_ETQF_TX_ANTISPOOF | /* tx antispoof */ - IXGBE_ETH_P_LLDP)); /* LLDP eth type */ + (IXGBE_ETQF_FILTER_EN | + IXGBE_ETQF_TX_ANTISPOOF | + IXGBE_ETH_P_LLDP)); + + IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_FC), + (IXGBE_ETQF_FILTER_EN | + IXGBE_ETQF_TX_ANTISPOOF | + ETH_P_PAUSE)); + } /* For VFs that have spoof checking turned off */ for (i = 0; i < adapter->num_vfs; i++) { @@ -3777,8 +3810,6 @@ static void ixgbe_setup_rdrxctl(struct ixgbe_adapter *adapter) u32 rdrxctl = IXGBE_READ_REG(hw, IXGBE_RDRXCTL); switch (hw->mac.type) { - case ixgbe_mac_X550: - case ixgbe_mac_X550EM_x: case ixgbe_mac_82598EB: /* * For VMDq support of different descriptor types or @@ -3792,6 +3823,11 @@ static void ixgbe_setup_rdrxctl(struct ixgbe_adapter *adapter) */ rdrxctl |= IXGBE_RDRXCTL_MVMEN; break; + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + if (adapter->num_vfs) + rdrxctl |= IXGBE_RDRXCTL_PSP; + /* fall through for older HW */ case ixgbe_mac_82599EB: case ixgbe_mac_X540: /* Disable RSC for ACK packets */ @@ -4767,6 +4803,12 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter) break; } +#ifdef CONFIG_IXGBE_DCA + /* configure DCA */ + if (adapter->flags & IXGBE_FLAG_DCA_CAPABLE) + ixgbe_setup_dca(adapter); +#endif /* CONFIG_IXGBE_DCA */ + #ifdef IXGBE_FCOE /* configure FCoE L2 filters, redirection table, and Rx control */ ixgbe_configure_fcoe(adapter); @@ -4793,6 +4835,7 @@ static void ixgbe_sfp_link_config(struct ixgbe_adapter *adapter) adapter->flags2 |= IXGBE_FLAG2_SEARCH_FOR_SFP; adapter->flags2 |= IXGBE_FLAG2_SFP_NEEDS_RESET; + adapter->sfp_poll_time = 0; } /** @@ -4883,9 +4926,6 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter) case ixgbe_mac_82599EB: gpie |= IXGBE_SDP0_GPIEN_8259X; break; - case ixgbe_mac_X540: - gpie |= IXGBE_EIMS_TS; - break; default: break; } @@ -4895,9 +4935,15 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter) if (adapter->flags & IXGBE_FLAG_FAN_FAIL_CAPABLE) gpie |= IXGBE_SDP1_GPIEN(hw); - if (hw->mac.type == ixgbe_mac_82599EB) { - gpie |= IXGBE_SDP1_GPIEN_8259X; - gpie |= IXGBE_SDP2_GPIEN_8259X; + switch (hw->mac.type) { + case ixgbe_mac_82599EB: + gpie |= IXGBE_SDP1_GPIEN_8259X | IXGBE_SDP2_GPIEN_8259X; + break; + case ixgbe_mac_X550EM_x: + gpie |= IXGBE_SDP0_GPIEN_X540; + break; + default: + break; } IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie); @@ -5220,11 +5266,6 @@ void ixgbe_down(struct ixgbe_adapter *adapter) ixgbe_clean_all_tx_rings(adapter); ixgbe_clean_all_rx_rings(adapter); - -#ifdef CONFIG_IXGBE_DCA - /* since we reset the hardware DCA settings were cleared */ - ixgbe_setup_dca(adapter); -#endif } /** @@ -5270,7 +5311,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) rss = min_t(int, ixgbe_max_rss_indices(adapter), num_online_cpus()); adapter->ring_feature[RING_F_RSS].limit = rss; adapter->flags2 |= IXGBE_FLAG2_RSC_CAPABLE; - adapter->flags2 |= IXGBE_FLAG2_RSC_ENABLED; adapter->max_q_vectors = MAX_Q_VECTORS_82599; adapter->atr_sample_rate = 20; fdir = min_t(int, IXGBE_MAX_FDIR_INDICES, num_online_cpus()); @@ -5296,7 +5336,6 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) switch (hw->mac.type) { case ixgbe_mac_82598EB: adapter->flags2 &= ~IXGBE_FLAG2_RSC_CAPABLE; - adapter->flags2 &= ~IXGBE_FLAG2_RSC_ENABLED; if (hw->device_id == IXGBE_DEV_ID_82598AT) adapter->flags |= IXGBE_FLAG_FAN_FAIL_CAPABLE; @@ -6692,10 +6731,16 @@ static void ixgbe_sfp_detection_subtask(struct ixgbe_adapter *adapter) !(adapter->flags2 & IXGBE_FLAG2_SFP_NEEDS_RESET)) return; + if (adapter->sfp_poll_time && + time_after(adapter->sfp_poll_time, jiffies)) + return; /* If not yet time to poll for SFP */ + /* someone else is in init, wait until next service event */ if (test_and_set_bit(__IXGBE_IN_SFP_INIT, &adapter->state)) return; + adapter->sfp_poll_time = jiffies + IXGBE_SFP_POLL_JIFFIES - 1; + err = hw->phy.ops.identify_sfp(hw); if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) goto sfp_out; @@ -8695,8 +8740,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->phy.reset_if_overtemp = true; err = hw->mac.ops.reset_hw(hw); hw->phy.reset_if_overtemp = false; - if (err == IXGBE_ERR_SFP_NOT_PRESENT && - hw->mac.type == ixgbe_mac_82598EB) { + if (err == IXGBE_ERR_SFP_NOT_PRESENT) { err = 0; } else if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { e_dev_err("failed to load because an unsupported SFP+ or QSFP module type was detected.\n"); @@ -9008,7 +9052,8 @@ static void ixgbe_remove(struct pci_dev *pdev) if (adapter->flags & IXGBE_FLAG_DCA_ENABLED) { adapter->flags &= ~IXGBE_FLAG_DCA_ENABLED; dca_remove_requester(&pdev->dev); - IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, 1); + IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_CTRL, + IXGBE_DCA_CTRL_DCA_DISABLE); } #endif @@ -9019,12 +9064,12 @@ static void ixgbe_remove(struct pci_dev *pdev) /* remove the added san mac */ ixgbe_del_sanmac_netdev(netdev); - if (netdev->reg_state == NETREG_REGISTERED) - unregister_netdev(netdev); - #ifdef CONFIG_PCI_IOV ixgbe_disable_sriov(adapter); #endif + if (netdev->reg_state == NETREG_REGISTERED) + unregister_netdev(netdev); + ixgbe_clear_interrupt_scheme(adapter); ixgbe_release_hw_control(adapter); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 597d0b1c2370..fb8673d63806 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -100,16 +100,17 @@ static u8 ixgbe_ones_comp_byte_add(u8 add1, u8 add2) } /** - * ixgbe_read_i2c_combined_generic - Perform I2C read combined operation + * ixgbe_read_i2c_combined_generic_int - Perform I2C read combined operation * @hw: pointer to the hardware structure * @addr: I2C bus address to read from * @reg: I2C device register to read from * @val: pointer to location to receive read value + * @lock: true if to take and release semaphore * * Returns an error code on error. - **/ -s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, - u16 reg, u16 *val) + */ +static s32 ixgbe_read_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val, bool lock) { u32 swfw_mask = hw->phy.phy_semaphore_mask; int max_retry = 10; @@ -124,7 +125,7 @@ s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, csum = ixgbe_ones_comp_byte_add(reg_high, reg & 0xFF); csum = ~csum; do { - if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) return IXGBE_ERR_SWFW_SYNC; ixgbe_i2c_start(hw); /* Device Address and write indication */ @@ -157,13 +158,15 @@ s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, if (ixgbe_clock_out_i2c_bit(hw, false)) goto fail; ixgbe_i2c_stop(hw); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); *val = (high_bits << 8) | low_bits; return 0; fail: ixgbe_i2c_bus_clear(hw); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); retry++; if (retry < max_retry) hw_dbg(hw, "I2C byte read combined error - Retry.\n"); @@ -175,17 +178,49 @@ fail: } /** - * ixgbe_write_i2c_combined_generic - Perform I2C write combined operation + * ixgbe_read_i2c_combined_generic - Perform I2C read combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to read from + * @reg: I2C device register to read from + * @val: pointer to location to receive read value + * + * Returns an error code on error. + */ +s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val) +{ + return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, true); +} + +/** + * ixgbe_read_i2c_combined_generic_unlocked - Unlocked I2C read combined + * @hw: pointer to the hardware structure + * @addr: I2C bus address to read from + * @reg: I2C device register to read from + * @val: pointer to location to receive read value + * + * Returns an error code on error. + */ +s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val) +{ + return ixgbe_read_i2c_combined_generic_int(hw, addr, reg, val, false); +} + +/** + * ixgbe_write_i2c_combined_generic_int - Perform I2C write combined operation * @hw: pointer to the hardware structure * @addr: I2C bus address to write to * @reg: I2C device register to write to * @val: value to write + * @lock: true if to take and release semaphore * * Returns an error code on error. - **/ -s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, - u8 addr, u16 reg, u16 val) + */ +static s32 ixgbe_write_i2c_combined_generic_int(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 val, bool lock) { + u32 swfw_mask = hw->phy.phy_semaphore_mask; int max_retry = 1; int retry = 0; u8 reg_high; @@ -197,6 +232,8 @@ s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, csum = ixgbe_ones_comp_byte_add(csum, val & 0xFF); csum = ~csum; do { + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + return IXGBE_ERR_SWFW_SYNC; ixgbe_i2c_start(hw); /* Device Address and write indication */ if (ixgbe_out_i2c_byte_ack(hw, addr)) @@ -217,10 +254,14 @@ s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, if (ixgbe_out_i2c_byte_ack(hw, csum)) goto fail; ixgbe_i2c_stop(hw); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); return 0; fail: ixgbe_i2c_bus_clear(hw); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); retry++; if (retry < max_retry) hw_dbg(hw, "I2C byte write combined error - Retry.\n"); @@ -232,6 +273,36 @@ fail: } /** + * ixgbe_write_i2c_combined_generic - Perform I2C write combined operation + * @hw: pointer to the hardware structure + * @addr: I2C bus address to write to + * @reg: I2C device register to write to + * @val: value to write + * + * Returns an error code on error. + */ +s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, + u8 addr, u16 reg, u16 val) +{ + return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, true); +} + +/** + * ixgbe_write_i2c_combined_generic_unlocked - Unlocked I2C write combined + * @hw: pointer to the hardware structure + * @addr: I2C bus address to write to + * @reg: I2C device register to write to + * @val: value to write + * + * Returns an error code on error. + */ +s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, + u8 addr, u16 reg, u16 val) +{ + return ixgbe_write_i2c_combined_generic_int(hw, addr, reg, val, false); +} + +/** * ixgbe_identify_phy_generic - Get physical layer module * @hw: pointer to hardware structure * @@ -1100,6 +1171,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) return IXGBE_ERR_SFP_NOT_PRESENT; } + /* LAN ID is needed for sfp_type determination */ + hw->mac.ops.set_lan_id(hw); + status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER, &identifier); @@ -1107,9 +1181,6 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) if (status) goto err_read_i2c_eeprom; - /* LAN ID is needed for sfp_type determination */ - hw->mac.ops.set_lan_id(hw); - if (identifier != IXGBE_SFF_IDENTIFIER_SFP) { hw->phy.type = ixgbe_phy_sfp_unsupported; return IXGBE_ERR_SFP_NOT_SUPPORTED; @@ -1159,7 +1230,7 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) hw->phy.sfp_type = ixgbe_sfp_type_lr; else hw->phy.sfp_type = ixgbe_sfp_type_unknown; - } else if (hw->mac.type == ixgbe_mac_82599EB) { + } else { if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) { if (hw->bus.lan_id == 0) hw->phy.sfp_type = @@ -1660,26 +1731,46 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, } /** - * ixgbe_read_i2c_byte_generic - Reads 8 bit word over I2C + * ixgbe_is_sfp_probe - Returns true if SFP is being detected + * @hw: pointer to hardware structure + * @offset: eeprom offset to be read + * @addr: I2C address to be read + */ +static bool ixgbe_is_sfp_probe(struct ixgbe_hw *hw, u8 offset, u8 addr) +{ + if (addr == IXGBE_I2C_EEPROM_DEV_ADDR && + offset == IXGBE_SFF_IDENTIFIER && + hw->phy.sfp_type == ixgbe_sfp_type_not_present) + return true; + return false; +} + +/** + * ixgbe_read_i2c_byte_generic_int - Reads 8 bit word over I2C * @hw: pointer to hardware structure * @byte_offset: byte offset to read * @data: value read + * @lock: true if to take and release semaphore * * Performs byte read operation to SFP module's EEPROM over I2C interface at * a specified device address. - **/ -s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 *data) + */ +static s32 ixgbe_read_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data, bool lock) { s32 status; u32 max_retry = 10; u32 retry = 0; u32 swfw_mask = hw->phy.phy_semaphore_mask; bool nack = true; + + if (ixgbe_is_sfp_probe(hw, byte_offset, dev_addr)) + max_retry = IXGBE_SFP_DETECT_RETRIES; + *data = 0; do { - if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) return IXGBE_ERR_SWFW_SYNC; ixgbe_i2c_start(hw); @@ -1721,12 +1812,16 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, goto fail; ixgbe_i2c_stop(hw); - break; + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + return 0; fail: ixgbe_i2c_bus_clear(hw); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); - msleep(100); + if (lock) { + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msleep(100); + } retry++; if (retry < max_retry) hw_dbg(hw, "I2C byte read error - Retrying.\n"); @@ -1735,29 +1830,60 @@ fail: } while (retry < max_retry); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); - return status; } /** - * ixgbe_write_i2c_byte_generic - Writes 8 bit word over I2C + * ixgbe_read_i2c_byte_generic - Reads 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to read + * @data: value read + * + * Performs byte read operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data) +{ + return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, true); +} + +/** + * ixgbe_read_i2c_byte_generic_unlocked - Reads 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to read + * @data: value read + * + * Performs byte read operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data) +{ + return ixgbe_read_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, false); +} + +/** + * ixgbe_write_i2c_byte_generic_int - Writes 8 bit word over I2C * @hw: pointer to hardware structure * @byte_offset: byte offset to write * @data: value to write + * @lock: true if to take and release semaphore * * Performs byte write operation to SFP module's EEPROM over I2C interface at * a specified device address. - **/ -s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, - u8 dev_addr, u8 data) + */ +static s32 ixgbe_write_i2c_byte_generic_int(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data, bool lock) { s32 status; u32 max_retry = 1; u32 retry = 0; u32 swfw_mask = hw->phy.phy_semaphore_mask; - if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) + if (lock && hw->mac.ops.acquire_swfw_sync(hw, swfw_mask)) return IXGBE_ERR_SWFW_SYNC; do { @@ -1788,7 +1914,9 @@ s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, goto fail; ixgbe_i2c_stop(hw); - break; + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + return 0; fail: ixgbe_i2c_bus_clear(hw); @@ -1799,21 +1927,57 @@ fail: hw_dbg(hw, "I2C byte write error.\n"); } while (retry < max_retry); - hw->mac.ops.release_swfw_sync(hw, swfw_mask); + if (lock) + hw->mac.ops.release_swfw_sync(hw, swfw_mask); return status; } /** + * ixgbe_write_i2c_byte_generic - Writes 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @data: value to write + * + * Performs byte write operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data) +{ + return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, true); +} + +/** + * ixgbe_write_i2c_byte_generic_unlocked - Writes 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @data: value to write + * + * Performs byte write operation to SFP module's EEPROM over I2C interface at + * a specified device address. + */ +s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data) +{ + return ixgbe_write_i2c_byte_generic_int(hw, byte_offset, dev_addr, + data, false); +} + +/** * ixgbe_i2c_start - Sets I2C start condition * @hw: pointer to hardware structure * * Sets I2C start condition (High -> Low on SDA while SCL is High) + * Set bit-bang mode on X550 hardware. **/ static void ixgbe_i2c_start(struct ixgbe_hw *hw) { u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + i2cctl |= IXGBE_I2C_BB_EN(hw); + /* Start condition must begin with data and clock high */ ixgbe_set_i2c_data(hw, &i2cctl, 1); ixgbe_raise_i2c_clk(hw, &i2cctl); @@ -1838,10 +2002,15 @@ static void ixgbe_i2c_start(struct ixgbe_hw *hw) * @hw: pointer to hardware structure * * Sets I2C stop condition (Low -> High on SDA while SCL is High) + * Disables bit-bang mode and negates data output enable on X550 + * hardware. **/ static void ixgbe_i2c_stop(struct ixgbe_hw *hw) { u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN(hw); + u32 bb_en_bit = IXGBE_I2C_BB_EN(hw); /* Stop condition must begin with data low and clock high */ ixgbe_set_i2c_data(hw, &i2cctl, 0); @@ -1854,6 +2023,13 @@ static void ixgbe_i2c_stop(struct ixgbe_hw *hw) /* bus free time between stop and start (4.7us)*/ udelay(IXGBE_I2C_T_BUF); + + if (bb_en_bit || data_oe_bit || clk_oe_bit) { + i2cctl &= ~bb_en_bit; + i2cctl |= data_oe_bit | clk_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); + IXGBE_WRITE_FLUSH(hw); + } } /** @@ -1868,6 +2044,7 @@ static s32 ixgbe_clock_in_i2c_byte(struct ixgbe_hw *hw, u8 *data) s32 i; bool bit = false; + *data = 0; for (i = 7; i >= 0; i--) { ixgbe_clock_in_i2c_bit(hw, &bit); *data |= bit << i; @@ -1901,6 +2078,7 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data) /* Release SDA line (set high) */ i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); i2cctl |= IXGBE_I2C_DATA_OUT(hw); + i2cctl |= IXGBE_I2C_DATA_OE_N_EN(hw); IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); IXGBE_WRITE_FLUSH(hw); @@ -1915,15 +2093,21 @@ static s32 ixgbe_clock_out_i2c_byte(struct ixgbe_hw *hw, u8 data) **/ static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw) { + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); s32 status = 0; u32 i = 0; u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); u32 timeout = 10; bool ack = true; + if (data_oe_bit) { + i2cctl |= IXGBE_I2C_DATA_OUT(hw); + i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); + IXGBE_WRITE_FLUSH(hw); + } ixgbe_raise_i2c_clk(hw, &i2cctl); - /* Minimum high period of clock is 4us */ udelay(IXGBE_I2C_T_HIGH); @@ -1961,7 +2145,14 @@ static s32 ixgbe_get_i2c_ack(struct ixgbe_hw *hw) static s32 ixgbe_clock_in_i2c_bit(struct ixgbe_hw *hw, bool *data) { u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + if (data_oe_bit) { + i2cctl |= IXGBE_I2C_DATA_OUT(hw); + i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), i2cctl); + IXGBE_WRITE_FLUSH(hw); + } ixgbe_raise_i2c_clk(hw, &i2cctl); /* Minimum high period of clock is 4us */ @@ -2016,13 +2207,20 @@ static s32 ixgbe_clock_out_i2c_bit(struct ixgbe_hw *hw, bool data) * @i2cctl: Current value of I2CCTL register * * Raises the I2C clock line '0'->'1' + * Negates the I2C clock output enable on X550 hardware. **/ static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) { + u32 clk_oe_bit = IXGBE_I2C_CLK_OE_N_EN(hw); u32 i = 0; u32 timeout = IXGBE_I2C_CLOCK_STRETCHING_TIMEOUT; u32 i2cctl_r = 0; + if (clk_oe_bit) { + *i2cctl |= clk_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); + } + for (i = 0; i < timeout; i++) { *i2cctl |= IXGBE_I2C_CLK_OUT(hw); IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); @@ -2042,11 +2240,13 @@ static void ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) * @i2cctl: Current value of I2CCTL register * * Lowers the I2C clock line '1'->'0' + * Asserts the I2C clock output enable on X550 hardware. **/ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) { *i2cctl &= ~IXGBE_I2C_CLK_OUT(hw); + *i2cctl &= ~IXGBE_I2C_CLK_OE_N_EN(hw); IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); IXGBE_WRITE_FLUSH(hw); @@ -2062,13 +2262,17 @@ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl) * @data: I2C data value (0 or 1) to set * * Sets the I2C data bit + * Asserts the I2C data output enable on X550 hardware. **/ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) { + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + if (data) *i2cctl |= IXGBE_I2C_DATA_OUT(hw); else *i2cctl &= ~IXGBE_I2C_DATA_OUT(hw); + *i2cctl &= ~data_oe_bit; IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); IXGBE_WRITE_FLUSH(hw); @@ -2076,6 +2280,14 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) /* Data rise/fall (1000ns/300ns) and set-up time (250ns) */ udelay(IXGBE_I2C_T_RISE + IXGBE_I2C_T_FALL + IXGBE_I2C_T_SU_DATA); + if (!data) /* Can't verify data in this case */ + return 0; + if (data_oe_bit) { + *i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); + IXGBE_WRITE_FLUSH(hw); + } + /* Verify data was set correctly */ *i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); if (data != ixgbe_get_i2c_data(hw, i2cctl)) { @@ -2092,9 +2304,19 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data) * @i2cctl: Current value of I2CCTL register * * Returns the I2C data bit value + * Negates the I2C data output enable on X550 hardware. **/ static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl) { + u32 data_oe_bit = IXGBE_I2C_DATA_OE_N_EN(hw); + + if (data_oe_bit) { + *i2cctl |= data_oe_bit; + IXGBE_WRITE_REG(hw, IXGBE_I2CCTL(hw), *i2cctl); + IXGBE_WRITE_FLUSH(hw); + udelay(IXGBE_I2C_T_FALL); + } + if (*i2cctl & IXGBE_I2C_DATA_IN(hw)) return true; return false; @@ -2109,10 +2331,11 @@ static bool ixgbe_get_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl) **/ static void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw) { - u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); + u32 i2cctl; u32 i; ixgbe_i2c_start(hw); + i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL(hw)); ixgbe_set_i2c_data(hw, &i2cctl, 1); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index e45988c4dad5..5abd66c84d00 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -66,6 +66,9 @@ #define IXGBE_SFF_1GBASET_CAPABLE 0x8 #define IXGBE_SFF_10GBASESR_CAPABLE 0x10 #define IXGBE_SFF_10GBASELR_CAPABLE 0x20 +#define IXGBE_SFF_SOFT_RS_SELECT_MASK 0x8 +#define IXGBE_SFF_SOFT_RS_SELECT_10G 0x8 +#define IXGBE_SFF_SOFT_RS_SELECT_1G 0x0 #define IXGBE_SFF_ADDRESSING_MODE 0x4 #define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE 0x1 #define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE 0x8 @@ -78,9 +81,29 @@ #define IXGBE_I2C_EEPROM_STATUS_FAIL 0x2 #define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS 0x3 #define IXGBE_CS4227 0xBE /* CS4227 address */ -#define IXGBE_CS4227_SPARE24_LSB 0x12B0 /* Reg to program EDC */ +#define IXGBE_CS4227_SCRATCH 2 +#define IXGBE_CS4227_RESET_PENDING 0x1357 +#define IXGBE_CS4227_RESET_COMPLETE 0x5AA5 +#define IXGBE_CS4227_RETRIES 15 +#define IXGBE_CS4227_EFUSE_STATUS 0x0181 +#define IXGBE_CS4227_LINE_SPARE22_MSB 0x12AD /* Reg to set speed */ +#define IXGBE_CS4227_LINE_SPARE24_LSB 0x12B0 /* Reg to set EDC */ +#define IXGBE_CS4227_HOST_SPARE22_MSB 0x1AAD /* Reg to set speed */ +#define IXGBE_CS4227_HOST_SPARE24_LSB 0x1AB0 /* Reg to program EDC */ +#define IXGBE_CS4227_EEPROM_STATUS 0x5001 +#define IXGBE_CS4227_EEPROM_LOAD_OK 0x0001 +#define IXGBE_CS4227_SPEED_1G 0x8000 +#define IXGBE_CS4227_SPEED_10G 0 #define IXGBE_CS4227_EDC_MODE_CX1 0x0002 #define IXGBE_CS4227_EDC_MODE_SR 0x0004 +#define IXGBE_CS4227_EDC_MODE_DIAG 0x0008 +#define IXGBE_CS4227_RESET_HOLD 500 /* microseconds */ +#define IXGBE_CS4227_RESET_DELAY 500 /* milliseconds */ +#define IXGBE_CS4227_CHECK_DELAY 30 /* milliseconds */ +#define IXGBE_PE 0xE0 /* Port expander addr */ +#define IXGBE_PE_OUTPUT 1 /* Output reg offset */ +#define IXGBE_PE_CONFIG 3 /* Config reg offset */ +#define IXGBE_PE_BIT1 (1 << 1) /* Flow control defines */ #define IXGBE_TAF_SYM_PAUSE 0x400 @@ -109,6 +132,8 @@ #define IXGBE_I2C_T_SU_STO 4 #define IXGBE_I2C_T_BUF 5 +#define IXGBE_SFP_DETECT_RETRIES 2 + #define IXGBE_TN_LASI_STATUS_REG 0x9005 #define IXGBE_TN_LASI_STATUS_TEMP_ALARM 0x0008 @@ -154,8 +179,12 @@ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, s32 ixgbe_tn_check_overtemp(struct ixgbe_hw *hw); s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data); +s32 ixgbe_read_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data); s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 data); +s32 ixgbe_write_i2c_byte_generic_unlocked(struct ixgbe_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data); s32 ixgbe_read_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 *eeprom_data); s32 ixgbe_read_i2c_sff8472_generic(struct ixgbe_hw *hw, u8 byte_offset, @@ -164,6 +193,10 @@ s32 ixgbe_write_i2c_eeprom_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 eeprom_data); s32 ixgbe_read_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 *val); +s32 ixgbe_read_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 *val); s32 ixgbe_write_i2c_combined_generic(struct ixgbe_hw *hw, u8 addr, u16 reg, u16 val); +s32 ixgbe_write_i2c_combined_generic_unlocked(struct ixgbe_hw *hw, u8 addr, + u16 reg, u16 val); #endif /* _IXGBE_PHY_H_ */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 63689192b149..995f03107eac 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -402,6 +402,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_FDIRSIP4M 0x0EE40 #define IXGBE_FDIRTCPM 0x0EE44 #define IXGBE_FDIRUDPM 0x0EE48 +#define IXGBE_FDIRSCTPM 0x0EE78 #define IXGBE_FDIRIP6M 0x0EE74 #define IXGBE_FDIRM 0x0EE70 @@ -1192,6 +1193,7 @@ struct ixgbe_thermal_sensor_data { /* RDRXCTL Bit Masks */ #define IXGBE_RDRXCTL_RDMTS_1_2 0x00000000 /* Rx Desc Min Threshold Size */ #define IXGBE_RDRXCTL_CRCSTRIP 0x00000002 /* CRC Strip */ +#define IXGBE_RDRXCTL_PSP 0x00000004 /* Pad small packet */ #define IXGBE_RDRXCTL_MVMEN 0x00000020 #define IXGBE_RDRXCTL_DMAIDONE 0x00000008 /* DMA init cycle done */ #define IXGBE_RDRXCTL_AGGDIS 0x00010000 /* Aggregation disable */ @@ -1750,6 +1752,9 @@ enum { * FCoE (0x8906): Filter 2 * 1588 (0x88f7): Filter 3 * FIP (0x8914): Filter 4 + * LLDP (0x88CC): Filter 5 + * LACP (0x8809): Filter 6 + * FC (0x8808): Filter 7 */ #define IXGBE_ETQF_FILTER_EAPOL 0 #define IXGBE_ETQF_FILTER_FCOE 2 @@ -1757,6 +1762,7 @@ enum { #define IXGBE_ETQF_FILTER_FIP 4 #define IXGBE_ETQF_FILTER_LLDP 5 #define IXGBE_ETQF_FILTER_LACP 6 +#define IXGBE_ETQF_FILTER_FC 7 /* VLAN Control Bit Masks */ #define IXGBE_VLNCTRL_VET 0x0000FFFF /* bits 0-15 */ @@ -1948,6 +1954,7 @@ enum { #define IXGBE_GSSR_SW_MNG_SM 0x0400 #define IXGBE_GSSR_SHARED_I2C_SM 0x1806 /* Wait for both phys & I2Cs */ #define IXGBE_GSSR_I2C_MASK 0x1800 +#define IXGBE_GSSR_NVM_PHY_MASK 0xF /* FW Status register bitmask */ #define IXGBE_FWSTS_FWRI 0x00000200 /* Firmware Reset Indication */ @@ -3255,9 +3262,11 @@ struct ixgbe_mac_operations { void (*flap_tx_laser)(struct ixgbe_hw *); void (*stop_link_on_d3)(struct ixgbe_hw *); s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); + s32 (*setup_mac_link)(struct ixgbe_hw *, ixgbe_link_speed, bool); s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool); s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *, bool *); + void (*set_rate_select_speed)(struct ixgbe_hw *, ixgbe_link_speed); /* Packet Buffer Manipulation */ void (*set_rxpba)(struct ixgbe_hw *, int, u32, int); @@ -3328,6 +3337,10 @@ struct ixgbe_phy_operations { s32 (*set_phy_power)(struct ixgbe_hw *, bool on); s32 (*enter_lplu)(struct ixgbe_hw *); s32 (*handle_lasi)(struct ixgbe_hw *hw); + s32 (*read_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + u16 *value); + s32 (*write_i2c_combined_unlocked)(struct ixgbe_hw *, u8 addr, u16 reg, + u16 value); }; struct ixgbe_eeprom_info { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 4e758435ece8..c1d4584f6469 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -567,19 +567,25 @@ static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw) **/ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) { - u32 swfw_sync; - u32 swmask = mask; - u32 fwmask = mask << 5; - u32 hwmask = 0; + u32 swmask = mask & IXGBE_GSSR_NVM_PHY_MASK; + u32 swi2c_mask = mask & IXGBE_GSSR_I2C_MASK; + u32 fwmask = swmask << 5; u32 timeout = 200; + u32 hwmask = 0; + u32 swfw_sync; u32 i; - if (swmask == IXGBE_GSSR_EEP_SM) + if (swmask & IXGBE_GSSR_EEP_SM) hwmask = IXGBE_GSSR_FLASH_SM; + /* SW only mask does not have FW bit pair */ + if (mask & IXGBE_GSSR_SW_MNG_SM) + swmask |= IXGBE_GSSR_SW_MNG_SM; + + swmask |= swi2c_mask; + fwmask |= swi2c_mask << 2; for (i = 0; i < timeout; i++) { - /* - * SW NVM semaphore bit is used for access to all + /* SW NVM semaphore bit is used for access to all * SW_FW_SYNC bits (not just NVM) */ if (ixgbe_get_swfw_sync_semaphore(hw)) @@ -590,39 +596,56 @@ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) swfw_sync |= swmask; IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); ixgbe_release_swfw_sync_semaphore(hw); - break; - } else { - /* - * Firmware currently using resource (fwmask), - * hardware currently using resource (hwmask), - * or other software thread currently using - * resource (swmask) - */ - ixgbe_release_swfw_sync_semaphore(hw); - usleep_range(5000, 10000); + usleep_range(5000, 6000); + return 0; } + /* Firmware currently using resource (fwmask), hardware + * currently using resource (hwmask), or other software + * thread currently using resource (swmask) + */ + ixgbe_release_swfw_sync_semaphore(hw); + usleep_range(5000, 10000); } - /* - * If the resource is not released by the FW/HW the SW can assume that - * the FW/HW malfunctions. In that case the SW should sets the - * SW bit(s) of the requested resource(s) while ignoring the - * corresponding FW/HW bits in the SW_FW_SYNC register. - */ - if (i >= timeout) { - swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw)); - if (swfw_sync & (fwmask | hwmask)) { - if (ixgbe_get_swfw_sync_semaphore(hw)) - return IXGBE_ERR_SWFW_SYNC; + /* Failed to get SW only semaphore */ + if (swmask == IXGBE_GSSR_SW_MNG_SM) { + hw_dbg(hw, "Failed to get SW only semaphore\n"); + return IXGBE_ERR_SWFW_SYNC; + } - swfw_sync |= swmask; - IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); - ixgbe_release_swfw_sync_semaphore(hw); - } + /* If the resource is not released by the FW/HW the SW can assume that + * the FW/HW malfunctions. In that case the SW should set the SW bit(s) + * of the requested resource(s) while ignoring the corresponding FW/HW + * bits in the SW_FW_SYNC register. + */ + if (ixgbe_get_swfw_sync_semaphore(hw)) + return IXGBE_ERR_SWFW_SYNC; + swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw)); + if (swfw_sync & (fwmask | hwmask)) { + swfw_sync |= swmask; + IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); + ixgbe_release_swfw_sync_semaphore(hw); + usleep_range(5000, 6000); + return 0; } + /* If the resource is not released by other SW the SW can assume that + * the other SW malfunctions. In that case the SW should clear all SW + * flags that it does not own and then repeat the whole process once + * again. + */ + if (swfw_sync & swmask) { + u32 rmask = IXGBE_GSSR_EEP_SM | IXGBE_GSSR_PHY0_SM | + IXGBE_GSSR_PHY1_SM | IXGBE_GSSR_MAC_CSR_SM; + + if (swi2c_mask) + rmask |= IXGBE_GSSR_I2C_MASK; + ixgbe_release_swfw_sync_X540(hw, rmask); + ixgbe_release_swfw_sync_semaphore(hw); + return IXGBE_ERR_SWFW_SYNC; + } + ixgbe_release_swfw_sync_semaphore(hw); - usleep_range(5000, 10000); - return 0; + return IXGBE_ERR_SWFW_SYNC; } /** @@ -635,9 +658,11 @@ s32 ixgbe_acquire_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) **/ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) { + u32 swmask = mask & (IXGBE_GSSR_NVM_PHY_MASK | IXGBE_GSSR_SW_MNG_SM); u32 swfw_sync; - u32 swmask = mask; + if (mask & IXGBE_GSSR_I2C_MASK) + swmask |= mask & IXGBE_GSSR_I2C_MASK; ixgbe_get_swfw_sync_semaphore(hw); swfw_sync = IXGBE_READ_REG(hw, IXGBE_SWFW_SYNC(hw)); @@ -645,7 +670,7 @@ void ixgbe_release_swfw_sync_X540(struct ixgbe_hw *hw, u32 mask) IXGBE_WRITE_REG(hw, IXGBE_SWFW_SYNC(hw), swfw_sync); ixgbe_release_swfw_sync_semaphore(hw); - usleep_range(5000, 10000); + usleep_range(5000, 6000); } /** @@ -686,6 +711,11 @@ static s32 ixgbe_get_swfw_sync_semaphore(struct ixgbe_hw *hw) usleep_range(50, 100); } + /* Release semaphores and return error if SW NVM semaphore + * was not granted because we do not have access to the EEPROM + */ + hw_dbg(hw, "REGSMP Software NVM semaphore not granted\n"); + ixgbe_release_swfw_sync_semaphore(hw); return IXGBE_ERR_EEPROM; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 9fe9445cd73b..ebe0ac950b14 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -56,6 +56,292 @@ static void ixgbe_setup_mux_ctl(struct ixgbe_hw *hw) IXGBE_WRITE_FLUSH(hw); } +/** + * ixgbe_read_cs4227 - Read CS4227 register + * @hw: pointer to hardware structure + * @reg: register number to write + * @value: pointer to receive value read + * + * Returns status code + */ +static s32 ixgbe_read_cs4227(struct ixgbe_hw *hw, u16 reg, u16 *value) +{ + return hw->phy.ops.read_i2c_combined_unlocked(hw, IXGBE_CS4227, reg, + value); +} + +/** + * ixgbe_write_cs4227 - Write CS4227 register + * @hw: pointer to hardware structure + * @reg: register number to write + * @value: value to write to register + * + * Returns status code + */ +static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value) +{ + return hw->phy.ops.write_i2c_combined_unlocked(hw, IXGBE_CS4227, reg, + value); +} + +/** + * ixgbe_check_cs4227_reg - Perform diag on a CS4227 register + * @hw: pointer to hardware structure + * @reg: the register to check + * + * Performs a diagnostic on a register in the CS4227 chip. Returns an error + * if it is not operating correctly. + * This function assumes that the caller has acquired the proper semaphore. + */ +static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg) +{ + s32 status; + u32 retry; + u16 reg_val; + + reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1; + status = ixgbe_write_cs4227(hw, reg, reg_val); + if (status) + return status; + for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) { + msleep(IXGBE_CS4227_CHECK_DELAY); + reg_val = 0xFFFF; + ixgbe_read_cs4227(hw, reg, ®_val); + if (!reg_val) + break; + } + if (reg_val) { + hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg); + return status; + } + + return 0; +} + +/** + * ixgbe_get_cs4227_status - Return CS4227 status + * @hw: pointer to hardware structure + * + * Performs a diagnostic on the CS4227 chip. Returns an error if it is + * not operating correctly. + * This function assumes that the caller has acquired the proper semaphore. + */ +static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw) +{ + s32 status; + u16 value = 0; + + /* Exit if the diagnostic has already been performed. */ + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value); + if (status) + return status; + if (value == IXGBE_CS4227_RESET_COMPLETE) + return 0; + + /* Check port 0. */ + status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB); + if (status) + return status; + + status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB); + if (status) + return status; + + /* Check port 1. */ + status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB + + (1 << 12)); + if (status) + return status; + + return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB + + (1 << 12)); +} + +/** + * ixgbe_read_pe - Read register from port expander + * @hw: pointer to hardware structure + * @reg: register number to read + * @value: pointer to receive read value + * + * Returns status code + */ +static s32 ixgbe_read_pe(struct ixgbe_hw *hw, u8 reg, u8 *value) +{ + s32 status; + + status = ixgbe_read_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, value); + if (status) + hw_err(hw, "port expander access failed with %d\n", status); + return status; +} + +/** + * ixgbe_write_pe - Write register to port expander + * @hw: pointer to hardware structure + * @reg: register number to write + * @value: value to write + * + * Returns status code + */ +static s32 ixgbe_write_pe(struct ixgbe_hw *hw, u8 reg, u8 value) +{ + s32 status; + + status = ixgbe_write_i2c_byte_generic_unlocked(hw, reg, IXGBE_PE, + value); + if (status) + hw_err(hw, "port expander access failed with %d\n", status); + return status; +} + +/** + * ixgbe_reset_cs4227 - Reset CS4227 using port expander + * @hw: pointer to hardware structure + * + * This function assumes that the caller has acquired the proper semaphore. + * Returns error code + */ +static s32 ixgbe_reset_cs4227(struct ixgbe_hw *hw) +{ + s32 status; + u32 retry; + u16 value; + u8 reg; + + /* Trigger hard reset. */ + status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®); + if (status) + return status; + reg |= IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg); + if (status) + return status; + + status = ixgbe_read_pe(hw, IXGBE_PE_CONFIG, ®); + if (status) + return status; + reg &= ~IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_CONFIG, reg); + if (status) + return status; + + status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®); + if (status) + return status; + reg &= ~IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg); + if (status) + return status; + + usleep_range(IXGBE_CS4227_RESET_HOLD, IXGBE_CS4227_RESET_HOLD + 100); + + status = ixgbe_read_pe(hw, IXGBE_PE_OUTPUT, ®); + if (status) + return status; + reg |= IXGBE_PE_BIT1; + status = ixgbe_write_pe(hw, IXGBE_PE_OUTPUT, reg); + if (status) + return status; + + /* Wait for the reset to complete. */ + msleep(IXGBE_CS4227_RESET_DELAY); + for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) { + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EFUSE_STATUS, + &value); + if (!status && value == IXGBE_CS4227_EEPROM_LOAD_OK) + break; + msleep(IXGBE_CS4227_CHECK_DELAY); + } + if (retry == IXGBE_CS4227_RETRIES) { + hw_err(hw, "CS4227 reset did not complete\n"); + return IXGBE_ERR_PHY; + } + + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_EEPROM_STATUS, &value); + if (status || !(value & IXGBE_CS4227_EEPROM_LOAD_OK)) { + hw_err(hw, "CS4227 EEPROM did not load successfully\n"); + return IXGBE_ERR_PHY; + } + + return 0; +} + +/** + * ixgbe_check_cs4227 - Check CS4227 and reset as needed + * @hw: pointer to hardware structure + */ +static void ixgbe_check_cs4227(struct ixgbe_hw *hw) +{ + u32 swfw_mask = hw->phy.phy_semaphore_mask; + s32 status; + u16 value; + u8 retry; + + for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) { + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_err(hw, "semaphore failed with %d\n", status); + msleep(IXGBE_CS4227_CHECK_DELAY); + continue; + } + + /* Get status of reset flow. */ + status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value); + if (!status && value == IXGBE_CS4227_RESET_COMPLETE) + goto out; + + if (status || value != IXGBE_CS4227_RESET_PENDING) + break; + + /* Reset is pending. Wait and check again. */ + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msleep(IXGBE_CS4227_CHECK_DELAY); + } + /* If still pending, assume other instance failed. */ + if (retry == IXGBE_CS4227_RETRIES) { + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_err(hw, "semaphore failed with %d\n", status); + return; + } + } + + /* Reset the CS4227. */ + status = ixgbe_reset_cs4227(hw); + if (status) { + hw_err(hw, "CS4227 reset failed: %d", status); + goto out; + } + + /* Reset takes so long, temporarily release semaphore in case the + * other driver instance is waiting for the reset indication. + */ + ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH, + IXGBE_CS4227_RESET_PENDING); + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + usleep_range(10000, 12000); + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_err(hw, "semaphore failed with %d", status); + return; + } + + /* Is the CS4227 working correctly? */ + status = ixgbe_get_cs4227_status(hw); + if (status) { + hw_err(hw, "CS4227 status failed: %d", status); + goto out; + } + + /* Record completion for next time. */ + status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH, + IXGBE_CS4227_RESET_COMPLETE); + +out: + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msleep(hw->eeprom.semaphore_delay); +} + /** ixgbe_identify_phy_x550em - Get PHY type based on device id * @hw: pointer to hardware structure * @@ -68,7 +354,7 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) /* set up for CS4227 usage */ hw->phy.phy_semaphore_mask = IXGBE_GSSR_SHARED_I2C_SM; ixgbe_setup_mux_ctl(hw); - + ixgbe_check_cs4227(hw); return ixgbe_identify_module_generic(hw); case IXGBE_DEV_ID_X550EM_X_KX4: hw->phy.type = ixgbe_phy_x550em_kx4; @@ -910,6 +1196,96 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) } /** + * ixgbe_supported_sfp_modules_X550em - Check if SFP module type is supported + * @hw: pointer to hardware structure + * @linear: true if SFP module is linear + */ +static s32 ixgbe_supported_sfp_modules_X550em(struct ixgbe_hw *hw, bool *linear) +{ + switch (hw->phy.sfp_type) { + case ixgbe_sfp_type_not_present: + return IXGBE_ERR_SFP_NOT_PRESENT; + case ixgbe_sfp_type_da_cu_core0: + case ixgbe_sfp_type_da_cu_core1: + *linear = true; + break; + case ixgbe_sfp_type_srlr_core0: + case ixgbe_sfp_type_srlr_core1: + case ixgbe_sfp_type_da_act_lmt_core0: + case ixgbe_sfp_type_da_act_lmt_core1: + case ixgbe_sfp_type_1g_sx_core0: + case ixgbe_sfp_type_1g_sx_core1: + case ixgbe_sfp_type_1g_lx_core0: + case ixgbe_sfp_type_1g_lx_core1: + *linear = false; + break; + case ixgbe_sfp_type_unknown: + case ixgbe_sfp_type_1g_cu_core0: + case ixgbe_sfp_type_1g_cu_core1: + default: + return IXGBE_ERR_SFP_NOT_SUPPORTED; + } + + return 0; +} + +/** + * ixgbe_setup_mac_link_sfp_x550em - Configure the KR PHY for SFP. + * @hw: pointer to hardware structure + * + * Configures the extern PHY and the integrated KR PHY for SFP support. + */ +static s32 +ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, + ixgbe_link_speed speed, + __always_unused bool autoneg_wait_to_complete) +{ + s32 status; + u16 slice, value; + bool setup_linear = false; + + /* Check if SFP module is supported and linear */ + status = ixgbe_supported_sfp_modules_X550em(hw, &setup_linear); + + /* If no SFP module present, then return success. Return success since + * there is no reason to configure CS4227 and SFP not present error is + * not accepted in the setup MAC link flow. + */ + if (status == IXGBE_ERR_SFP_NOT_PRESENT) + return 0; + + if (status) + return status; + + /* Configure CS4227 LINE side to 10G SR. */ + slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12); + value = IXGBE_CS4227_SPEED_10G; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, + value); + + /* Configure CS4227 for HOST connection rate then type. */ + slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12); + value = speed & IXGBE_LINK_SPEED_10GB_FULL ? + IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, + value); + + slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12); + if (setup_linear) + value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1; + else + value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, + value); + + /* If internal link mode is XFI, then setup XFI internal link. */ + if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) + status = ixgbe_setup_ixfi_x550em(hw, &speed); + + return status; +} + +/** * ixgbe_setup_mac_link_t_X550em - Sets the auto advertised link speed * @hw: pointer to hardware structure * @speed: new link speed @@ -1003,6 +1379,10 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) mac->ops.disable_tx_laser = NULL; mac->ops.enable_tx_laser = NULL; mac->ops.flap_tx_laser = NULL; + mac->ops.setup_link = ixgbe_setup_mac_link_multispeed_fiber; + mac->ops.setup_mac_link = ixgbe_setup_mac_link_sfp_x550em; + mac->ops.set_rate_select_speed = + ixgbe_set_soft_rate_select_speed; break; case ixgbe_media_type_copper: mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em; @@ -1018,53 +1398,18 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) */ static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) { - bool setup_linear; - u16 reg_slice, edc_mode; - s32 ret_val; + s32 status; + bool linear; - switch (hw->phy.sfp_type) { - case ixgbe_sfp_type_unknown: - return 0; - case ixgbe_sfp_type_not_present: - return IXGBE_ERR_SFP_NOT_PRESENT; - case ixgbe_sfp_type_da_cu_core0: - case ixgbe_sfp_type_da_cu_core1: - setup_linear = true; - break; - case ixgbe_sfp_type_srlr_core0: - case ixgbe_sfp_type_srlr_core1: - case ixgbe_sfp_type_da_act_lmt_core0: - case ixgbe_sfp_type_da_act_lmt_core1: - case ixgbe_sfp_type_1g_sx_core0: - case ixgbe_sfp_type_1g_sx_core1: - setup_linear = false; - break; - default: - return IXGBE_ERR_SFP_NOT_SUPPORTED; - } + /* Check if SFP module is supported */ + status = ixgbe_supported_sfp_modules_X550em(hw, &linear); + if (status) + return status; ixgbe_init_mac_link_ops_X550em(hw); hw->phy.ops.reset = NULL; - /* The CS4227 slice address is the base address + the port-pair reg - * offset. I.e. Slice 0 = 0x12B0 and slice 1 = 0x22B0. - */ - reg_slice = IXGBE_CS4227_SPARE24_LSB + (hw->bus.lan_id << 12); - - if (setup_linear) - edc_mode = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 0x1; - else - edc_mode = (IXGBE_CS4227_EDC_MODE_SR << 1) | 0x1; - - /* Configure CS4227 for connection type. */ - ret_val = hw->phy.ops.write_i2c_combined(hw, IXGBE_CS4227, reg_slice, - edc_mode); - - if (ret_val) - ret_val = hw->phy.ops.write_i2c_combined(hw, 0x80, reg_slice, - edc_mode); - - return ret_val; + return 0; } /** ixgbe_get_link_capabilities_x550em - Determines link capabilities @@ -1272,7 +1617,7 @@ static s32 ixgbe_handle_lasi_ext_t_x550em(struct ixgbe_hw *hw) if (status) return status; - if (lsc) + if (lsc && phy->ops.setup_internal_link) return phy->ops.setup_internal_link(hw); return 0; @@ -1927,6 +2272,62 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw, IXGBE_WRITE_REG(hw, IXGBE_PFFLPH, (u32)(pfflp >> 32)); } +/** + * ixgbe_set_mux - Set mux for port 1 access with CS4227 + * @hw: pointer to hardware structure + * @state: set mux if 1, clear if 0 + */ +static void ixgbe_set_mux(struct ixgbe_hw *hw, u8 state) +{ + u32 esdp; + + if (!hw->bus.lan_id) + return; + esdp = IXGBE_READ_REG(hw, IXGBE_ESDP); + if (state) + esdp |= IXGBE_ESDP_SDP1; + else + esdp &= ~IXGBE_ESDP_SDP1; + IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); + IXGBE_WRITE_FLUSH(hw); +} + +/** + * ixgbe_acquire_swfw_sync_X550em - Acquire SWFW semaphore + * @hw: pointer to hardware structure + * @mask: Mask to specify which semaphore to acquire + * + * Acquires the SWFW semaphore and sets the I2C MUX + */ +static s32 ixgbe_acquire_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask) +{ + s32 status; + + status = ixgbe_acquire_swfw_sync_X540(hw, mask); + if (status) + return status; + + if (mask & IXGBE_GSSR_I2C_MASK) + ixgbe_set_mux(hw, 1); + + return 0; +} + +/** + * ixgbe_release_swfw_sync_X550em - Release SWFW semaphore + * @hw: pointer to hardware structure + * @mask: Mask to specify which semaphore to release + * + * Releases the SWFW semaphore and sets the I2C MUX + */ +static void ixgbe_release_swfw_sync_X550em(struct ixgbe_hw *hw, u32 mask) +{ + if (mask & IXGBE_GSSR_I2C_MASK) + ixgbe_set_mux(hw, 0); + + ixgbe_release_swfw_sync_X540(hw, mask); +} + #define X550_COMMON_MAC \ .init_hw = &ixgbe_init_hw_generic, \ .start_hw = &ixgbe_start_hw_X540, \ @@ -1964,8 +2365,6 @@ static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw, &ixgbe_set_source_address_pruning_X550, \ .set_ethertype_anti_spoofing = \ &ixgbe_set_ethertype_anti_spoofing_X550, \ - .acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X540, \ - .release_swfw_sync = &ixgbe_release_swfw_sync_X540, \ .disable_rx_buff = &ixgbe_disable_rx_buff_generic, \ .enable_rx_buff = &ixgbe_enable_rx_buff_generic, \ .get_thermal_sensor_data = NULL, \ @@ -1985,6 +2384,8 @@ static struct ixgbe_mac_operations mac_ops_X550 = { .get_link_capabilities = &ixgbe_get_copper_link_capabilities_generic, .get_bus_info = &ixgbe_get_bus_info_generic, .setup_sfp = NULL, + .acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X540, + .release_swfw_sync = &ixgbe_release_swfw_sync_X540, }; static struct ixgbe_mac_operations mac_ops_X550EM_x = { @@ -1997,7 +2398,8 @@ static struct ixgbe_mac_operations mac_ops_X550EM_x = { .get_link_capabilities = &ixgbe_get_link_capabilities_X550em, .get_bus_info = &ixgbe_get_bus_info_X550em, .setup_sfp = ixgbe_setup_sfp_modules_X550em, - + .acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X550em, + .release_swfw_sync = &ixgbe_release_swfw_sync_X550em, }; #define X550_COMMON_EEP \ @@ -2039,14 +2441,17 @@ static struct ixgbe_phy_operations phy_ops_X550 = { X550_COMMON_PHY .init = NULL, .identify = &ixgbe_identify_phy_generic, - .read_i2c_combined = &ixgbe_read_i2c_combined_generic, - .write_i2c_combined = &ixgbe_write_i2c_combined_generic, }; static struct ixgbe_phy_operations phy_ops_X550EM_x = { X550_COMMON_PHY .init = &ixgbe_init_phy_ops_X550em, .identify = &ixgbe_identify_phy_x550em, + .read_i2c_combined = &ixgbe_read_i2c_combined_generic, + .write_i2c_combined = &ixgbe_write_i2c_combined_generic, + .read_i2c_combined_unlocked = &ixgbe_read_i2c_combined_generic_unlocked, + .write_i2c_combined_unlocked = + &ixgbe_write_i2c_combined_generic_unlocked, }; static const u32 ixgbe_mvals_X550[IXGBE_MVALS_IDX_LIMIT] = { diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 149a0b4489be..7570b5c7ccd8 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1008,7 +1008,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) container_of(napi, struct ixgbevf_q_vector, napi); struct ixgbevf_adapter *adapter = q_vector->adapter; struct ixgbevf_ring *ring; - int per_ring_budget; + int per_ring_budget, work_done = 0; bool clean_complete = true; ixgbevf_for_each_ring(ring, q_vector->tx) @@ -1027,10 +1027,12 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) else per_ring_budget = budget; - ixgbevf_for_each_ring(ring, q_vector->rx) - clean_complete &= (ixgbevf_clean_rx_irq(q_vector, ring, - per_ring_budget) - < per_ring_budget); + ixgbevf_for_each_ring(ring, q_vector->rx) { + int cleaned = ixgbevf_clean_rx_irq(q_vector, ring, + per_ring_budget); + work_done += cleaned; + clean_complete &= (cleaned < per_ring_budget); + } #ifdef CONFIG_NET_RX_BUSY_POLL ixgbevf_qv_unlock_napi(q_vector); @@ -1040,7 +1042,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) if (!clean_complete) return budget; /* all work done, exit the polling mode */ - napi_complete(napi); + napi_complete_done(napi, work_done); if (adapter->rx_itr_setting & 1) ixgbevf_set_itr(q_vector); if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && @@ -3896,6 +3898,7 @@ static const struct net_device_ops ixgbevf_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ixgbevf_netpoll, #endif + .ndo_features_check = passthru_features_check, }; static void ixgbevf_assign_netdev_ops(struct net_device *dev) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 960169efe636..603d29df5832 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1586,7 +1586,6 @@ static void mv643xx_eth_get_drvinfo(struct net_device *dev, sizeof(drvinfo->version)); strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); - drvinfo->n_stats = ARRAY_SIZE(mv643xx_eth_stats); } static int mv643xx_eth_nway_reset(struct net_device *dev) @@ -1845,29 +1844,19 @@ static void mv643xx_eth_program_multicast_filter(struct net_device *dev) struct netdev_hw_addr *ha; int i; - if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { - int port_num; - u32 accept; + if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) + goto promiscuous; -oom: - port_num = mp->port_num; - accept = 0x01010101; - for (i = 0; i < 0x100; i += 4) { - wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept); - wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept); - } - return; - } - - mc_spec = kzalloc(0x200, GFP_ATOMIC); - if (mc_spec == NULL) - goto oom; - mc_other = mc_spec + (0x100 >> 2); + /* Allocate both mc_spec and mc_other tables */ + mc_spec = kcalloc(128, sizeof(u32), GFP_ATOMIC); + if (!mc_spec) + goto promiscuous; + mc_other = &mc_spec[64]; netdev_for_each_mc_addr(ha, dev) { u8 *a = ha->addr; u32 *table; - int entry; + u8 entry; if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) { table = mc_spec; @@ -1880,12 +1869,23 @@ oom: table[entry >> 2] |= 1 << (8 * (entry & 3)); } - for (i = 0; i < 0x100; i += 4) { - wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, mc_spec[i >> 2]); - wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, mc_other[i >> 2]); + for (i = 0; i < 64; i++) { + wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i * sizeof(u32), + mc_spec[i]); + wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i * sizeof(u32), + mc_other[i]); } kfree(mc_spec); + return; + +promiscuous: + for (i = 0; i < 64; i++) { + wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i * sizeof(u32), + 0x01010101u); + wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i * sizeof(u32), + 0x01010101u); + } } static void mv643xx_eth_set_rx_mode(struct net_device *dev) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 514df76fc70f..dd6fe942acf9 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -32,6 +32,7 @@ #include <linux/of_address.h> #include <linux/phy.h> #include <linux/clk.h> +#include <linux/cpu.h> /* Registers */ #define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2)) @@ -285,23 +286,34 @@ struct mvneta_pcpu_stats { u64 tx_bytes; }; +struct mvneta_pcpu_port { + /* Pointer to the shared port */ + struct mvneta_port *pp; + + /* Pointer to the CPU-local NAPI struct */ + struct napi_struct napi; + + /* Cause of the previous interrupt */ + u32 cause_rx_tx; +}; + struct mvneta_port { + struct mvneta_pcpu_port __percpu *ports; + struct mvneta_pcpu_stats __percpu *stats; + int pkt_size; unsigned int frag_size; void __iomem *base; struct mvneta_rx_queue *rxqs; struct mvneta_tx_queue *txqs; struct net_device *dev; - - u32 cause_rx_tx; - struct napi_struct napi; + struct notifier_block cpu_notifier; /* Core clock */ struct clk *clk; u8 mcast_count[256]; u16 tx_ring_size; u16 rx_ring_size; - struct mvneta_pcpu_stats *stats; struct mii_bus *mii_bus; struct phy_device *phy_dev; @@ -468,7 +480,7 @@ struct mvneta_rx_queue { /* The hardware supports eight (8) rx queues, but we are only allowing * the first one to be used. Therefore, let's just allocate one queue. */ -static int rxq_number = 1; +static int rxq_number = 8; static int txq_number = 8; static int rxq_def; @@ -756,14 +768,7 @@ static void mvneta_port_up(struct mvneta_port *pp) mvreg_write(pp, MVNETA_TXQ_CMD, q_map); /* Enable all initialized RXQs. */ - q_map = 0; - for (queue = 0; queue < rxq_number; queue++) { - struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; - if (rxq->descs != NULL) - q_map |= (1 << queue); - } - - mvreg_write(pp, MVNETA_RXQ_CMD, q_map); + mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def)); } /* Stop the Ethernet port activity */ @@ -949,7 +954,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) /* Set CPU queue access map - all CPUs have access to all RX * queues and to all TX queues */ - for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) + for_each_present_cpu(cpu) mvreg_write(pp, MVNETA_CPU_MAP(cpu), (MVNETA_CPU_RXQ_ACCESS_ALL_MASK | MVNETA_CPU_TXQ_ACCESS_ALL_MASK)); @@ -1426,17 +1431,6 @@ static u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb) return MVNETA_TX_L4_CSUM_NOT; } -/* Returns rx queue pointer (find last set bit) according to causeRxTx - * value - */ -static struct mvneta_rx_queue *mvneta_rx_policy(struct mvneta_port *pp, - u32 cause) -{ - int queue = fls(cause >> 8) - 1; - - return (queue < 0 || queue >= rxq_number) ? NULL : &pp->rxqs[queue]; -} - /* Drop packets received by the RXQ and free buffers */ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp, struct mvneta_rx_queue *rxq) @@ -1461,6 +1455,7 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp, static int mvneta_rx(struct mvneta_port *pp, int rx_todo, struct mvneta_rx_queue *rxq) { + struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); struct net_device *dev = pp->dev; int rx_done; u32 rcvd_pkts = 0; @@ -1515,7 +1510,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, skb->protocol = eth_type_trans(skb, dev); mvneta_rx_csum(pp, rx_status, skb); - napi_gro_receive(&pp->napi, skb); + napi_gro_receive(&port->napi, skb); rcvd_pkts++; rcvd_bytes += rx_bytes; @@ -1550,7 +1545,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, mvneta_rx_csum(pp, rx_status, skb); - napi_gro_receive(&pp->napi, skb); + napi_gro_receive(&port->napi, skb); } if (rcvd_pkts) { @@ -2061,12 +2056,10 @@ static void mvneta_set_rx_mode(struct net_device *dev) /* Interrupt handling - the callback for request_irq() */ static irqreturn_t mvneta_isr(int irq, void *dev_id) { - struct mvneta_port *pp = (struct mvneta_port *)dev_id; + struct mvneta_pcpu_port *port = (struct mvneta_pcpu_port *)dev_id; - /* Mask all interrupts */ - mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); - - napi_schedule(&pp->napi); + disable_percpu_irq(port->pp->dev->irq); + napi_schedule(&port->napi); return IRQ_HANDLED; } @@ -2104,11 +2097,11 @@ static int mvneta_poll(struct napi_struct *napi, int budget) { int rx_done = 0; u32 cause_rx_tx; - unsigned long flags; struct mvneta_port *pp = netdev_priv(napi->dev); + struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); if (!netif_running(pp->dev)) { - napi_complete(napi); + napi_complete(&port->napi); return rx_done; } @@ -2135,47 +2128,17 @@ static int mvneta_poll(struct napi_struct *napi, int budget) /* For the case where the last mvneta_poll did not process all * RX packets */ - cause_rx_tx |= pp->cause_rx_tx; - if (rxq_number > 1) { - while ((cause_rx_tx & MVNETA_RX_INTR_MASK_ALL) && (budget > 0)) { - int count; - struct mvneta_rx_queue *rxq; - /* get rx queue number from cause_rx_tx */ - rxq = mvneta_rx_policy(pp, cause_rx_tx); - if (!rxq) - break; - - /* process the packet in that rx queue */ - count = mvneta_rx(pp, budget, rxq); - rx_done += count; - budget -= count; - if (budget > 0) { - /* set off the rx bit of the - * corresponding bit in the cause rx - * tx register, so that next iteration - * will find the next rx queue where - * packets are received on - */ - cause_rx_tx &= ~((1 << rxq->id) << 8); - } - } - } else { - rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]); - budget -= rx_done; - } + cause_rx_tx |= port->cause_rx_tx; + rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]); + budget -= rx_done; if (budget > 0) { cause_rx_tx = 0; - napi_complete(napi); - local_irq_save(flags); - mvreg_write(pp, MVNETA_INTR_NEW_MASK, - MVNETA_RX_INTR_MASK(rxq_number) | - MVNETA_TX_INTR_MASK(txq_number) | - MVNETA_MISCINTR_INTR_MASK); - local_irq_restore(flags); + napi_complete(&port->napi); + enable_percpu_irq(pp->dev->irq, 0); } - pp->cause_rx_tx = cause_rx_tx; + port->cause_rx_tx = cause_rx_tx; return rx_done; } @@ -2379,26 +2342,19 @@ static void mvneta_cleanup_txqs(struct mvneta_port *pp) /* Cleanup all Rx queues */ static void mvneta_cleanup_rxqs(struct mvneta_port *pp) { - int queue; - - for (queue = 0; queue < rxq_number; queue++) - mvneta_rxq_deinit(pp, &pp->rxqs[queue]); + mvneta_rxq_deinit(pp, &pp->rxqs[rxq_def]); } /* Init all Rx queues */ static int mvneta_setup_rxqs(struct mvneta_port *pp) { - int queue; - - for (queue = 0; queue < rxq_number; queue++) { - int err = mvneta_rxq_init(pp, &pp->rxqs[queue]); - if (err) { - netdev_err(pp->dev, "%s: can't create rxq=%d\n", - __func__, queue); - mvneta_cleanup_rxqs(pp); - return err; - } + int err = mvneta_rxq_init(pp, &pp->rxqs[rxq_def]); + if (err) { + netdev_err(pp->dev, "%s: can't create rxq=%d\n", + __func__, rxq_def); + mvneta_cleanup_rxqs(pp); + return err; } return 0; @@ -2424,6 +2380,8 @@ static int mvneta_setup_txqs(struct mvneta_port *pp) static void mvneta_start_dev(struct mvneta_port *pp) { + unsigned int cpu; + mvneta_max_rx_size_set(pp, pp->pkt_size); mvneta_txq_max_tx_size_set(pp, pp->pkt_size); @@ -2431,7 +2389,11 @@ static void mvneta_start_dev(struct mvneta_port *pp) mvneta_port_enable(pp); /* Enable polling on the port */ - napi_enable(&pp->napi); + for_each_present_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + napi_enable(&port->napi); + } /* Unmask interrupts */ mvreg_write(pp, MVNETA_INTR_NEW_MASK, @@ -2449,9 +2411,15 @@ static void mvneta_start_dev(struct mvneta_port *pp) static void mvneta_stop_dev(struct mvneta_port *pp) { + unsigned int cpu; + phy_stop(pp->phy_dev); - napi_disable(&pp->napi); + for_each_present_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + napi_disable(&port->napi); + } netif_carrier_off(pp->dev); @@ -2691,6 +2659,125 @@ static void mvneta_mdio_remove(struct mvneta_port *pp) pp->phy_dev = NULL; } +static void mvneta_percpu_enable(void *arg) +{ + struct mvneta_port *pp = arg; + + enable_percpu_irq(pp->dev->irq, IRQ_TYPE_NONE); +} + +static void mvneta_percpu_disable(void *arg) +{ + struct mvneta_port *pp = arg; + + disable_percpu_irq(pp->dev->irq); +} + +static void mvneta_percpu_elect(struct mvneta_port *pp) +{ + int online_cpu_idx, cpu, i = 0; + + online_cpu_idx = rxq_def % num_online_cpus(); + + for_each_online_cpu(cpu) { + if (i == online_cpu_idx) + /* Enable per-CPU interrupt on the one CPU we + * just elected + */ + smp_call_function_single(cpu, mvneta_percpu_enable, + pp, true); + else + /* Disable per-CPU interrupt on all the other CPU */ + smp_call_function_single(cpu, mvneta_percpu_disable, + pp, true); + i++; + } +}; + +static int mvneta_percpu_notifier(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + struct mvneta_port *pp = container_of(nfb, struct mvneta_port, + cpu_notifier); + int cpu = (unsigned long)hcpu, other_cpu; + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + netif_tx_stop_all_queues(pp->dev); + + /* We have to synchronise on tha napi of each CPU + * except the one just being waked up + */ + for_each_online_cpu(other_cpu) { + if (other_cpu != cpu) { + struct mvneta_pcpu_port *other_port = + per_cpu_ptr(pp->ports, other_cpu); + + napi_synchronize(&other_port->napi); + } + } + + /* Mask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); + napi_enable(&port->napi); + + /* Enable per-CPU interrupt on the one CPU we care + * about. + */ + mvneta_percpu_elect(pp); + + /* Unmask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + MVNETA_RX_INTR_MASK(rxq_number) | + MVNETA_TX_INTR_MASK(txq_number) | + MVNETA_MISCINTR_INTR_MASK); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, + MVNETA_CAUSE_PHY_STATUS_CHANGE | + MVNETA_CAUSE_LINK_CHANGE | + MVNETA_CAUSE_PSC_SYNC_CHANGE); + netif_tx_start_all_queues(pp->dev); + break; + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + netif_tx_stop_all_queues(pp->dev); + /* Mask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); + + napi_synchronize(&port->napi); + napi_disable(&port->napi); + /* Disable per-CPU interrupts on the CPU that is + * brought down. + */ + smp_call_function_single(cpu, mvneta_percpu_disable, + pp, true); + + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: + /* Check if a new CPU must be elected now this on is down */ + mvneta_percpu_elect(pp); + /* Unmask all ethernet port interrupts */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + MVNETA_RX_INTR_MASK(rxq_number) | + MVNETA_TX_INTR_MASK(txq_number) | + MVNETA_MISCINTR_INTR_MASK); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, + MVNETA_CAUSE_PHY_STATUS_CHANGE | + MVNETA_CAUSE_LINK_CHANGE | + MVNETA_CAUSE_PSC_SYNC_CHANGE); + netif_tx_start_all_queues(pp->dev); + break; + } + + return NOTIFY_OK; +} + static int mvneta_open(struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); @@ -2709,13 +2796,29 @@ static int mvneta_open(struct net_device *dev) goto err_cleanup_rxqs; /* Connect to port interrupt line */ - ret = request_irq(pp->dev->irq, mvneta_isr, 0, - MVNETA_DRIVER_NAME, pp); + ret = request_percpu_irq(pp->dev->irq, mvneta_isr, + MVNETA_DRIVER_NAME, pp->ports); if (ret) { netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq); goto err_cleanup_txqs; } + /* Even though the documentation says that request_percpu_irq + * doesn't enable the interrupts automatically, it actually + * does so on the local CPU. + * + * Make sure it's disabled. + */ + mvneta_percpu_disable(pp); + + /* Elect a CPU to handle our RX queue interrupt */ + mvneta_percpu_elect(pp); + + /* Register a CPU notifier to handle the case where our CPU + * might be taken offline. + */ + register_cpu_notifier(&pp->cpu_notifier); + /* In default link is down */ netif_carrier_off(pp->dev); @@ -2730,7 +2833,7 @@ static int mvneta_open(struct net_device *dev) return 0; err_free_irq: - free_irq(pp->dev->irq, pp); + free_percpu_irq(pp->dev->irq, pp->ports); err_cleanup_txqs: mvneta_cleanup_txqs(pp); err_cleanup_rxqs: @@ -2742,10 +2845,14 @@ err_cleanup_rxqs: static int mvneta_stop(struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); + int cpu; mvneta_stop_dev(pp); mvneta_mdio_remove(pp); - free_irq(dev->irq, pp); + unregister_cpu_notifier(&pp->cpu_notifier); + for_each_present_cpu(cpu) + smp_call_function_single(cpu, mvneta_percpu_disable, pp, true); + free_percpu_irq(dev->irq, pp->ports); mvneta_cleanup_rxqs(pp); mvneta_cleanup_txqs(pp); @@ -3032,14 +3139,7 @@ static int mvneta_probe(struct platform_device *pdev) const char *managed; int phy_mode; int err; - - /* Our multiqueue support is not complete, so for now, only - * allow the usage of the first RX queue - */ - if (rxq_def != 0) { - dev_err(&pdev->dev, "Invalid rxq_def argument: %d\n", rxq_def); - return -EINVAL; - } + int cpu; dev = alloc_etherdev_mqs(sizeof(struct mvneta_port), txq_number, rxq_number); if (!dev) @@ -3091,6 +3191,7 @@ static int mvneta_probe(struct platform_device *pdev) err = of_property_read_string(dn, "managed", &managed); pp->use_inband_status = (err == 0 && strcmp(managed, "in-band-status") == 0); + pp->cpu_notifier.notifier_call = mvneta_percpu_notifier; pp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(pp->clk)) { @@ -3107,11 +3208,18 @@ static int mvneta_probe(struct platform_device *pdev) goto err_clk; } + /* Alloc per-cpu port structure */ + pp->ports = alloc_percpu(struct mvneta_pcpu_port); + if (!pp->ports) { + err = -ENOMEM; + goto err_clk; + } + /* Alloc per-cpu stats */ pp->stats = netdev_alloc_pcpu_stats(struct mvneta_pcpu_stats); if (!pp->stats) { err = -ENOMEM; - goto err_clk; + goto err_free_ports; } dt_mac_addr = of_get_mac_address(dn); @@ -3152,7 +3260,12 @@ static int mvneta_probe(struct platform_device *pdev) if (dram_target_info) mvneta_conf_mbus_windows(pp, dram_target_info); - netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT); + for_each_present_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); + + netif_napi_add(dev, &port->napi, mvneta_poll, NAPI_POLL_WEIGHT); + port->pp = pp; + } dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; dev->hw_features |= dev->features; @@ -3183,6 +3296,8 @@ static int mvneta_probe(struct platform_device *pdev) err_free_stats: free_percpu(pp->stats); +err_free_ports: + free_percpu(pp->ports); err_clk: clk_disable_unprepare(pp->clk); err_put_phy_node: @@ -3202,6 +3317,7 @@ static int mvneta_remove(struct platform_device *pdev) unregister_netdev(dev); clk_disable_unprepare(pp->clk); + free_percpu(pp->ports); free_percpu(pp->stats); irq_dispose_mapping(dev->irq); of_node_put(pp->phy_node); diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index d9f4498832a1..5606a043063e 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4819,6 +4819,18 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port, memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); + /* if the address is invalid, use a random value */ + if (!is_valid_ether_addr(dev->dev_addr)) { + struct sockaddr sa = { AF_UNSPEC }; + + netdev_warn(dev, + "Invalid MAC address, defaulting to random\n"); + eth_hw_addr_random(dev); + memcpy(sa.sa_data, dev->dev_addr, ETH_ALEN); + if (sky2_set_mac_address(dev, &sa)) + netdev_warn(dev, "Failed to set MAC address.\n"); + } + return dev; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index f79d8124321e..ddb5541882f5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -95,9 +95,6 @@ mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) (u16) (mdev->dev->caps.fw_ver & 0xffff)); strlcpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } static const char mlx4_en_priv_flags[][ETH_GSTRING_LEN] = { diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 4726122ea76b..886e1bc86374 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -573,10 +573,8 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv) { struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_dev *dev = mdev->dev; - struct mlx4_mac_entry *entry; int index = 0; int err = 0; - u64 reg_id = 0; int *qpn = &priv->base_qpn; u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr); @@ -600,44 +598,11 @@ static int mlx4_en_get_qp(struct mlx4_en_priv *priv) en_dbg(DRV, priv, "Reserved qp %d\n", *qpn); if (err) { en_err(priv, "Failed to reserve qp for mac registration\n"); - goto qp_err; - } - - err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, ®_id); - if (err) - goto steer_err; - - err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn, - &priv->tunnel_reg_id); - if (err) - goto tunnel_err; - - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - err = -ENOMEM; - goto alloc_err; + mlx4_unregister_mac(dev, priv->port, mac); + return err; } - memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac)); - memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac)); - entry->reg_id = reg_id; - - hlist_add_head_rcu(&entry->hlist, - &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); return 0; - -alloc_err: - if (priv->tunnel_reg_id) - mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); -tunnel_err: - mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id); - -steer_err: - mlx4_qp_release_range(dev, *qpn, 1); - -qp_err: - mlx4_unregister_mac(dev, priv->port, mac); - return err; } static void mlx4_en_put_qp(struct mlx4_en_priv *priv) @@ -645,39 +610,13 @@ static void mlx4_en_put_qp(struct mlx4_en_priv *priv) struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_dev *dev = mdev->dev; int qpn = priv->base_qpn; - u64 mac; if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { - mac = mlx4_mac_to_u64(priv->dev->dev_addr); + u64 mac = mlx4_mac_to_u64(priv->dev->dev_addr); en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", priv->dev->dev_addr); mlx4_unregister_mac(dev, priv->port, mac); } else { - struct mlx4_mac_entry *entry; - struct hlist_node *tmp; - struct hlist_head *bucket; - unsigned int i; - - for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { - bucket = &priv->mac_hash[i]; - hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { - mac = mlx4_mac_to_u64(entry->mac); - en_dbg(DRV, priv, "Registering MAC: %pM for deleting\n", - entry->mac); - mlx4_en_uc_steer_release(priv, entry->mac, - qpn, entry->reg_id); - - mlx4_unregister_mac(dev, priv->port, mac); - hlist_del_rcu(&entry->hlist); - kfree_rcu(entry, rcu); - } - } - - if (priv->tunnel_reg_id) { - mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); - priv->tunnel_reg_id = 0; - } - en_dbg(DRV, priv, "Releasing qp: port %d, qpn %d\n", priv->port, qpn); mlx4_qp_release_range(dev, qpn, 1); @@ -1283,6 +1222,75 @@ static void mlx4_en_netpoll(struct net_device *dev) } #endif +static int mlx4_en_set_rss_steer_rules(struct mlx4_en_priv *priv) +{ + u64 reg_id; + int err = 0; + int *qpn = &priv->base_qpn; + struct mlx4_mac_entry *entry; + + err = mlx4_en_uc_steer_add(priv, priv->dev->dev_addr, qpn, ®_id); + if (err) + return err; + + err = mlx4_en_tunnel_steer_add(priv, priv->dev->dev_addr, *qpn, + &priv->tunnel_reg_id); + if (err) + goto tunnel_err; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + err = -ENOMEM; + goto alloc_err; + } + + memcpy(entry->mac, priv->dev->dev_addr, sizeof(entry->mac)); + memcpy(priv->current_mac, entry->mac, sizeof(priv->current_mac)); + entry->reg_id = reg_id; + hlist_add_head_rcu(&entry->hlist, + &priv->mac_hash[entry->mac[MLX4_EN_MAC_HASH_IDX]]); + + return 0; + +alloc_err: + if (priv->tunnel_reg_id) + mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); + +tunnel_err: + mlx4_en_uc_steer_release(priv, priv->dev->dev_addr, *qpn, reg_id); + return err; +} + +static void mlx4_en_delete_rss_steer_rules(struct mlx4_en_priv *priv) +{ + u64 mac; + unsigned int i; + int qpn = priv->base_qpn; + struct hlist_head *bucket; + struct hlist_node *tmp; + struct mlx4_mac_entry *entry; + + for (i = 0; i < MLX4_EN_MAC_HASH_SIZE; ++i) { + bucket = &priv->mac_hash[i]; + hlist_for_each_entry_safe(entry, tmp, bucket, hlist) { + mac = mlx4_mac_to_u64(entry->mac); + en_dbg(DRV, priv, "Registering MAC:%pM for deleting\n", + entry->mac); + mlx4_en_uc_steer_release(priv, entry->mac, + qpn, entry->reg_id); + + mlx4_unregister_mac(priv->mdev->dev, priv->port, mac); + hlist_del_rcu(&entry->hlist); + kfree_rcu(entry, rcu); + } + } + + if (priv->tunnel_reg_id) { + mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); + priv->tunnel_reg_id = 0; + } +} + static void mlx4_en_tx_timeout(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -1684,6 +1692,11 @@ int mlx4_en_start_port(struct net_device *dev) goto tx_err; } + /* Set Unicast and VXLAN steering rules */ + if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0 && + mlx4_en_set_rss_steer_rules(priv)) + mlx4_warn(mdev, "Failed setting steering rules\n"); + /* Attach rx QP to bradcast address */ eth_broadcast_addr(&mc_list[10]); mc_list[5] = priv->port; /* needed for B0 steering support */ @@ -1831,6 +1844,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) for (i = 0; i < priv->tx_ring_num; i++) mlx4_en_free_tx_buf(dev, priv->tx_ring[i]); + if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0) + mlx4_en_delete_rss_steer_rules(priv); + /* Free RSS qps */ mlx4_en_release_rss_steer(priv); @@ -2800,7 +2816,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, struct mlx4_en_priv *priv; int i; int err; - u64 mac_u64; dev = alloc_etherdev_mqs(sizeof(struct mlx4_en_priv), MAX_TX_RINGS, MAX_RX_RINGS); @@ -2892,17 +2907,17 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, dev->addr_len = ETH_ALEN; mlx4_en_u64_to_mac(dev->dev_addr, mdev->dev->caps.def_mac[priv->port]); if (!is_valid_ether_addr(dev->dev_addr)) { - if (mlx4_is_slave(priv->mdev->dev)) { - eth_hw_addr_random(dev); - en_warn(priv, "Assigned random MAC address %pM\n", dev->dev_addr); - mac_u64 = mlx4_mac_to_u64(dev->dev_addr); - mdev->dev->caps.def_mac[priv->port] = mac_u64; - } else { - en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n", - priv->port, dev->dev_addr); - err = -EINVAL; - goto out; - } + en_err(priv, "Port: %d, invalid mac burned: %pM, quiting\n", + priv->port, dev->dev_addr); + err = -EINVAL; + goto out; + } else if (mlx4_is_slave(priv->mdev->dev) && + (priv->mdev->dev->port_random_macs & 1 << priv->port)) { + /* Random MAC was assigned in mlx4_slave_cap + * in mlx4_core module + */ + dev->addr_assign_type |= NET_ADDR_RANDOM; + en_warn(priv, "Assigned random MAC address %pM\n", dev->dev_addr); } memcpy(priv->current_mac, dev->dev_addr, sizeof(priv->current_mac)); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index e8ec1dec5789..f13a4d7bbf95 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -2840,3 +2840,19 @@ int set_phv_bit(struct mlx4_dev *dev, u8 port, int new_val) return -EOPNOTSUPP; } EXPORT_SYMBOL(set_phv_bit); + +void mlx4_replace_zero_macs(struct mlx4_dev *dev) +{ + int i; + u8 mac_addr[ETH_ALEN]; + + dev->port_random_macs = 0; + for (i = 1; i <= dev->caps.num_ports; ++i) + if (!dev->caps.def_mac[i] && + dev->caps.port_type[i] == MLX4_PORT_TYPE_ETH) { + eth_random_addr(mac_addr); + dev->port_random_macs |= 1 << i; + dev->caps.def_mac[i] = mlx4_mac_to_u64(mac_addr); + } +} +EXPORT_SYMBOL_GPL(mlx4_replace_zero_macs); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index cc3a9897574c..85f1b1e7e505 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -863,6 +863,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) return -ENODEV; } + mlx4_replace_zero_macs(dev); + dev->caps.qp0_qkey = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 232b2b55f23b..e1cf9036af22 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1378,6 +1378,8 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work); void mlx4_init_quotas(struct mlx4_dev *dev); +/* for VFs, replace zero MACs with randomly-generated MACs at driver start */ +void mlx4_replace_zero_macs(struct mlx4_dev *dev); int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port); /* Returns the VF index of slave */ int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave); diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 78f51e103880..93195191f45b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -318,7 +318,7 @@ int mlx4_mr_hw_get_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr, key, NULL); } else { mailbox = mlx4_alloc_cmd_mailbox(dev); - if (IS_ERR_OR_NULL(mailbox)) + if (IS_ERR(mailbox)) return PTR_ERR(mailbox); err = mlx4_cmd_box(dev, 0, mailbox->dma, key, diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 20268634a9ab..3311f35d08e0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -422,15 +422,15 @@ int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn, u64 qp_mask = 0; int err = 0; + if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) + return -EINVAL; + mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); cmd = (struct mlx4_update_qp_context *)mailbox->buf; - if (!attr || (attr & ~MLX4_UPDATE_QP_SUPPORTED_ATTRS)) - return -EINVAL; - if (attr & MLX4_UPDATE_QP_SMAC) { pri_addr_path_mask |= 1ULL << MLX4_UPD_QP_PATH_MASK_MAC_INDEX; cmd->qp_context.pri_path.grh_mylmc = params->smac_index; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 731423ca575d..ac4b99ab1f85 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1238,8 +1238,10 @@ static int add_res_range(struct mlx4_dev *dev, int slave, u64 base, int count, return 0; undo: - for (--i; i >= base; --i) + for (--i; i >= 0; --i) { rb_erase(&res_arr[i]->node, root); + list_del_init(&res_arr[i]->list); + } spin_unlock_irq(mlx4_tlock(dev)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 75ff58dc1ff5..fabfc9e0a948 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -254,6 +254,156 @@ static void dump_buf(void *buf, int size, int data_only, int offset) pr_debug("\n"); } +enum { + MLX5_DRIVER_STATUS_ABORTED = 0xfe, + MLX5_DRIVER_SYND = 0xbadd00de, +}; + +static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, + u32 *synd, u8 *status) +{ + *synd = 0; + *status = 0; + + switch (op) { + case MLX5_CMD_OP_TEARDOWN_HCA: + case MLX5_CMD_OP_DISABLE_HCA: + case MLX5_CMD_OP_MANAGE_PAGES: + case MLX5_CMD_OP_DESTROY_MKEY: + case MLX5_CMD_OP_DESTROY_EQ: + case MLX5_CMD_OP_DESTROY_CQ: + case MLX5_CMD_OP_DESTROY_QP: + case MLX5_CMD_OP_DESTROY_PSV: + case MLX5_CMD_OP_DESTROY_SRQ: + case MLX5_CMD_OP_DESTROY_XRC_SRQ: + case MLX5_CMD_OP_DESTROY_DCT: + case MLX5_CMD_OP_DEALLOC_Q_COUNTER: + case MLX5_CMD_OP_DEALLOC_PD: + case MLX5_CMD_OP_DEALLOC_UAR: + case MLX5_CMD_OP_DETTACH_FROM_MCG: + case MLX5_CMD_OP_DEALLOC_XRCD: + case MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN: + case MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT: + case MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY: + case MLX5_CMD_OP_DESTROY_TIR: + case MLX5_CMD_OP_DESTROY_SQ: + case MLX5_CMD_OP_DESTROY_RQ: + case MLX5_CMD_OP_DESTROY_RMP: + case MLX5_CMD_OP_DESTROY_TIS: + case MLX5_CMD_OP_DESTROY_RQT: + case MLX5_CMD_OP_DESTROY_FLOW_TABLE: + case MLX5_CMD_OP_DESTROY_FLOW_GROUP: + case MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY: + return MLX5_CMD_STAT_OK; + + case MLX5_CMD_OP_QUERY_HCA_CAP: + case MLX5_CMD_OP_QUERY_ADAPTER: + case MLX5_CMD_OP_INIT_HCA: + case MLX5_CMD_OP_ENABLE_HCA: + case MLX5_CMD_OP_QUERY_PAGES: + case MLX5_CMD_OP_SET_HCA_CAP: + case MLX5_CMD_OP_QUERY_ISSI: + case MLX5_CMD_OP_SET_ISSI: + case MLX5_CMD_OP_CREATE_MKEY: + case MLX5_CMD_OP_QUERY_MKEY: + case MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS: + case MLX5_CMD_OP_PAGE_FAULT_RESUME: + case MLX5_CMD_OP_CREATE_EQ: + case MLX5_CMD_OP_QUERY_EQ: + case MLX5_CMD_OP_GEN_EQE: + case MLX5_CMD_OP_CREATE_CQ: + case MLX5_CMD_OP_QUERY_CQ: + case MLX5_CMD_OP_MODIFY_CQ: + case MLX5_CMD_OP_CREATE_QP: + case MLX5_CMD_OP_RST2INIT_QP: + case MLX5_CMD_OP_INIT2RTR_QP: + case MLX5_CMD_OP_RTR2RTS_QP: + case MLX5_CMD_OP_RTS2RTS_QP: + case MLX5_CMD_OP_SQERR2RTS_QP: + case MLX5_CMD_OP_2ERR_QP: + case MLX5_CMD_OP_2RST_QP: + case MLX5_CMD_OP_QUERY_QP: + case MLX5_CMD_OP_SQD_RTS_QP: + case MLX5_CMD_OP_INIT2INIT_QP: + case MLX5_CMD_OP_CREATE_PSV: + case MLX5_CMD_OP_CREATE_SRQ: + case MLX5_CMD_OP_QUERY_SRQ: + case MLX5_CMD_OP_ARM_RQ: + case MLX5_CMD_OP_CREATE_XRC_SRQ: + case MLX5_CMD_OP_QUERY_XRC_SRQ: + case MLX5_CMD_OP_ARM_XRC_SRQ: + case MLX5_CMD_OP_CREATE_DCT: + case MLX5_CMD_OP_DRAIN_DCT: + case MLX5_CMD_OP_QUERY_DCT: + case MLX5_CMD_OP_ARM_DCT_FOR_KEY_VIOLATION: + case MLX5_CMD_OP_QUERY_VPORT_STATE: + case MLX5_CMD_OP_MODIFY_VPORT_STATE: + case MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT: + case MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT: + case MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT: + case MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT: + case MLX5_CMD_OP_QUERY_ROCE_ADDRESS: + case MLX5_CMD_OP_SET_ROCE_ADDRESS: + case MLX5_CMD_OP_QUERY_HCA_VPORT_CONTEXT: + case MLX5_CMD_OP_MODIFY_HCA_VPORT_CONTEXT: + case MLX5_CMD_OP_QUERY_HCA_VPORT_GID: + case MLX5_CMD_OP_QUERY_HCA_VPORT_PKEY: + case MLX5_CMD_OP_QUERY_VPORT_COUNTER: + case MLX5_CMD_OP_ALLOC_Q_COUNTER: + case MLX5_CMD_OP_QUERY_Q_COUNTER: + case MLX5_CMD_OP_ALLOC_PD: + case MLX5_CMD_OP_ALLOC_UAR: + case MLX5_CMD_OP_CONFIG_INT_MODERATION: + case MLX5_CMD_OP_ACCESS_REG: + case MLX5_CMD_OP_ATTACH_TO_MCG: + case MLX5_CMD_OP_GET_DROPPED_PACKET_LOG: + case MLX5_CMD_OP_MAD_IFC: + case MLX5_CMD_OP_QUERY_MAD_DEMUX: + case MLX5_CMD_OP_SET_MAD_DEMUX: + case MLX5_CMD_OP_NOP: + case MLX5_CMD_OP_ALLOC_XRCD: + case MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN: + case MLX5_CMD_OP_QUERY_CONG_STATUS: + case MLX5_CMD_OP_MODIFY_CONG_STATUS: + case MLX5_CMD_OP_QUERY_CONG_PARAMS: + case MLX5_CMD_OP_MODIFY_CONG_PARAMS: + case MLX5_CMD_OP_QUERY_CONG_STATISTICS: + case MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT: + case MLX5_CMD_OP_SET_L2_TABLE_ENTRY: + case MLX5_CMD_OP_QUERY_L2_TABLE_ENTRY: + case MLX5_CMD_OP_CREATE_TIR: + case MLX5_CMD_OP_MODIFY_TIR: + case MLX5_CMD_OP_QUERY_TIR: + case MLX5_CMD_OP_CREATE_SQ: + case MLX5_CMD_OP_MODIFY_SQ: + case MLX5_CMD_OP_QUERY_SQ: + case MLX5_CMD_OP_CREATE_RQ: + case MLX5_CMD_OP_MODIFY_RQ: + case MLX5_CMD_OP_QUERY_RQ: + case MLX5_CMD_OP_CREATE_RMP: + case MLX5_CMD_OP_MODIFY_RMP: + case MLX5_CMD_OP_QUERY_RMP: + case MLX5_CMD_OP_CREATE_TIS: + case MLX5_CMD_OP_MODIFY_TIS: + case MLX5_CMD_OP_QUERY_TIS: + case MLX5_CMD_OP_CREATE_RQT: + case MLX5_CMD_OP_MODIFY_RQT: + case MLX5_CMD_OP_QUERY_RQT: + case MLX5_CMD_OP_CREATE_FLOW_TABLE: + case MLX5_CMD_OP_QUERY_FLOW_TABLE: + case MLX5_CMD_OP_CREATE_FLOW_GROUP: + case MLX5_CMD_OP_QUERY_FLOW_GROUP: + case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY: + case MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY: + *status = MLX5_DRIVER_STATUS_ABORTED; + *synd = MLX5_DRIVER_SYND; + return -EIO; + default: + mlx5_core_err(dev, "Unknown FW command (%d)\n", op); + return -EINVAL; + } +} + const char *mlx5_command_str(int command) { switch (command) { @@ -473,6 +623,7 @@ static void cmd_work_handler(struct work_struct *work) struct mlx5_core_dev *dev = container_of(cmd, struct mlx5_core_dev, cmd); struct mlx5_cmd_layout *lay; struct semaphore *sem; + unsigned long flags; sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; down(sem); @@ -485,6 +636,9 @@ static void cmd_work_handler(struct work_struct *work) } } else { ent->idx = cmd->max_reg_cmds; + spin_lock_irqsave(&cmd->alloc_lock, flags); + clear_bit(ent->idx, &cmd->bitmask); + spin_unlock_irqrestore(&cmd->alloc_lock, flags); } ent->token = alloc_token(cmd); @@ -584,6 +738,16 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent) return err; } +static __be32 *get_synd_ptr(struct mlx5_outbox_hdr *out) +{ + return &out->syndrome; +} + +static u8 *get_status_ptr(struct mlx5_outbox_hdr *out) +{ + return &out->status; +} + /* Notes: * 1. Callback functions may not sleep * 2. page queue commands do not support asynchrous completion @@ -1081,7 +1245,7 @@ static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg) } } -void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) +void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec) { struct mlx5_cmd *cmd = &dev->cmd; struct mlx5_cmd_work_ent *ent; @@ -1092,7 +1256,10 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) s64 ds; struct mlx5_cmd_stats *stats; unsigned long flags; + unsigned long vector; + /* there can be at most 32 command queues */ + vector = vec & 0xffffffff; for (i = 0; i < (1 << cmd->log_sz); i++) { if (test_bit(i, &vector)) { struct semaphore *sem; @@ -1110,11 +1277,16 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) ent->ret = verify_signature(ent); else ent->ret = 0; - ent->status = ent->lay->status_own >> 1; + if (vec & MLX5_TRIGGERED_CMD_COMP) + ent->status = MLX5_DRIVER_STATUS_ABORTED; + else + ent->status = ent->lay->status_own >> 1; + mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n", ent->ret, deliv_status_to_str(ent->status), ent->status); } free_ent(cmd, ent->idx); + if (ent->callback) { ds = ent->ts2 - ent->ts1; if (ent->op < ARRAY_SIZE(cmd->stats)) { @@ -1136,6 +1308,7 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, unsigned long vector) mlx5_free_cmd_msg(dev, ent->out); free_msg(dev, ent->in); + err = err ? err : ent->status; free_cmd(ent); callback(err, context); } else { @@ -1183,6 +1356,11 @@ static struct mlx5_cmd_msg *alloc_msg(struct mlx5_core_dev *dev, int in_size, return msg; } +static u16 opcode_from_in(struct mlx5_inbox_hdr *in) +{ + return be16_to_cpu(in->opcode); +} + static int is_manage_pages(struct mlx5_inbox_hdr *in) { return be16_to_cpu(in->opcode) == MLX5_CMD_OP_MANAGE_PAGES; @@ -1197,6 +1375,15 @@ static int cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, gfp_t gfp; int err; u8 status = 0; + u32 drv_synd; + + if (pci_channel_offline(dev->pdev) || + dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + err = mlx5_internal_err_ret_value(dev, opcode_from_in(in), &drv_synd, &status); + *get_synd_ptr(out) = cpu_to_be32(drv_synd); + *get_status_ptr(out) = status; + return err; + } pages_queue = is_manage_pages(in); gfp = callback ? GFP_ATOMIC : GFP_KERNEL; @@ -1363,6 +1550,7 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev) int err; int i; + memset(cmd, 0, sizeof(*cmd)); cmd_if_rev = cmdif_rev(dev); if (cmd_if_rev != CMD_IF_REV) { dev_err(&dev->pdev->dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c index 04ab7e445eae..b51e42d6fbec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -242,6 +242,7 @@ int mlx5_init_cq_table(struct mlx5_core_dev *dev) struct mlx5_cq_table *table = &dev->priv.cq_table; int err; + memset(table, 0, sizeof(*table)); spin_lock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); err = mlx5_cq_debugfs_init(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 59874d666cff..bb801a9bbd42 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1367,13 +1367,13 @@ int mlx5e_open_locked(struct net_device *netdev) err = mlx5e_set_dev_port_mtu(netdev); if (err) - return err; + goto err_clear_state_opened_flag; err = mlx5e_open_channels(priv); if (err) { netdev_err(netdev, "%s: mlx5e_open_channels failed, %d\n", __func__, err); - return err; + goto err_clear_state_opened_flag; } mlx5e_update_carrier(priv); @@ -1382,6 +1382,10 @@ int mlx5e_open_locked(struct net_device *netdev) schedule_delayed_work(&priv->update_stats_work, 0); return 0; + +err_clear_state_opened_flag: + clear_bit(MLX5E_STATE_OPENED, &priv->state); + return err; } static int mlx5e_open(struct net_device *netdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index a40b96d4c662..1f01fe8fde42 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -346,6 +346,7 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx, int inlen; eq->nent = roundup_pow_of_two(nent + MLX5_NUM_SPARE_EQE); + eq->cons_index = 0; err = mlx5_buf_alloc(dev, eq->nent * MLX5_EQE_SIZE, &eq->buf); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index 292d76f2a904..f5deb642d0d6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -34,6 +34,7 @@ #include <linux/module.h> #include <linux/random.h> #include <linux/vmalloc.h> +#include <linux/hardirq.h> #include <linux/mlx5/driver.h> #include <linux/mlx5/cmd.h> #include "mlx5_core.h" @@ -46,39 +47,113 @@ enum { enum { MLX5_HEALTH_SYNDR_FW_ERR = 0x1, MLX5_HEALTH_SYNDR_IRISC_ERR = 0x7, + MLX5_HEALTH_SYNDR_HW_UNRECOVERABLE_ERR = 0x8, MLX5_HEALTH_SYNDR_CRC_ERR = 0x9, MLX5_HEALTH_SYNDR_FETCH_PCI_ERR = 0xa, MLX5_HEALTH_SYNDR_HW_FTL_ERR = 0xb, MLX5_HEALTH_SYNDR_ASYNC_EQ_OVERRUN_ERR = 0xc, MLX5_HEALTH_SYNDR_EQ_ERR = 0xd, + MLX5_HEALTH_SYNDR_EQ_INV = 0xe, MLX5_HEALTH_SYNDR_FFSER_ERR = 0xf, + MLX5_HEALTH_SYNDR_HIGH_TEMP = 0x10 }; -static DEFINE_SPINLOCK(health_lock); -static LIST_HEAD(health_list); -static struct work_struct health_work; +enum { + MLX5_NIC_IFC_FULL = 0, + MLX5_NIC_IFC_DISABLED = 1, + MLX5_NIC_IFC_NO_DRAM_NIC = 2 +}; -static void health_care(struct work_struct *work) +static u8 get_nic_interface(struct mlx5_core_dev *dev) { - struct mlx5_core_health *health, *n; - struct mlx5_core_dev *dev; - struct mlx5_priv *priv; - LIST_HEAD(tlist); + return (ioread32be(&dev->iseg->cmdq_addr_l_sz) >> 8) & 3; +} + +static void trigger_cmd_completions(struct mlx5_core_dev *dev) +{ + unsigned long flags; + u64 vector; - spin_lock_irq(&health_lock); - list_splice_init(&health_list, &tlist); + /* wait for pending handlers to complete */ + synchronize_irq(dev->priv.msix_arr[MLX5_EQ_VEC_CMD].vector); + spin_lock_irqsave(&dev->cmd.alloc_lock, flags); + vector = ~dev->cmd.bitmask & ((1ul << (1 << dev->cmd.log_sz)) - 1); + if (!vector) + goto no_trig; + + vector |= MLX5_TRIGGERED_CMD_COMP; + spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags); + + mlx5_core_dbg(dev, "vector 0x%llx\n", vector); + mlx5_cmd_comp_handler(dev, vector); + return; + +no_trig: + spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags); +} + +static int in_fatal(struct mlx5_core_dev *dev) +{ + struct mlx5_core_health *health = &dev->priv.health; + struct health_buffer __iomem *h = health->health; - spin_unlock_irq(&health_lock); + if (get_nic_interface(dev) == MLX5_NIC_IFC_DISABLED) + return 1; - list_for_each_entry_safe(health, n, &tlist, list) { - priv = container_of(health, struct mlx5_priv, health); - dev = container_of(priv, struct mlx5_core_dev, priv); - mlx5_core_warn(dev, "handling bad device here\n"); - /* nothing yet */ - spin_lock_irq(&health_lock); - list_del_init(&health->list); - spin_unlock_irq(&health_lock); + if (ioread32be(&h->fw_ver) == 0xffffffff) + return 1; + + return 0; +} + +void mlx5_enter_error_state(struct mlx5_core_dev *dev) +{ + if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) + return; + + mlx5_core_err(dev, "start\n"); + if (pci_channel_offline(dev->pdev) || in_fatal(dev)) + dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR; + + mlx5_core_event(dev, MLX5_DEV_EVENT_SYS_ERROR, 0); + mlx5_core_err(dev, "end\n"); +} + +static void mlx5_handle_bad_state(struct mlx5_core_dev *dev) +{ + u8 nic_interface = get_nic_interface(dev); + + switch (nic_interface) { + case MLX5_NIC_IFC_FULL: + mlx5_core_warn(dev, "Expected to see disabled NIC but it is full driver\n"); + break; + + case MLX5_NIC_IFC_DISABLED: + mlx5_core_warn(dev, "starting teardown\n"); + break; + + case MLX5_NIC_IFC_NO_DRAM_NIC: + mlx5_core_warn(dev, "Expected to see disabled NIC but it is no dram nic\n"); + break; + default: + mlx5_core_warn(dev, "Expected to see disabled NIC but it is has invalid value %d\n", + nic_interface); } + + mlx5_disable_device(dev); +} + +static void health_care(struct work_struct *work) +{ + struct mlx5_core_health *health; + struct mlx5_core_dev *dev; + struct mlx5_priv *priv; + + health = container_of(work, struct mlx5_core_health, work); + priv = container_of(health, struct mlx5_priv, health); + dev = container_of(priv, struct mlx5_core_dev, priv); + mlx5_core_warn(dev, "handling bad device here\n"); + mlx5_handle_bad_state(dev); } static const char *hsynd_str(u8 synd) @@ -88,6 +163,8 @@ static const char *hsynd_str(u8 synd) return "firmware internal error"; case MLX5_HEALTH_SYNDR_IRISC_ERR: return "irisc not responding"; + case MLX5_HEALTH_SYNDR_HW_UNRECOVERABLE_ERR: + return "unrecoverable hardware error"; case MLX5_HEALTH_SYNDR_CRC_ERR: return "firmware CRC error"; case MLX5_HEALTH_SYNDR_FETCH_PCI_ERR: @@ -98,48 +175,81 @@ static const char *hsynd_str(u8 synd) return "async EQ buffer overrun"; case MLX5_HEALTH_SYNDR_EQ_ERR: return "EQ error"; + case MLX5_HEALTH_SYNDR_EQ_INV: + return "Invalid EQ refrenced"; case MLX5_HEALTH_SYNDR_FFSER_ERR: return "FFSER error"; + case MLX5_HEALTH_SYNDR_HIGH_TEMP: + return "High temprature"; default: return "unrecognized error"; } } -static u16 read_be16(__be16 __iomem *p) +static u16 get_maj(u32 fw) { - return swab16(readl((__force u16 __iomem *) p)); + return fw >> 28; } -static u32 read_be32(__be32 __iomem *p) +static u16 get_min(u32 fw) { - return swab32(readl((__force u32 __iomem *) p)); + return fw >> 16 & 0xfff; +} + +static u16 get_sub(u32 fw) +{ + return fw & 0xffff; } static void print_health_info(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; struct health_buffer __iomem *h = health->health; + char fw_str[18]; + u32 fw; int i; + /* If the syndrom is 0, the device is OK and no need to print buffer */ + if (!ioread8(&h->synd)) + return; + for (i = 0; i < ARRAY_SIZE(h->assert_var); i++) - pr_info("assert_var[%d] 0x%08x\n", i, read_be32(h->assert_var + i)); + dev_err(&dev->pdev->dev, "assert_var[%d] 0x%08x\n", i, ioread32be(h->assert_var + i)); + + dev_err(&dev->pdev->dev, "assert_exit_ptr 0x%08x\n", ioread32be(&h->assert_exit_ptr)); + dev_err(&dev->pdev->dev, "assert_callra 0x%08x\n", ioread32be(&h->assert_callra)); + fw = ioread32be(&h->fw_ver); + sprintf(fw_str, "%d.%d.%d", get_maj(fw), get_min(fw), get_sub(fw)); + dev_err(&dev->pdev->dev, "fw_ver %s\n", fw_str); + dev_err(&dev->pdev->dev, "hw_id 0x%08x\n", ioread32be(&h->hw_id)); + dev_err(&dev->pdev->dev, "irisc_index %d\n", ioread8(&h->irisc_index)); + dev_err(&dev->pdev->dev, "synd 0x%x: %s\n", ioread8(&h->synd), hsynd_str(ioread8(&h->synd))); + dev_err(&dev->pdev->dev, "ext_synd 0x%04x\n", ioread16be(&h->ext_synd)); +} + +static unsigned long get_next_poll_jiffies(void) +{ + unsigned long next; - pr_info("assert_exit_ptr 0x%08x\n", read_be32(&h->assert_exit_ptr)); - pr_info("assert_callra 0x%08x\n", read_be32(&h->assert_callra)); - pr_info("fw_ver 0x%08x\n", read_be32(&h->fw_ver)); - pr_info("hw_id 0x%08x\n", read_be32(&h->hw_id)); - pr_info("irisc_index %d\n", readb(&h->irisc_index)); - pr_info("synd 0x%x: %s\n", readb(&h->synd), hsynd_str(readb(&h->synd))); - pr_info("ext_sync 0x%04x\n", read_be16(&h->ext_sync)); + get_random_bytes(&next, sizeof(next)); + next %= HZ; + next += jiffies + MLX5_HEALTH_POLL_INTERVAL; + + return next; } static void poll_health(unsigned long data) { struct mlx5_core_dev *dev = (struct mlx5_core_dev *)data; struct mlx5_core_health *health = &dev->priv.health; - unsigned long next; u32 count; + if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + trigger_cmd_completions(dev); + mod_timer(&health->timer, get_next_poll_jiffies()); + return; + } + count = ioread32be(health->health_counter); if (count == health->prev) ++health->miss_counter; @@ -148,18 +258,16 @@ static void poll_health(unsigned long data) health->prev = count; if (health->miss_counter == MAX_MISSES) { - mlx5_core_err(dev, "device's health compromised\n"); + dev_err(&dev->pdev->dev, "device's health compromised - reached miss count\n"); print_health_info(dev); - spin_lock_irq(&health_lock); - list_add_tail(&health->list, &health_list); - spin_unlock_irq(&health_lock); - - queue_work(mlx5_core_wq, &health_work); } else { - get_random_bytes(&next, sizeof(next)); - next %= HZ; - next += jiffies + MLX5_HEALTH_POLL_INTERVAL; - mod_timer(&health->timer, next); + mod_timer(&health->timer, get_next_poll_jiffies()); + } + + if (in_fatal(dev) && !health->sick) { + health->sick = true; + print_health_info(dev); + queue_work(health->wq, &health->work); } } @@ -167,7 +275,6 @@ void mlx5_start_health_poll(struct mlx5_core_dev *dev) { struct mlx5_core_health *health = &dev->priv.health; - INIT_LIST_HEAD(&health->list); init_timer(&health->timer); health->health = &dev->iseg->health; health->health_counter = &dev->iseg->health_counter; @@ -183,18 +290,33 @@ void mlx5_stop_health_poll(struct mlx5_core_dev *dev) struct mlx5_core_health *health = &dev->priv.health; del_timer_sync(&health->timer); - - spin_lock_irq(&health_lock); - if (!list_empty(&health->list)) - list_del_init(&health->list); - spin_unlock_irq(&health_lock); } -void mlx5_health_cleanup(void) +void mlx5_health_cleanup(struct mlx5_core_dev *dev) { + struct mlx5_core_health *health = &dev->priv.health; + + destroy_workqueue(health->wq); } -void __init mlx5_health_init(void) +int mlx5_health_init(struct mlx5_core_dev *dev) { - INIT_WORK(&health_work, health_care); + struct mlx5_core_health *health; + char *name; + + health = &dev->priv.health; + name = kmalloc(64, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(name, "mlx5_health"); + strcat(name, dev_name(&dev->pdev->dev)); + health->wq = create_singlethread_workqueue(name); + kfree(name); + if (!health->wq) + return -ENOMEM; + + INIT_WORK(&health->work, health_care); + + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 03aabdd79abe..2388aec208fa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -39,12 +39,14 @@ #include <linux/slab.h> #include <linux/io-mapping.h> #include <linux/interrupt.h> +#include <linux/delay.h> #include <linux/mlx5/driver.h> #include <linux/mlx5/cq.h> #include <linux/mlx5/qp.h> #include <linux/mlx5/srq.h> #include <linux/debugfs.h> #include <linux/kmod.h> +#include <linux/delay.h> #include <linux/mlx5/mlx5_ifc.h> #include "mlx5_core.h" @@ -62,7 +64,6 @@ static int prof_sel = MLX5_DEFAULT_PROF; module_param_named(prof_sel, prof_sel, int, 0444); MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2"); -struct workqueue_struct *mlx5_core_wq; static LIST_HEAD(intf_list); static LIST_HEAD(dev_list); static DEFINE_MUTEX(intf_mutex); @@ -152,6 +153,25 @@ static struct mlx5_profile profile[] = { }, }; +#define FW_INIT_TIMEOUT_MILI 2000 +#define FW_INIT_WAIT_MS 2 + +static int wait_fw_init(struct mlx5_core_dev *dev, u32 max_wait_mili) +{ + unsigned long end = jiffies + msecs_to_jiffies(max_wait_mili); + int err = 0; + + while (fw_initializing(dev)) { + if (time_after(jiffies, end)) { + err = -EBUSY; + break; + } + msleep(FW_INIT_WAIT_MS); + } + + return err; +} + static int set_dma_caps(struct pci_dev *pdev) { int err; @@ -182,6 +202,34 @@ static int set_dma_caps(struct pci_dev *pdev) return err; } +static int mlx5_pci_enable_device(struct mlx5_core_dev *dev) +{ + struct pci_dev *pdev = dev->pdev; + int err = 0; + + mutex_lock(&dev->pci_status_mutex); + if (dev->pci_status == MLX5_PCI_STATUS_DISABLED) { + err = pci_enable_device(pdev); + if (!err) + dev->pci_status = MLX5_PCI_STATUS_ENABLED; + } + mutex_unlock(&dev->pci_status_mutex); + + return err; +} + +static void mlx5_pci_disable_device(struct mlx5_core_dev *dev) +{ + struct pci_dev *pdev = dev->pdev; + + mutex_lock(&dev->pci_status_mutex); + if (dev->pci_status == MLX5_PCI_STATUS_ENABLED) { + pci_disable_device(pdev); + dev->pci_status = MLX5_PCI_STATUS_DISABLED; + } + mutex_unlock(&dev->pci_status_mutex); +} + static int request_bar(struct pci_dev *pdev) { int err = 0; @@ -672,12 +720,126 @@ static void unmap_bf_area(struct mlx5_core_dev *dev) io_mapping_free(dev->priv.bf_mapping); } -static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) +static void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) +{ + struct mlx5_device_context *dev_ctx; + struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); + + dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); + if (!dev_ctx) + return; + + dev_ctx->intf = intf; + dev_ctx->context = intf->add(dev); + + if (dev_ctx->context) { + spin_lock_irq(&priv->ctx_lock); + list_add_tail(&dev_ctx->list, &priv->ctx_list); + spin_unlock_irq(&priv->ctx_lock); + } else { + kfree(dev_ctx); + } +} + +static void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv) +{ + struct mlx5_device_context *dev_ctx; + struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); + + list_for_each_entry(dev_ctx, &priv->ctx_list, list) + if (dev_ctx->intf == intf) { + spin_lock_irq(&priv->ctx_lock); + list_del(&dev_ctx->list); + spin_unlock_irq(&priv->ctx_lock); + + intf->remove(dev, dev_ctx->context); + kfree(dev_ctx); + return; + } +} + +static int mlx5_register_device(struct mlx5_core_dev *dev) { struct mlx5_priv *priv = &dev->priv; - int err; + struct mlx5_interface *intf; + + mutex_lock(&intf_mutex); + list_add_tail(&priv->dev_list, &dev_list); + list_for_each_entry(intf, &intf_list, list) + mlx5_add_device(intf, priv); + mutex_unlock(&intf_mutex); + + return 0; +} + +static void mlx5_unregister_device(struct mlx5_core_dev *dev) +{ + struct mlx5_priv *priv = &dev->priv; + struct mlx5_interface *intf; + + mutex_lock(&intf_mutex); + list_for_each_entry(intf, &intf_list, list) + mlx5_remove_device(intf, priv); + list_del(&priv->dev_list); + mutex_unlock(&intf_mutex); +} + +int mlx5_register_interface(struct mlx5_interface *intf) +{ + struct mlx5_priv *priv; + + if (!intf->add || !intf->remove) + return -EINVAL; + + mutex_lock(&intf_mutex); + list_add_tail(&intf->list, &intf_list); + list_for_each_entry(priv, &dev_list, dev_list) + mlx5_add_device(intf, priv); + mutex_unlock(&intf_mutex); + + return 0; +} +EXPORT_SYMBOL(mlx5_register_interface); + +void mlx5_unregister_interface(struct mlx5_interface *intf) +{ + struct mlx5_priv *priv; + + mutex_lock(&intf_mutex); + list_for_each_entry(priv, &dev_list, dev_list) + mlx5_remove_device(intf, priv); + list_del(&intf->list); + mutex_unlock(&intf_mutex); +} +EXPORT_SYMBOL(mlx5_unregister_interface); + +void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol) +{ + struct mlx5_priv *priv = &mdev->priv; + struct mlx5_device_context *dev_ctx; + unsigned long flags; + void *result = NULL; + + spin_lock_irqsave(&priv->ctx_lock, flags); + + list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list) + if ((dev_ctx->intf->protocol == protocol) && + dev_ctx->intf->get_dev) { + result = dev_ctx->intf->get_dev(dev_ctx->context); + break; + } + + spin_unlock_irqrestore(&priv->ctx_lock, flags); + + return result; +} +EXPORT_SYMBOL(mlx5_get_protocol_dev); + +static int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv) +{ + struct pci_dev *pdev = dev->pdev; + int err = 0; - dev->pdev = pdev; pci_set_drvdata(dev->pdev, dev); strncpy(priv->name, dev_name(&pdev->dev), MLX5_MAX_NAME_LEN); priv->name[MLX5_MAX_NAME_LEN - 1] = 0; @@ -694,7 +856,7 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) if (!priv->dbg_root) return -ENOMEM; - err = pci_enable_device(pdev); + err = mlx5_pci_enable_device(dev); if (err) { dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n"); goto err_dbg; @@ -721,13 +883,61 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) dev_err(&pdev->dev, "Failed mapping initialization segment, aborting\n"); goto err_clr_master; } + + return 0; + +err_clr_master: + pci_clear_master(dev->pdev); + release_bar(dev->pdev); +err_disable: + mlx5_pci_disable_device(dev); + +err_dbg: + debugfs_remove(priv->dbg_root); + return err; +} + +static void mlx5_pci_close(struct mlx5_core_dev *dev, struct mlx5_priv *priv) +{ + iounmap(dev->iseg); + pci_clear_master(dev->pdev); + release_bar(dev->pdev); + mlx5_pci_disable_device(dev); + debugfs_remove(priv->dbg_root); +} + +#define MLX5_IB_MOD "mlx5_ib" +static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) +{ + struct pci_dev *pdev = dev->pdev; + int err; + + mutex_lock(&dev->intf_state_mutex); + if (dev->interface_state == MLX5_INTERFACE_STATE_UP) { + dev_warn(&dev->pdev->dev, "%s: interface is up, NOP\n", + __func__); + goto out; + } + dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev), fw_rev_min(dev), fw_rev_sub(dev)); + /* on load removing any previous indication of internal error, device is + * up + */ + dev->state = MLX5_DEVICE_STATE_UP; + err = mlx5_cmd_init(dev); if (err) { dev_err(&pdev->dev, "Failed initializing command interface, aborting\n"); - goto err_unmap; + goto out_err; + } + + err = wait_fw_init(dev, FW_INIT_TIMEOUT_MILI); + if (err) { + dev_err(&dev->pdev->dev, "Firmware over %d MS in initializing state, aborting\n", + FW_INIT_TIMEOUT_MILI); + goto out_err; } mlx5_pagealloc_init(dev); @@ -842,8 +1052,29 @@ static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev) mlx5_init_srq_table(dev); mlx5_init_mr_table(dev); + err = mlx5_register_device(dev); + if (err) { + dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err); + goto err_reg_dev; + } + + err = request_module_nowait(MLX5_IB_MOD); + if (err) + pr_info("failed request module on %s\n", MLX5_IB_MOD); + + dev->interface_state = MLX5_INTERFACE_STATE_UP; +out: + mutex_unlock(&dev->intf_state_mutex); + return 0; +err_reg_dev: + mlx5_cleanup_mr_table(dev); + mlx5_cleanup_srq_table(dev); + mlx5_cleanup_qp_table(dev); + mlx5_cleanup_cq_table(dev); + mlx5_irq_clear_affinity_hints(dev); + err_unmap_bf_area: unmap_bf_area(dev); @@ -865,7 +1096,7 @@ err_stop_poll: mlx5_stop_health_poll(dev); if (mlx5_cmd_teardown_hca(dev)) { dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n"); - return err; + goto out_err; } err_pagealloc_stop: @@ -881,25 +1112,25 @@ err_pagealloc_cleanup: mlx5_pagealloc_cleanup(dev); mlx5_cmd_cleanup(dev); -err_unmap: - iounmap(dev->iseg); - -err_clr_master: - pci_clear_master(dev->pdev); - release_bar(dev->pdev); - -err_disable: - pci_disable_device(dev->pdev); +out_err: + dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR; + mutex_unlock(&dev->intf_state_mutex); -err_dbg: - debugfs_remove(priv->dbg_root); return err; } -static void mlx5_dev_cleanup(struct mlx5_core_dev *dev) +static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) { - struct mlx5_priv *priv = &dev->priv; + int err = 0; + mutex_lock(&dev->intf_state_mutex); + if (dev->interface_state == MLX5_INTERFACE_STATE_DOWN) { + dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n", + __func__); + goto out; + } + mlx5_unregister_device(dev); + mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); mlx5_cleanup_qp_table(dev); mlx5_cleanup_cq_table(dev); @@ -911,139 +1142,25 @@ static void mlx5_dev_cleanup(struct mlx5_core_dev *dev) mlx5_eq_cleanup(dev); mlx5_disable_msix(dev); mlx5_stop_health_poll(dev); - if (mlx5_cmd_teardown_hca(dev)) { + err = mlx5_cmd_teardown_hca(dev); + if (err) { dev_err(&dev->pdev->dev, "tear_down_hca failed, skip cleanup\n"); - return; + goto out; } mlx5_pagealloc_stop(dev); mlx5_reclaim_startup_pages(dev); mlx5_core_disable_hca(dev); mlx5_pagealloc_cleanup(dev); mlx5_cmd_cleanup(dev); - iounmap(dev->iseg); - pci_clear_master(dev->pdev); - release_bar(dev->pdev); - pci_disable_device(dev->pdev); - debugfs_remove(priv->dbg_root); -} - -static void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) -{ - struct mlx5_device_context *dev_ctx; - struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); - - dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); - if (!dev_ctx) { - pr_warn("mlx5_add_device: alloc context failed\n"); - return; - } - - dev_ctx->intf = intf; - dev_ctx->context = intf->add(dev); - - if (dev_ctx->context) { - spin_lock_irq(&priv->ctx_lock); - list_add_tail(&dev_ctx->list, &priv->ctx_list); - spin_unlock_irq(&priv->ctx_lock); - } else { - kfree(dev_ctx); - } -} - -static void mlx5_remove_device(struct mlx5_interface *intf, struct mlx5_priv *priv) -{ - struct mlx5_device_context *dev_ctx; - struct mlx5_core_dev *dev = container_of(priv, struct mlx5_core_dev, priv); - - list_for_each_entry(dev_ctx, &priv->ctx_list, list) - if (dev_ctx->intf == intf) { - spin_lock_irq(&priv->ctx_lock); - list_del(&dev_ctx->list); - spin_unlock_irq(&priv->ctx_lock); - - intf->remove(dev, dev_ctx->context); - kfree(dev_ctx); - return; - } -} -static int mlx5_register_device(struct mlx5_core_dev *dev) -{ - struct mlx5_priv *priv = &dev->priv; - struct mlx5_interface *intf; - - mutex_lock(&intf_mutex); - list_add_tail(&priv->dev_list, &dev_list); - list_for_each_entry(intf, &intf_list, list) - mlx5_add_device(intf, priv); - mutex_unlock(&intf_mutex); - - return 0; -} -static void mlx5_unregister_device(struct mlx5_core_dev *dev) -{ - struct mlx5_priv *priv = &dev->priv; - struct mlx5_interface *intf; - - mutex_lock(&intf_mutex); - list_for_each_entry(intf, &intf_list, list) - mlx5_remove_device(intf, priv); - list_del(&priv->dev_list); - mutex_unlock(&intf_mutex); -} - -int mlx5_register_interface(struct mlx5_interface *intf) -{ - struct mlx5_priv *priv; - - if (!intf->add || !intf->remove) - return -EINVAL; - - mutex_lock(&intf_mutex); - list_add_tail(&intf->list, &intf_list); - list_for_each_entry(priv, &dev_list, dev_list) - mlx5_add_device(intf, priv); - mutex_unlock(&intf_mutex); - - return 0; -} -EXPORT_SYMBOL(mlx5_register_interface); - -void mlx5_unregister_interface(struct mlx5_interface *intf) -{ - struct mlx5_priv *priv; - - mutex_lock(&intf_mutex); - list_for_each_entry(priv, &dev_list, dev_list) - mlx5_remove_device(intf, priv); - list_del(&intf->list); - mutex_unlock(&intf_mutex); -} -EXPORT_SYMBOL(mlx5_unregister_interface); - -void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol) -{ - struct mlx5_priv *priv = &mdev->priv; - struct mlx5_device_context *dev_ctx; - unsigned long flags; - void *result = NULL; - - spin_lock_irqsave(&priv->ctx_lock, flags); - - list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list) - if ((dev_ctx->intf->protocol == protocol) && - dev_ctx->intf->get_dev) { - result = dev_ctx->intf->get_dev(dev_ctx->context); - break; - } - spin_unlock_irqrestore(&priv->ctx_lock, flags); - - return result; +out: + dev->interface_state = MLX5_INTERFACE_STATE_DOWN; + mutex_unlock(&dev->intf_state_mutex); + return err; } -EXPORT_SYMBOL(mlx5_get_protocol_dev); -static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, - unsigned long param) +void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, + unsigned long param) { struct mlx5_priv *priv = &dev->priv; struct mlx5_device_context *dev_ctx; @@ -1064,7 +1181,6 @@ struct mlx5_core_event_handler { void *data); }; -#define MLX5_IB_MOD "mlx5_ib" static int init_one(struct pci_dev *pdev, const struct pci_device_id *id) @@ -1088,43 +1204,166 @@ static int init_one(struct pci_dev *pdev, prof_sel = MLX5_DEFAULT_PROF; } dev->profile = &profile[prof_sel]; + dev->pdev = pdev; dev->event = mlx5_core_event; INIT_LIST_HEAD(&priv->ctx_list); spin_lock_init(&priv->ctx_lock); - err = mlx5_dev_init(dev, pdev); + mutex_init(&dev->pci_status_mutex); + mutex_init(&dev->intf_state_mutex); + err = mlx5_pci_init(dev, priv); if (err) { - dev_err(&pdev->dev, "mlx5_dev_init failed %d\n", err); - goto out; + dev_err(&pdev->dev, "mlx5_pci_init failed with error code %d\n", err); + goto clean_dev; } - err = mlx5_register_device(dev); + err = mlx5_health_init(dev); if (err) { - dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err); - goto out_init; + dev_err(&pdev->dev, "mlx5_health_init failed with error code %d\n", err); + goto close_pci; } - err = request_module_nowait(MLX5_IB_MOD); - if (err) - pr_info("failed request module on %s\n", MLX5_IB_MOD); + err = mlx5_load_one(dev, priv); + if (err) { + dev_err(&pdev->dev, "mlx5_load_one failed with error code %d\n", err); + goto clean_health; + } return 0; -out_init: - mlx5_dev_cleanup(dev); -out: +clean_health: + mlx5_health_cleanup(dev); +close_pci: + mlx5_pci_close(dev, priv); +clean_dev: + pci_set_drvdata(pdev, NULL); kfree(dev); + return err; } + static void remove_one(struct pci_dev *pdev) { struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_priv *priv = &dev->priv; - mlx5_unregister_device(dev); - mlx5_dev_cleanup(dev); + if (mlx5_unload_one(dev, priv)) { + dev_err(&dev->pdev->dev, "mlx5_unload_one failed\n"); + mlx5_health_cleanup(dev); + return; + } + mlx5_health_cleanup(dev); + mlx5_pci_close(dev, priv); + pci_set_drvdata(pdev, NULL); kfree(dev); } +static pci_ers_result_t mlx5_pci_err_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_priv *priv = &dev->priv; + + dev_info(&pdev->dev, "%s was called\n", __func__); + mlx5_enter_error_state(dev); + mlx5_unload_one(dev, priv); + mlx5_pci_disable_device(dev); + return state == pci_channel_io_perm_failure ? + PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t mlx5_pci_slot_reset(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + int err = 0; + + dev_info(&pdev->dev, "%s was called\n", __func__); + + err = mlx5_pci_enable_device(dev); + if (err) { + dev_err(&pdev->dev, "%s: mlx5_pci_enable_device failed with error code: %d\n" + , __func__, err); + return PCI_ERS_RESULT_DISCONNECT; + } + pci_set_master(pdev); + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + return err ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED; +} + +void mlx5_disable_device(struct mlx5_core_dev *dev) +{ + mlx5_pci_err_detected(dev->pdev, 0); +} + +/* wait for the device to show vital signs. For now we check + * that we can read the device ID and that the health buffer + * shows a non zero value which is different than 0xffffffff + */ +static void wait_vital(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_core_health *health = &dev->priv.health; + const int niter = 100; + u32 count; + u16 did; + int i; + + /* Wait for firmware to be ready after reset */ + msleep(1000); + for (i = 0; i < niter; i++) { + if (pci_read_config_word(pdev, 2, &did)) { + dev_warn(&pdev->dev, "failed reading config word\n"); + break; + } + if (did == pdev->device) { + dev_info(&pdev->dev, "device ID correctly read after %d iterations\n", i); + break; + } + msleep(50); + } + if (i == niter) + dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__); + + for (i = 0; i < niter; i++) { + count = ioread32be(health->health_counter); + if (count && count != 0xffffffff) { + dev_info(&pdev->dev, "Counter value 0x%x after %d iterations\n", count, i); + break; + } + msleep(50); + } + + if (i == niter) + dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__); +} + +static void mlx5_pci_resume(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_priv *priv = &dev->priv; + int err; + + dev_info(&pdev->dev, "%s was called\n", __func__); + + pci_save_state(pdev); + wait_vital(pdev); + + err = mlx5_load_one(dev, priv); + if (err) + dev_err(&pdev->dev, "%s: mlx5_load_one failed with error code: %d\n" + , __func__, err); + else + dev_info(&pdev->dev, "%s: device recovered\n", __func__); +} + +static const struct pci_error_handlers mlx5_err_handler = { + .error_detected = mlx5_pci_err_detected, + .slot_reset = mlx5_pci_slot_reset, + .resume = mlx5_pci_resume +}; + static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */ { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */ @@ -1141,7 +1380,8 @@ static struct pci_driver mlx5_core_driver = { .name = DRIVER_NAME, .id_table = mlx5_core_pci_table, .probe = init_one, - .remove = remove_one + .remove = remove_one, + .err_handler = &mlx5_err_handler }; static int __init init(void) @@ -1149,16 +1389,10 @@ static int __init init(void) int err; mlx5_register_debugfs(); - mlx5_core_wq = create_singlethread_workqueue("mlx5_core_wq"); - if (!mlx5_core_wq) { - err = -ENOMEM; - goto err_debug; - } - mlx5_health_init(); err = pci_register_driver(&mlx5_core_driver); if (err) - goto err_health; + goto err_debug; #ifdef CONFIG_MLX5_CORE_EN mlx5e_init(); @@ -1166,9 +1400,6 @@ static int __init init(void) return 0; -err_health: - mlx5_health_cleanup(); - destroy_workqueue(mlx5_core_wq); err_debug: mlx5_unregister_debugfs(); return err; @@ -1180,8 +1411,6 @@ static void __exit cleanup(void) mlx5e_cleanup(); #endif pci_unregister_driver(&mlx5_core_driver); - mlx5_health_cleanup(); - destroy_workqueue(mlx5_core_wq); mlx5_unregister_debugfs(); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 566a70488db1..cee5b7a839bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -43,25 +43,25 @@ extern int mlx5_core_debug_mask; -#define mlx5_core_dbg(dev, format, ...) \ - pr_debug("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_dbg(__dev, format, ...) \ + dev_dbg(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) -#define mlx5_core_dbg_mask(dev, mask, format, ...) \ +#define mlx5_core_dbg_mask(__dev, mask, format, ...) \ do { \ if ((mask) & mlx5_core_debug_mask) \ - mlx5_core_dbg(dev, format, ##__VA_ARGS__); \ + mlx5_core_dbg(__dev, format, ##__VA_ARGS__); \ } while (0) -#define mlx5_core_err(dev, format, ...) \ - pr_err("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_err(__dev, format, ...) \ + dev_err(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) -#define mlx5_core_warn(dev, format, ...) \ - pr_warn("%s:%s:%d:(pid %d): " format, \ - (dev)->priv.name, __func__, __LINE__, current->pid, \ +#define mlx5_core_warn(__dev, format, ...) \ + dev_warn(&(__dev)->pdev->dev, "%s:%s:%d:(pid %d): " format, \ + (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) enum { @@ -86,6 +86,10 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev); int mlx5_query_board_id(struct mlx5_core_dev *dev); int mlx5_cmd_init_hca(struct mlx5_core_dev *dev); int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev); +void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, + unsigned long param); +void mlx5_enter_error_state(struct mlx5_core_dev *dev); +void mlx5_disable_device(struct mlx5_core_dev *dev); void mlx5e_init(void); void mlx5e_cleanup(void); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index 1adb300dd850..6fa22b51e460 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -40,6 +40,7 @@ void mlx5_init_mr_table(struct mlx5_core_dev *dev) { struct mlx5_mr_table *table = &dev->priv.mr_table; + memset(table, 0, sizeof(*table)); rwlock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 8a64542abc16..1cda5d268ec9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -275,12 +275,36 @@ out_alloc: return err; } + +static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id) +{ + struct mlx5_manage_pages_inbox *in; + struct mlx5_manage_pages_outbox out; + int err; + + in = kzalloc(sizeof(*in), GFP_KERNEL); + if (!in) + return; + + memset(&out, 0, sizeof(out)); + in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); + in->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE); + in->func_id = cpu_to_be16(func_id); + err = mlx5_cmd_exec(dev, in, sizeof(*in), &out, sizeof(out)); + if (!err) + err = mlx5_cmd_status_to_err(&out.hdr); + + if (err) + mlx5_core_warn(dev, "page notify failed\n"); + + kfree(in); +} + static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, int notify_fail) { struct mlx5_manage_pages_inbox *in; struct mlx5_manage_pages_outbox out; - struct mlx5_manage_pages_inbox *nin; int inlen; u64 addr; int err; @@ -289,8 +313,9 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages, inlen = sizeof(*in) + npages * sizeof(in->pas[0]); in = mlx5_vzalloc(inlen); if (!in) { + err = -ENOMEM; mlx5_core_warn(dev, "vzalloc failed %d\n", inlen); - return -ENOMEM; + goto out_free; } memset(&out, 0, sizeof(out)); @@ -316,43 +341,29 @@ retry: if (err) { mlx5_core_warn(dev, "func_id 0x%x, npages %d, err %d\n", func_id, npages, err); - goto out_alloc; + goto out_4k; } dev->priv.fw_pages += npages; - if (out.hdr.status) { - err = mlx5_cmd_status_to_err(&out.hdr); - if (err) { - mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", - func_id, npages, out.hdr.status); - goto out_alloc; - } + err = mlx5_cmd_status_to_err(&out.hdr); + if (err) { + mlx5_core_warn(dev, "func_id 0x%x, npages %d, status %d\n", + func_id, npages, out.hdr.status); + goto out_4k; } mlx5_core_dbg(dev, "err %d\n", err); - goto out_free; - -out_alloc: - if (notify_fail) { - nin = kzalloc(sizeof(*nin), GFP_KERNEL); - if (!nin) { - mlx5_core_warn(dev, "allocation failed\n"); - goto out_4k; - } - memset(&out, 0, sizeof(out)); - nin->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_MANAGE_PAGES); - nin->hdr.opmod = cpu_to_be16(MLX5_PAGES_CANT_GIVE); - if (mlx5_cmd_exec(dev, nin, sizeof(*nin), &out, sizeof(out))) - mlx5_core_warn(dev, "page notify failed\n"); - kfree(nin); - } + kvfree(in); + return 0; out_4k: for (i--; i >= 0; i--) free_4k(dev, be64_to_cpu(in->pas[i])); out_free: kvfree(in); + if (notify_fail) + page_notify_fail(dev, func_id); return err; } @@ -482,15 +493,20 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev) struct fw_page *fwp; struct rb_node *p; int nclaimed = 0; - int err; + int err = 0; do { p = rb_first(&dev->priv.page_root); if (p) { fwp = rb_entry(p, struct fw_page, rb_node); - err = reclaim_pages(dev, fwp->func_id, - optimal_reclaimed_pages(), - &nclaimed); + if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) { + free_4k(dev, fwp->addr); + nclaimed = 1; + } else { + err = reclaim_pages(dev, fwp->func_id, + optimal_reclaimed_pages(), + &nclaimed); + } if (err) { mlx5_core_warn(dev, "failed reclaiming pages (%d)\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index 3b9480fa3403..a87e773e93f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -90,16 +90,13 @@ int mlx5_set_port_caps(struct mlx5_core_dev *dev, u8 port_num, u32 caps) { struct mlx5_reg_pcap in; struct mlx5_reg_pcap out; - int err; memset(&in, 0, sizeof(in)); in.caps_127_96 = cpu_to_be32(caps); in.port_num = port_num; - err = mlx5_core_access_reg(dev, &in, sizeof(in), &out, - sizeof(out), MLX5_REG_PCAP, 0, 1); - - return err; + return mlx5_core_access_reg(dev, &in, sizeof(in), &out, + sizeof(out), MLX5_REG_PCAP, 0, 1); } EXPORT_SYMBOL_GPL(mlx5_set_port_caps); @@ -107,16 +104,13 @@ int mlx5_query_port_ptys(struct mlx5_core_dev *dev, u32 *ptys, int ptys_size, int proto_mask, u8 local_port) { u32 in[MLX5_ST_SZ_DW(ptys_reg)]; - int err; memset(in, 0, sizeof(in)); MLX5_SET(ptys_reg, in, local_port, local_port); MLX5_SET(ptys_reg, in, proto_mask, proto_mask); - err = mlx5_core_access_reg(dev, in, sizeof(in), ptys, - ptys_size, MLX5_REG_PTYS, 0, 0); - - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), ptys, + ptys_size, MLX5_REG_PTYS, 0, 0); } EXPORT_SYMBOL_GPL(mlx5_query_port_ptys); @@ -199,7 +193,6 @@ int mlx5_set_port_proto(struct mlx5_core_dev *dev, u32 proto_admin, { u32 in[MLX5_ST_SZ_DW(ptys_reg)]; u32 out[MLX5_ST_SZ_DW(ptys_reg)]; - int err; memset(in, 0, sizeof(in)); @@ -210,9 +203,8 @@ int mlx5_set_port_proto(struct mlx5_core_dev *dev, u32 proto_admin, else MLX5_SET(ptys_reg, in, ib_proto_admin, proto_admin); - err = mlx5_core_access_reg(dev, in, sizeof(in), out, - sizeof(out), MLX5_REG_PTYS, 0, 1); - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), out, + sizeof(out), MLX5_REG_PTYS, 0, 1); } EXPORT_SYMBOL_GPL(mlx5_set_port_proto); @@ -250,7 +242,7 @@ int mlx5_query_port_admin_status(struct mlx5_core_dev *dev, return err; *status = MLX5_GET(paos_reg, out, admin_status); - return err; + return 0; } EXPORT_SYMBOL_GPL(mlx5_query_port_admin_status); @@ -308,15 +300,12 @@ static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc, int pvlc_size, u8 local_port) { u32 in[MLX5_ST_SZ_DW(pvlc_reg)]; - int err; memset(in, 0, sizeof(in)); MLX5_SET(pvlc_reg, in, local_port, local_port); - err = mlx5_core_access_reg(dev, in, sizeof(in), pvlc, - pvlc_size, MLX5_REG_PVLC, 0, 0); - - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), pvlc, + pvlc_size, MLX5_REG_PVLC, 0, 0); } int mlx5_query_port_vl_hw_cap(struct mlx5_core_dev *dev, @@ -339,16 +328,14 @@ int mlx5_set_port_pause(struct mlx5_core_dev *dev, u32 rx_pause, u32 tx_pause) { u32 in[MLX5_ST_SZ_DW(pfcc_reg)]; u32 out[MLX5_ST_SZ_DW(pfcc_reg)]; - int err; memset(in, 0, sizeof(in)); MLX5_SET(pfcc_reg, in, local_port, 1); MLX5_SET(pfcc_reg, in, pptx, tx_pause); MLX5_SET(pfcc_reg, in, pprx, rx_pause); - err = mlx5_core_access_reg(dev, in, sizeof(in), out, - sizeof(out), MLX5_REG_PFCC, 0, 1); - return err; + return mlx5_core_access_reg(dev, in, sizeof(in), out, + sizeof(out), MLX5_REG_PFCC, 0, 1); } EXPORT_SYMBOL_GPL(mlx5_set_port_pause); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c index 8b494b562263..30e2ba3f5f16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c @@ -345,6 +345,7 @@ void mlx5_init_qp_table(struct mlx5_core_dev *dev) { struct mlx5_qp_table *table = &dev->priv.qp_table; + memset(table, 0, sizeof(*table)); spin_lock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); mlx5_qp_debugfs_init(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/srq.c b/drivers/net/ethernet/mellanox/mlx5/core/srq.c index c48f504ccbeb..ffada801976b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/srq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/srq.c @@ -531,6 +531,7 @@ void mlx5_init_srq_table(struct mlx5_core_dev *dev) { struct mlx5_srq_table *table = &dev->priv.srq_table; + memset(table, 0, sizeof(*table)); spin_lock_init(&table->lock); INIT_RADIX_TREE(&table->tree, GFP_ATOMIC); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/transobj.c b/drivers/net/ethernet/mellanox/mlx5/core/transobj.c index b4c87c7b0cf0..d7068f54e800 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/transobj.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/transobj.c @@ -177,7 +177,7 @@ int mlx5_core_modify_tir(struct mlx5_core_dev *dev, u32 tirn, u32 *in, void mlx5_core_destroy_tir(struct mlx5_core_dev *dev, u32 tirn) { - u32 in[MLX5_ST_SZ_DW(destroy_tir_out)]; + u32 in[MLX5_ST_SZ_DW(destroy_tir_in)]; u32 out[MLX5_ST_SZ_DW(destroy_tir_out)]; memset(in, 0, sizeof(in)); @@ -206,7 +206,7 @@ int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, int inlen, void mlx5_core_destroy_tis(struct mlx5_core_dev *dev, u32 tisn) { - u32 in[MLX5_ST_SZ_DW(destroy_tis_out)]; + u32 in[MLX5_ST_SZ_DW(destroy_tis_in)]; u32 out[MLX5_ST_SZ_DW(destroy_tis_out)]; memset(in, 0, sizeof(in)); diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 2941d9c5ae48..e36e12219c9b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -30,3 +30,14 @@ config MLXSW_SWITCHX2 To compile this driver as a module, choose M here: the module will be called mlxsw_switchx2. + +config MLXSW_SPECTRUM + tristate "Mellanox Technologies Spectrum support" + depends on MLXSW_CORE && NET_SWITCHDEV + default m + ---help--- + This driver supports Mellanox Technologies Spectrum Ethernet + Switch ASICs. + + To compile this driver as a module, choose M here: the + module will be called mlxsw_spectrum. diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 0a05f65ee814..af015818fd19 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -4,3 +4,6 @@ obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o mlxsw_pci-objs := pci.o obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o mlxsw_switchx2-objs := switchx2.o +obj-$(CONFIG_MLXSW_SPECTRUM) += mlxsw_spectrum.o +mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ + spectrum_switchdev.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index 770db17eb03f..cd63b8263688 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -464,6 +464,8 @@ MLXSW_ITEM32(cmd_mbox, query_aq_cap, max_sg_rq, 0x10, 0, 8); * passed in this command must be pinned. */ +#define MLXSW_CMD_MAP_FA_VPM_ENTRIES_MAX 32 + static inline int mlxsw_cmd_map_fa(struct mlxsw_core *mlxsw_core, char *in_mbox, u32 vpm_entries_count) { @@ -568,7 +570,7 @@ MLXSW_ITEM32(cmd_mbox, config_profile, set_max_vlan_groups, 0x0C, 6, 1); */ MLXSW_ITEM32(cmd_mbox, config_profile, set_max_regions, 0x0C, 7, 1); -/* cmd_mbox_config_profile_set_fid_based +/* cmd_mbox_config_profile_set_flood_mode * Capability bit. Setting a bit to 1 configures the profile * according to the mailbox contents. */ @@ -649,12 +651,8 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_vlan_groups, 0x28, 0, 12); MLXSW_ITEM32(cmd_mbox, config_profile, max_regions, 0x2C, 0, 16); /* cmd_mbox_config_profile_max_flood_tables - * Maximum number of Flooding Tables. Flooding Tables are associated to - * the different packet types for the different switch partitions. - * Note that the table size depends on the fid_based mode. - * In SwitchX silicon, tables are split equally between the switch - * partitions. e.g. for 2 swids and 8 tables, the first 4 are associated - * with swid-1 and the last 4 are associated with swid-2. + * Maximum number of single-entry flooding tables. Different flooding tables + * can be associated with different packet types. */ MLXSW_ITEM32(cmd_mbox, config_profile, max_flood_tables, 0x30, 16, 4); @@ -665,15 +663,42 @@ MLXSW_ITEM32(cmd_mbox, config_profile, max_flood_tables, 0x30, 16, 4); */ MLXSW_ITEM32(cmd_mbox, config_profile, max_vid_flood_tables, 0x30, 8, 4); -/* cmd_mbox_config_profile_fid_based - * FID Based Flood Mode - * 00 Do not use FID to offset the index into the Port Group Table/Multicast ID - * 01 Use FID to offset the index to the Port Group Table (pgi) - * 10 Use FID to offset the index to the Port Group Table (pgi) and - * the Multicast ID +/* cmd_mbox_config_profile_flood_mode + * Flooding mode to use. + * 0-2 - Backward compatible modes for SwitchX devices. + * 3 - Mixed mode, where: + * max_flood_tables indicates the number of single-entry tables. + * max_vid_flood_tables indicates the number of per-VID tables. + * max_fid_offset_flood_tables indicates the number of FID-offset tables. + * max_fid_flood_tables indicates the number of per-FID tables. */ MLXSW_ITEM32(cmd_mbox, config_profile, flood_mode, 0x30, 0, 2); +/* cmd_mbox_config_profile_max_fid_offset_flood_tables + * Maximum number of FID-offset flooding tables. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, + max_fid_offset_flood_tables, 0x34, 24, 4); + +/* cmd_mbox_config_profile_fid_offset_flood_table_size + * The size (number of entries) of each FID-offset flood table. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, + fid_offset_flood_table_size, 0x34, 0, 16); + +/* cmd_mbox_config_profile_max_fid_flood_tables + * Maximum number of per-FID flooding tables. + * + * Note: This flooding tables cover special FIDs only (vFIDs), starting at + * FID value 4K and higher. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, max_fid_flood_tables, 0x38, 24, 4); + +/* cmd_mbox_config_profile_fid_flood_table_size + * The size (number of entries) of each per-FID table. + */ +MLXSW_ITEM32(cmd_mbox, config_profile, fid_flood_table_size, 0x38, 0, 16); + /* cmd_mbox_config_profile_max_ib_mc * Maximum number of multicast FDB records for InfiniBand * FDB (in 512 chunks) per InfiniBand switch partition. diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 28c19cc1a17c..bd80ac714a8a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -511,7 +511,6 @@ static int mlxsw_emad_traps_set(struct mlxsw_core *mlxsw_core) return err; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, - MLXSW_REG_HTGT_TRAP_GROUP_EMAD, MLXSW_TRAP_ID_ETHEMAD); return mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); } @@ -556,8 +555,8 @@ static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core) { char hpkt_pl[MLXSW_REG_HPKT_LEN]; + mlxsw_core->emad.use_emad = false; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_DISCARD, - MLXSW_REG_HTGT_TRAP_GROUP_EMAD, MLXSW_TRAP_ID_ETHEMAD); mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 165808471188..807827350a89 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -54,6 +54,7 @@ MODULE_ALIAS(MLXSW_MODULE_ALIAS_PREFIX kind) #define MLXSW_DEVICE_KIND_SWITCHX2 "switchx2" +#define MLXSW_DEVICE_KIND_SPECTRUM "spectrum" struct mlxsw_core; struct mlxsw_driver; @@ -153,6 +154,10 @@ struct mlxsw_config_profile { u8 max_flood_tables; u8 max_vid_flood_tables; u8 flood_mode; + u8 max_fid_offset_flood_tables; + u16 fid_offset_flood_table_size; + u8 max_fid_flood_tables; + u16 fid_flood_table_size; u16 max_ib_mc; u16 max_pkey; u8 ar_sec; diff --git a/drivers/net/ethernet/mellanox/mlxsw/item.h b/drivers/net/ethernet/mellanox/mlxsw/item.h index 36fb1cec53c9..a94dbda6590b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/item.h +++ b/drivers/net/ethernet/mellanox/mlxsw/item.h @@ -171,15 +171,21 @@ static inline void __mlxsw_item_set64(char *buf, struct mlxsw_item *item, } static inline void __mlxsw_item_memcpy_from(char *buf, char *dst, - struct mlxsw_item *item) + struct mlxsw_item *item, + unsigned short index) { - memcpy(dst, &buf[item->offset], item->size.bytes); + unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char)); + + memcpy(dst, &buf[offset], item->size.bytes); } -static inline void __mlxsw_item_memcpy_to(char *buf, char *src, - struct mlxsw_item *item) +static inline void __mlxsw_item_memcpy_to(char *buf, const char *src, + struct mlxsw_item *item, + unsigned short index) { - memcpy(&buf[item->offset], src, item->size.bytes); + unsigned int offset = __mlxsw_item_offset(item, index, sizeof(char)); + + memcpy(&buf[offset], src, item->size.bytes); } static inline u16 @@ -373,12 +379,40 @@ static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ static inline void \ mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, char *dst) \ { \ - __mlxsw_item_memcpy_from(buf, dst, &__ITEM_NAME(_type, _cname, _iname));\ + __mlxsw_item_memcpy_from(buf, dst, \ + &__ITEM_NAME(_type, _cname, _iname), 0); \ +} \ +static inline void \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, const char *src) \ +{ \ + __mlxsw_item_memcpy_to(buf, src, \ + &__ITEM_NAME(_type, _cname, _iname), 0); \ +} + +#define MLXSW_ITEM_BUF_INDEXED(_type, _cname, _iname, _offset, _sizebytes, \ + _step, _instepoffset) \ +static struct mlxsw_item __ITEM_NAME(_type, _cname, _iname) = { \ + .offset = _offset, \ + .step = _step, \ + .in_step_offset = _instepoffset, \ + .size = {.bytes = _sizebytes,}, \ + .name = #_type "_" #_cname "_" #_iname, \ +}; \ +static inline void \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_from(char *buf, \ + unsigned short index, \ + char *dst) \ +{ \ + __mlxsw_item_memcpy_from(buf, dst, \ + &__ITEM_NAME(_type, _cname, _iname), index); \ } \ static inline void \ -mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, char *src) \ +mlxsw_##_type##_##_cname##_##_iname##_memcpy_to(char *buf, \ + unsigned short index, \ + const char *src) \ { \ - __mlxsw_item_memcpy_to(buf, src, &__ITEM_NAME(_type, _cname, _iname)); \ + __mlxsw_item_memcpy_to(buf, src, \ + &__ITEM_NAME(_type, _cname, _iname), index); \ } #define MLXSW_ITEM_BIT_ARRAY(_type, _cname, _iname, _offset, _sizebytes, \ diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index cef866c37648..371ea3f56aed 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -57,6 +57,7 @@ static const char mlxsw_pci_driver_name[] = "mlxsw_pci"; static const struct pci_device_id mlxsw_pci_id_table[] = { {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SWITCHX2), 0}, + {PCI_VDEVICE(MELLANOX, PCI_DEVICE_ID_MELLANOX_SPECTRUM), 0}, {0, } }; @@ -67,6 +68,8 @@ static const char *mlxsw_pci_device_kind_get(const struct pci_device_id *id) switch (id->device) { case PCI_DEVICE_ID_MELLANOX_SWITCHX2: return MLXSW_DEVICE_KIND_SWITCHX2; + case PCI_DEVICE_ID_MELLANOX_SPECTRUM: + return MLXSW_DEVICE_KIND_SPECTRUM; default: BUG(); } @@ -171,8 +174,8 @@ struct mlxsw_pci { struct msix_entry msix_entry; struct mlxsw_core *core; struct { - u16 num_pages; struct mlxsw_pci_mem_item *items; + unsigned int count; } fw_area; struct { struct mlxsw_pci_mem_item out_mbox; @@ -431,8 +434,7 @@ static int mlxsw_pci_wqe_frag_map(struct mlxsw_pci *mlxsw_pci, char *wqe, mapaddr = pci_map_single(pdev, frag_data, frag_len, direction); if (unlikely(pci_dma_mapping_error(pdev, mapaddr))) { - if (net_ratelimit()) - dev_err(&pdev->dev, "failed to dma map tx frag\n"); + dev_err_ratelimited(&pdev->dev, "failed to dma map tx frag\n"); return -EIO; } mlxsw_pci_wqe_address_set(wqe, index, mapaddr); @@ -497,6 +499,7 @@ static int mlxsw_pci_rdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, struct mlxsw_pci_queue *q) { struct mlxsw_pci_queue_elem_info *elem_info; + u8 sdq_count = mlxsw_pci_sdq_count(mlxsw_pci); int i; int err; @@ -504,9 +507,9 @@ static int mlxsw_pci_rdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, q->consumer_counter = 0; /* Set CQ of same number of this RDQ with base - * above MLXSW_PCI_SDQS_MAX as the lower ones are assigned to SDQs. + * above SDQ count as the lower ones are assigned to SDQs. */ - mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, q->num + MLXSW_PCI_SDQS_COUNT); + mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, sdq_count + q->num); mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */ for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) { dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i); @@ -699,8 +702,8 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci, put_new_skb: memset(wqe, 0, q->elem_size); err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info); - if (err && net_ratelimit()) - dev_dbg(&pdev->dev, "Failed to alloc skb for RDQ\n"); + if (err) + dev_dbg_ratelimited(&pdev->dev, "Failed to alloc skb for RDQ\n"); /* Everything is set up, ring doorbell to pass elem to HW */ q->producer_counter++; mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q); @@ -830,7 +833,8 @@ static void mlxsw_pci_eq_tasklet(unsigned long data) { struct mlxsw_pci_queue *q = (struct mlxsw_pci_queue *) data; struct mlxsw_pci *mlxsw_pci = q->pci; - unsigned long active_cqns[BITS_TO_LONGS(MLXSW_PCI_CQS_COUNT)]; + u8 cq_count = mlxsw_pci_cq_count(mlxsw_pci); + unsigned long active_cqns[BITS_TO_LONGS(MLXSW_PCI_CQS_MAX)]; char *eqe; u8 cqn; bool cq_handle = false; @@ -866,7 +870,7 @@ static void mlxsw_pci_eq_tasklet(unsigned long data) if (!cq_handle) return; - for_each_set_bit(cqn, active_cqns, MLXSW_PCI_CQS_COUNT) { + for_each_set_bit(cqn, active_cqns, cq_count) { q = mlxsw_pci_cq_get(mlxsw_pci, cqn); mlxsw_pci_queue_tasklet_schedule(q); } @@ -1067,10 +1071,8 @@ static int mlxsw_pci_aqs_init(struct mlxsw_pci *mlxsw_pci, char *mbox) num_eqs = mlxsw_cmd_mbox_query_aq_cap_max_num_eqs_get(mbox); eq_log2sz = mlxsw_cmd_mbox_query_aq_cap_log_max_eq_sz_get(mbox); - if ((num_sdqs != MLXSW_PCI_SDQS_COUNT) || - (num_rdqs != MLXSW_PCI_RDQS_COUNT) || - (num_cqs != MLXSW_PCI_CQS_COUNT) || - (num_eqs != MLXSW_PCI_EQS_COUNT)) { + if (num_sdqs + num_rdqs > num_cqs || + num_cqs > MLXSW_PCI_CQS_MAX || num_eqs != MLXSW_PCI_EQS_COUNT) { dev_err(&pdev->dev, "Unsupported number of queues\n"); return -EINVAL; } @@ -1215,6 +1217,14 @@ static int mlxsw_pci_config_profile(struct mlxsw_pci *mlxsw_pci, char *mbox, mbox, profile->max_flood_tables); mlxsw_cmd_mbox_config_profile_max_vid_flood_tables_set( mbox, profile->max_vid_flood_tables); + mlxsw_cmd_mbox_config_profile_max_fid_offset_flood_tables_set( + mbox, profile->max_fid_offset_flood_tables); + mlxsw_cmd_mbox_config_profile_fid_offset_flood_table_size_set( + mbox, profile->fid_offset_flood_table_size); + mlxsw_cmd_mbox_config_profile_max_fid_flood_tables_set( + mbox, profile->max_fid_flood_tables); + mlxsw_cmd_mbox_config_profile_fid_flood_table_size_set( + mbox, profile->fid_flood_table_size); } if (profile->used_flood_mode) { mlxsw_cmd_mbox_config_profile_set_flood_mode_set( @@ -1272,6 +1282,7 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox, u16 num_pages) { struct mlxsw_pci_mem_item *mem_item; + int nent = 0; int i; int err; @@ -1279,7 +1290,7 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox, GFP_KERNEL); if (!mlxsw_pci->fw_area.items) return -ENOMEM; - mlxsw_pci->fw_area.num_pages = num_pages; + mlxsw_pci->fw_area.count = num_pages; mlxsw_cmd_mbox_zero(mbox); for (i = 0; i < num_pages; i++) { @@ -1293,13 +1304,22 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox, err = -ENOMEM; goto err_alloc; } - mlxsw_cmd_mbox_map_fa_pa_set(mbox, i, mem_item->mapaddr); - mlxsw_cmd_mbox_map_fa_log2size_set(mbox, i, 0); /* 1 page */ + mlxsw_cmd_mbox_map_fa_pa_set(mbox, nent, mem_item->mapaddr); + mlxsw_cmd_mbox_map_fa_log2size_set(mbox, nent, 0); /* 1 page */ + if (++nent == MLXSW_CMD_MAP_FA_VPM_ENTRIES_MAX) { + err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent); + if (err) + goto err_cmd_map_fa; + nent = 0; + mlxsw_cmd_mbox_zero(mbox); + } } - err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, num_pages); - if (err) - goto err_cmd_map_fa; + if (nent) { + err = mlxsw_cmd_map_fa(mlxsw_pci->core, mbox, nent); + if (err) + goto err_cmd_map_fa; + } return 0; @@ -1322,7 +1342,7 @@ static void mlxsw_pci_fw_area_fini(struct mlxsw_pci *mlxsw_pci) mlxsw_cmd_unmap_fa(mlxsw_pci->core); - for (i = 0; i < mlxsw_pci->fw_area.num_pages; i++) { + for (i = 0; i < mlxsw_pci->fw_area.count; i++) { mem_item = &mlxsw_pci->fw_area.items[i]; pci_free_consistent(mlxsw_pci->pdev, mem_item->size, diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index 1ef9664b4512..142f33d978c5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -40,6 +40,7 @@ #include "item.h" #define PCI_DEVICE_ID_MELLANOX_SWITCHX2 0xc738 +#define PCI_DEVICE_ID_MELLANOX_SPECTRUM 0xcb84 #define MLXSW_PCI_BAR0_SIZE (1024 * 1024) /* 1MB */ #define MLXSW_PCI_PAGE_SIZE 4096 @@ -71,9 +72,7 @@ #define MLXSW_PCI_DOORBELL(offset, type_offset, num) \ ((offset) + (type_offset) + (num) * 4) -#define MLXSW_PCI_RDQS_COUNT 24 -#define MLXSW_PCI_SDQS_COUNT 24 -#define MLXSW_PCI_CQS_COUNT (MLXSW_PCI_RDQS_COUNT + MLXSW_PCI_SDQS_COUNT) +#define MLXSW_PCI_CQS_MAX 96 #define MLXSW_PCI_EQS_COUNT 2 #define MLXSW_PCI_EQ_ASYNC_NUM 0 #define MLXSW_PCI_EQ_COMP_NUM 1 diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 096e1c12175a..4fcba46bbae0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -99,57 +99,6 @@ static const struct mlxsw_reg_info mlxsw_reg_spad = { */ MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6); -/* SMID - Switch Multicast ID - * -------------------------- - * In multi-chip configuration, each device should maintain mapping between - * Multicast ID (MID) into a list of local ports. This mapping is used in all - * the devices other than the ingress device, and is implemented as part of the - * FDB. The MID record maps from a MID, which is a unique identi- fier of the - * multicast group within the stacking domain, into a list of local ports into - * which the packet is replicated. - */ -#define MLXSW_REG_SMID_ID 0x2007 -#define MLXSW_REG_SMID_LEN 0x420 - -static const struct mlxsw_reg_info mlxsw_reg_smid = { - .id = MLXSW_REG_SMID_ID, - .len = MLXSW_REG_SMID_LEN, -}; - -/* reg_smid_swid - * Switch partition ID. - * Access: Index - */ -MLXSW_ITEM32(reg, smid, swid, 0x00, 24, 8); - -/* reg_smid_mid - * Multicast identifier - global identifier that represents the multicast group - * across all devices - * Access: Index - */ -MLXSW_ITEM32(reg, smid, mid, 0x00, 0, 16); - -/* reg_smid_port - * Local port memebership (1 bit per port). - * Access: RW - */ -MLXSW_ITEM_BIT_ARRAY(reg, smid, port, 0x20, 0x20, 1); - -/* reg_smid_port_mask - * Local port mask (1 bit per port). - * Access: W - */ -MLXSW_ITEM_BIT_ARRAY(reg, smid, port_mask, 0x220, 0x20, 1); - -static inline void mlxsw_reg_smid_pack(char *payload, u16 mid) -{ - MLXSW_REG_ZERO(smid, payload); - mlxsw_reg_smid_swid_set(payload, 0); - mlxsw_reg_smid_mid_set(payload, mid); - mlxsw_reg_smid_port_set(payload, MLXSW_PORT_CPU_PORT, 1); - mlxsw_reg_smid_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1); -} - /* SSPR - Switch System Port Record Register * ----------------------------------------- * Configures the system port to local port mapping. @@ -208,11 +157,359 @@ static inline void mlxsw_reg_sspr_pack(char *payload, u8 local_port) mlxsw_reg_sspr_system_port_set(payload, local_port); } +/* SFDAT - Switch Filtering Database Aging Time + * -------------------------------------------- + * Controls the Switch aging time. Aging time is able to be set per Switch + * Partition. + */ +#define MLXSW_REG_SFDAT_ID 0x2009 +#define MLXSW_REG_SFDAT_LEN 0x8 + +static const struct mlxsw_reg_info mlxsw_reg_sfdat = { + .id = MLXSW_REG_SFDAT_ID, + .len = MLXSW_REG_SFDAT_LEN, +}; + +/* reg_sfdat_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32(reg, sfdat, swid, 0x00, 24, 8); + +/* reg_sfdat_age_time + * Aging time in seconds + * Min - 10 seconds + * Max - 1,000,000 seconds + * Default is 300 seconds. + * Access: RW + */ +MLXSW_ITEM32(reg, sfdat, age_time, 0x04, 0, 20); + +static inline void mlxsw_reg_sfdat_pack(char *payload, u32 age_time) +{ + MLXSW_REG_ZERO(sfdat, payload); + mlxsw_reg_sfdat_swid_set(payload, 0); + mlxsw_reg_sfdat_age_time_set(payload, age_time); +} + +/* SFD - Switch Filtering Database + * ------------------------------- + * The following register defines the access to the filtering database. + * The register supports querying, adding, removing and modifying the database. + * The access is optimized for bulk updates in which case more than one + * FDB record is present in the same command. + */ +#define MLXSW_REG_SFD_ID 0x200A +#define MLXSW_REG_SFD_BASE_LEN 0x10 /* base length, without records */ +#define MLXSW_REG_SFD_REC_LEN 0x10 /* record length */ +#define MLXSW_REG_SFD_REC_MAX_COUNT 64 +#define MLXSW_REG_SFD_LEN (MLXSW_REG_SFD_BASE_LEN + \ + MLXSW_REG_SFD_REC_LEN * MLXSW_REG_SFD_REC_MAX_COUNT) + +static const struct mlxsw_reg_info mlxsw_reg_sfd = { + .id = MLXSW_REG_SFD_ID, + .len = MLXSW_REG_SFD_LEN, +}; + +/* reg_sfd_swid + * Switch partition ID for queries. Reserved on Write. + * Access: Index + */ +MLXSW_ITEM32(reg, sfd, swid, 0x00, 24, 8); + +enum mlxsw_reg_sfd_op { + /* Dump entire FDB a (process according to record_locator) */ + MLXSW_REG_SFD_OP_QUERY_DUMP = 0, + /* Query records by {MAC, VID/FID} value */ + MLXSW_REG_SFD_OP_QUERY_QUERY = 1, + /* Query and clear activity. Query records by {MAC, VID/FID} value */ + MLXSW_REG_SFD_OP_QUERY_QUERY_AND_CLEAR_ACTIVITY = 2, + /* Test. Response indicates if each of the records could be + * added to the FDB. + */ + MLXSW_REG_SFD_OP_WRITE_TEST = 0, + /* Add/modify. Aged-out records cannot be added. This command removes + * the learning notification of the {MAC, VID/FID}. Response includes + * the entries that were added to the FDB. + */ + MLXSW_REG_SFD_OP_WRITE_EDIT = 1, + /* Remove record by {MAC, VID/FID}. This command also removes + * the learning notification and aged-out notifications + * of the {MAC, VID/FID}. The response provides current (pre-removal) + * entries as non-aged-out. + */ + MLXSW_REG_SFD_OP_WRITE_REMOVE = 2, + /* Remove learned notification by {MAC, VID/FID}. The response provides + * the removed learning notification. + */ + MLXSW_REG_SFD_OP_WRITE_REMOVE_NOTIFICATION = 2, +}; + +/* reg_sfd_op + * Operation. + * Access: OP + */ +MLXSW_ITEM32(reg, sfd, op, 0x04, 30, 2); + +/* reg_sfd_record_locator + * Used for querying the FDB. Use record_locator=0 to initiate the + * query. When a record is returned, a new record_locator is + * returned to be used in the subsequent query. + * Reserved for database update. + * Access: Index + */ +MLXSW_ITEM32(reg, sfd, record_locator, 0x04, 0, 30); + +/* reg_sfd_num_rec + * Request: Number of records to read/add/modify/remove + * Response: Number of records read/added/replaced/removed + * See above description for more details. + * Ranges 0..64 + * Access: RW + */ +MLXSW_ITEM32(reg, sfd, num_rec, 0x08, 0, 8); + +static inline void mlxsw_reg_sfd_pack(char *payload, enum mlxsw_reg_sfd_op op, + u32 record_locator) +{ + MLXSW_REG_ZERO(sfd, payload); + mlxsw_reg_sfd_op_set(payload, op); + mlxsw_reg_sfd_record_locator_set(payload, record_locator); +} + +/* reg_sfd_rec_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_swid, MLXSW_REG_SFD_BASE_LEN, 24, 8, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +enum mlxsw_reg_sfd_rec_type { + MLXSW_REG_SFD_REC_TYPE_UNICAST = 0x0, +}; + +/* reg_sfd_rec_type + * FDB record type. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_type, MLXSW_REG_SFD_BASE_LEN, 20, 4, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +enum mlxsw_reg_sfd_rec_policy { + /* Replacement disabled, aging disabled. */ + MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY = 0, + /* (mlag remote): Replacement enabled, aging disabled, + * learning notification enabled on this port. + */ + MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_MLAG = 1, + /* (ingress device): Replacement enabled, aging enabled. */ + MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS = 3, +}; + +/* reg_sfd_rec_policy + * Policy. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_policy, MLXSW_REG_SFD_BASE_LEN, 18, 2, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +/* reg_sfd_rec_a + * Activity. Set for new static entries. Set for static entries if a frame SMAC + * lookup hits on the entry. + * To clear the a bit, use "query and clear activity" op. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_a, MLXSW_REG_SFD_BASE_LEN, 16, 1, + MLXSW_REG_SFD_REC_LEN, 0x00, false); + +/* reg_sfd_rec_mac + * MAC address. + * Access: Index + */ +MLXSW_ITEM_BUF_INDEXED(reg, sfd, rec_mac, MLXSW_REG_SFD_BASE_LEN, 6, + MLXSW_REG_SFD_REC_LEN, 0x02); + +enum mlxsw_reg_sfd_rec_action { + /* forward */ + MLXSW_REG_SFD_REC_ACTION_NOP = 0, + /* forward and trap, trap_id is FDB_TRAP */ + MLXSW_REG_SFD_REC_ACTION_MIRROR_TO_CPU = 1, + /* trap and do not forward, trap_id is FDB_TRAP */ + MLXSW_REG_SFD_REC_ACTION_TRAP = 3, + MLXSW_REG_SFD_REC_ACTION_DISCARD_ERROR = 15, +}; + +/* reg_sfd_rec_action + * Action to apply on the packet. + * Note: Dynamic entries can only be configured with NOP action. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, rec_action, MLXSW_REG_SFD_BASE_LEN, 28, 4, + MLXSW_REG_SFD_REC_LEN, 0x0C, false); + +/* reg_sfd_uc_sub_port + * LAG sub port. + * Must be 0 if multichannel VEPA is not enabled. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_uc_fid_vid + * Filtering ID or VLAN ID + * For SwitchX and SwitchX-2: + * - Dynamic entries (policy 2,3) use FID + * - Static entries (policy 0) use VID + * - When independent learning is configured, VID=FID + * For Spectrum: use FID for both Dynamic and Static entries. + * VID should not be used. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_uc_system_port + * Unique port identifier for the final destination of the packet. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_system_port, MLXSW_REG_SFD_BASE_LEN, 0, 16, + MLXSW_REG_SFD_REC_LEN, 0x0C, false); + +static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index, + enum mlxsw_reg_sfd_rec_policy policy, + const char *mac, u16 vid, + enum mlxsw_reg_sfd_rec_action action, + u8 local_port) +{ + u8 num_rec = mlxsw_reg_sfd_num_rec_get(payload); + + if (rec_index >= num_rec) + mlxsw_reg_sfd_num_rec_set(payload, rec_index + 1); + mlxsw_reg_sfd_rec_swid_set(payload, rec_index, 0); + mlxsw_reg_sfd_rec_type_set(payload, rec_index, + MLXSW_REG_SFD_REC_TYPE_UNICAST); + mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy); + mlxsw_reg_sfd_rec_mac_memcpy_to(payload, rec_index, mac); + mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0); + mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid); + mlxsw_reg_sfd_rec_action_set(payload, rec_index, action); + mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port); +} + +static inline void +mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index, + char *mac, u16 *p_vid, + u8 *p_local_port) +{ + mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac); + *p_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index); + *p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index); +} + +/* SFN - Switch FDB Notification Register + * ------------------------------------------- + * The switch provides notifications on newly learned FDB entries and + * aged out entries. The notifications can be polled by software. + */ +#define MLXSW_REG_SFN_ID 0x200B +#define MLXSW_REG_SFN_BASE_LEN 0x10 /* base length, without records */ +#define MLXSW_REG_SFN_REC_LEN 0x10 /* record length */ +#define MLXSW_REG_SFN_REC_MAX_COUNT 64 +#define MLXSW_REG_SFN_LEN (MLXSW_REG_SFN_BASE_LEN + \ + MLXSW_REG_SFN_REC_LEN * MLXSW_REG_SFN_REC_MAX_COUNT) + +static const struct mlxsw_reg_info mlxsw_reg_sfn = { + .id = MLXSW_REG_SFN_ID, + .len = MLXSW_REG_SFN_LEN, +}; + +/* reg_sfn_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32(reg, sfn, swid, 0x00, 24, 8); + +/* reg_sfn_num_rec + * Request: Number of learned notifications and aged-out notification + * records requested. + * Response: Number of notification records returned (must be smaller + * than or equal to the value requested) + * Ranges 0..64 + * Access: OP + */ +MLXSW_ITEM32(reg, sfn, num_rec, 0x04, 0, 8); + +static inline void mlxsw_reg_sfn_pack(char *payload) +{ + MLXSW_REG_ZERO(sfn, payload); + mlxsw_reg_sfn_swid_set(payload, 0); + mlxsw_reg_sfn_num_rec_set(payload, MLXSW_REG_SFN_REC_MAX_COUNT); +} + +/* reg_sfn_rec_swid + * Switch partition ID. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, rec_swid, MLXSW_REG_SFN_BASE_LEN, 24, 8, + MLXSW_REG_SFN_REC_LEN, 0x00, false); + +enum mlxsw_reg_sfn_rec_type { + /* MAC addresses learned on a regular port. */ + MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC = 0x5, + /* Aged-out MAC address on a regular port */ + MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7, +}; + +/* reg_sfn_rec_type + * Notification record type. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, rec_type, MLXSW_REG_SFN_BASE_LEN, 20, 4, + MLXSW_REG_SFN_REC_LEN, 0x00, false); + +/* reg_sfn_rec_mac + * MAC address. + * Access: RO + */ +MLXSW_ITEM_BUF_INDEXED(reg, sfn, rec_mac, MLXSW_REG_SFN_BASE_LEN, 6, + MLXSW_REG_SFN_REC_LEN, 0x02); + +/* reg_sfd_mac_sub_port + * VEPA channel on the local port. + * 0 if multichannel VEPA is not enabled. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, mac_sub_port, MLXSW_REG_SFN_BASE_LEN, 16, 8, + MLXSW_REG_SFN_REC_LEN, 0x08, false); + +/* reg_sfd_mac_fid + * Filtering identifier. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, mac_fid, MLXSW_REG_SFN_BASE_LEN, 0, 16, + MLXSW_REG_SFN_REC_LEN, 0x08, false); + +/* reg_sfd_mac_system_port + * Unique port identifier for the final destination of the packet. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, mac_system_port, MLXSW_REG_SFN_BASE_LEN, 0, 16, + MLXSW_REG_SFN_REC_LEN, 0x0C, false); + +static inline void mlxsw_reg_sfn_mac_unpack(char *payload, int rec_index, + char *mac, u16 *p_vid, + u8 *p_local_port) +{ + mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac); + *p_vid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index); + *p_local_port = mlxsw_reg_sfn_mac_system_port_get(payload, rec_index); +} + /* SPMS - Switch Port MSTP/RSTP State Register * ------------------------------------------- * Configures the spanning tree state of a physical port. */ -#define MLXSW_REG_SPMS_ID 0x200d +#define MLXSW_REG_SPMS_ID 0x200D #define MLXSW_REG_SPMS_LEN 0x404 static const struct mlxsw_reg_info mlxsw_reg_spms = { @@ -243,20 +540,166 @@ enum mlxsw_reg_spms_state { */ MLXSW_ITEM_BIT_ARRAY(reg, spms, state, 0x04, 0x400, 2); -static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port, u16 vid, - enum mlxsw_reg_spms_state state) +static inline void mlxsw_reg_spms_pack(char *payload, u8 local_port) { MLXSW_REG_ZERO(spms, payload); mlxsw_reg_spms_local_port_set(payload, local_port); +} + +static inline void mlxsw_reg_spms_vid_pack(char *payload, u16 vid, + enum mlxsw_reg_spms_state state) +{ mlxsw_reg_spms_state_set(payload, vid, state); } +/* SPVID - Switch Port VID + * ----------------------- + * The switch port VID configures the default VID for a port. + */ +#define MLXSW_REG_SPVID_ID 0x200E +#define MLXSW_REG_SPVID_LEN 0x08 + +static const struct mlxsw_reg_info mlxsw_reg_spvid = { + .id = MLXSW_REG_SPVID_ID, + .len = MLXSW_REG_SPVID_LEN, +}; + +/* reg_spvid_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, spvid, local_port, 0x00, 16, 8); + +/* reg_spvid_sub_port + * Virtual port within the physical port. + * Should be set to 0 when virtual ports are not enabled on the port. + * Access: Index + */ +MLXSW_ITEM32(reg, spvid, sub_port, 0x00, 8, 8); + +/* reg_spvid_pvid + * Port default VID + * Access: RW + */ +MLXSW_ITEM32(reg, spvid, pvid, 0x04, 0, 12); + +static inline void mlxsw_reg_spvid_pack(char *payload, u8 local_port, u16 pvid) +{ + MLXSW_REG_ZERO(spvid, payload); + mlxsw_reg_spvid_local_port_set(payload, local_port); + mlxsw_reg_spvid_pvid_set(payload, pvid); +} + +/* SPVM - Switch Port VLAN Membership + * ---------------------------------- + * The Switch Port VLAN Membership register configures the VLAN membership + * of a port in a VLAN denoted by VID. VLAN membership is managed per + * virtual port. The register can be used to add and remove VID(s) from a port. + */ +#define MLXSW_REG_SPVM_ID 0x200F +#define MLXSW_REG_SPVM_BASE_LEN 0x04 /* base length, without records */ +#define MLXSW_REG_SPVM_REC_LEN 0x04 /* record length */ +#define MLXSW_REG_SPVM_REC_MAX_COUNT 256 +#define MLXSW_REG_SPVM_LEN (MLXSW_REG_SPVM_BASE_LEN + \ + MLXSW_REG_SPVM_REC_LEN * MLXSW_REG_SPVM_REC_MAX_COUNT) + +static const struct mlxsw_reg_info mlxsw_reg_spvm = { + .id = MLXSW_REG_SPVM_ID, + .len = MLXSW_REG_SPVM_LEN, +}; + +/* reg_spvm_pt + * Priority tagged. If this bit is set, packets forwarded to the port with + * untagged VLAN membership (u bit is set) will be tagged with priority tag + * (VID=0) + * Access: RW + */ +MLXSW_ITEM32(reg, spvm, pt, 0x00, 31, 1); + +/* reg_spvm_pte + * Priority Tagged Update Enable. On Write operations, if this bit is cleared, + * the pt bit will NOT be updated. To update the pt bit, pte must be set. + * Access: WO + */ +MLXSW_ITEM32(reg, spvm, pte, 0x00, 30, 1); + +/* reg_spvm_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, spvm, local_port, 0x00, 16, 8); + +/* reg_spvm_sub_port + * Virtual port within the physical port. + * Should be set to 0 when virtual ports are not enabled on the port. + * Access: Index + */ +MLXSW_ITEM32(reg, spvm, sub_port, 0x00, 8, 8); + +/* reg_spvm_num_rec + * Number of records to update. Each record contains: i, e, u, vid. + * Access: OP + */ +MLXSW_ITEM32(reg, spvm, num_rec, 0x00, 0, 8); + +/* reg_spvm_rec_i + * Ingress membership in VLAN ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_i, + MLXSW_REG_SPVM_BASE_LEN, 14, 1, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +/* reg_spvm_rec_e + * Egress membership in VLAN ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_e, + MLXSW_REG_SPVM_BASE_LEN, 13, 1, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +/* reg_spvm_rec_u + * Untagged - port is an untagged member - egress transmission uses untagged + * frames on VID<n> + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_u, + MLXSW_REG_SPVM_BASE_LEN, 12, 1, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +/* reg_spvm_rec_vid + * Egress membership in VLAN ID. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvm, rec_vid, + MLXSW_REG_SPVM_BASE_LEN, 0, 12, + MLXSW_REG_SPVM_REC_LEN, 0, false); + +static inline void mlxsw_reg_spvm_pack(char *payload, u8 local_port, + u16 vid_begin, u16 vid_end, + bool is_member, bool untagged) +{ + int size = vid_end - vid_begin + 1; + int i; + + MLXSW_REG_ZERO(spvm, payload); + mlxsw_reg_spvm_local_port_set(payload, local_port); + mlxsw_reg_spvm_num_rec_set(payload, size); + + for (i = 0; i < size; i++) { + mlxsw_reg_spvm_rec_i_set(payload, i, is_member); + mlxsw_reg_spvm_rec_e_set(payload, i, is_member); + mlxsw_reg_spvm_rec_u_set(payload, i, untagged); + mlxsw_reg_spvm_rec_vid_set(payload, i, vid_begin + i); + } +} + /* SFGC - Switch Flooding Group Configuration * ------------------------------------------ * The following register controls the association of flooding tables and MIDs * to packet types used for flooding. */ -#define MLXSW_REG_SFGC_ID 0x2011 +#define MLXSW_REG_SFGC_ID 0x2011 #define MLXSW_REG_SFGC_LEN 0x10 static const struct mlxsw_reg_info mlxsw_reg_sfgc = { @@ -265,13 +708,15 @@ static const struct mlxsw_reg_info mlxsw_reg_sfgc = { }; enum mlxsw_reg_sfgc_type { - MLXSW_REG_SFGC_TYPE_BROADCAST = 0, - MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST = 1, - MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4 = 2, - MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6 = 3, - MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP = 5, - MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL = 6, - MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST = 7, + MLXSW_REG_SFGC_TYPE_BROADCAST, + MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST, + MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4, + MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6, + MLXSW_REG_SFGC_TYPE_RESERVED, + MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP, + MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL, + MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST, + MLXSW_REG_SFGC_TYPE_MAX, }; /* reg_sfgc_type @@ -408,7 +853,7 @@ static inline void mlxsw_reg_sftr_pack(char *payload, unsigned int flood_table, unsigned int index, enum mlxsw_flood_table_type table_type, - unsigned int range) + unsigned int range, u8 port, bool set) { MLXSW_REG_ZERO(sftr, payload); mlxsw_reg_sftr_swid_set(payload, 0); @@ -416,8 +861,8 @@ static inline void mlxsw_reg_sftr_pack(char *payload, mlxsw_reg_sftr_index_set(payload, index); mlxsw_reg_sftr_table_type_set(payload, table_type); mlxsw_reg_sftr_range_set(payload, range); - mlxsw_reg_sftr_port_set(payload, MLXSW_PORT_CPU_PORT, 1); - mlxsw_reg_sftr_port_mask_set(payload, MLXSW_PORT_CPU_PORT, 1); + mlxsw_reg_sftr_port_set(payload, port, set); + mlxsw_reg_sftr_port_mask_set(payload, port, 1); } /* SPMLR - Switch Port MAC Learning Register @@ -473,6 +918,285 @@ static inline void mlxsw_reg_spmlr_pack(char *payload, u8 local_port, mlxsw_reg_spmlr_learn_mode_set(payload, mode); } +/* SVFA - Switch VID to FID Allocation Register + * -------------------------------------------- + * Controls the VID to FID mapping and {Port, VID} to FID mapping for + * virtualized ports. + */ +#define MLXSW_REG_SVFA_ID 0x201C +#define MLXSW_REG_SVFA_LEN 0x10 + +static const struct mlxsw_reg_info mlxsw_reg_svfa = { + .id = MLXSW_REG_SVFA_ID, + .len = MLXSW_REG_SVFA_LEN, +}; + +/* reg_svfa_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32(reg, svfa, swid, 0x00, 24, 8); + +/* reg_svfa_local_port + * Local port number. + * Access: Index + * + * Note: Reserved for 802.1Q FIDs. + */ +MLXSW_ITEM32(reg, svfa, local_port, 0x00, 16, 8); + +enum mlxsw_reg_svfa_mt { + MLXSW_REG_SVFA_MT_VID_TO_FID, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, +}; + +/* reg_svfa_mapping_table + * Mapping table: + * 0 - VID to FID + * 1 - {Port, VID} to FID + * Access: Index + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, mapping_table, 0x00, 8, 3); + +/* reg_svfa_v + * Valid. + * Valid if set. + * Access: RW + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, v, 0x00, 0, 1); + +/* reg_svfa_fid + * Filtering ID. + * Access: RW + */ +MLXSW_ITEM32(reg, svfa, fid, 0x04, 16, 16); + +/* reg_svfa_vid + * VLAN ID. + * Access: Index + */ +MLXSW_ITEM32(reg, svfa, vid, 0x04, 0, 12); + +/* reg_svfa_counter_set_type + * Counter set type for flow counters. + * Access: RW + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, counter_set_type, 0x08, 24, 8); + +/* reg_svfa_counter_index + * Counter index for flow counters. + * Access: RW + * + * Note: Reserved for SwitchX-2. + */ +MLXSW_ITEM32(reg, svfa, counter_index, 0x08, 0, 24); + +static inline void mlxsw_reg_svfa_pack(char *payload, u8 local_port, + enum mlxsw_reg_svfa_mt mt, bool valid, + u16 fid, u16 vid) +{ + MLXSW_REG_ZERO(svfa, payload); + local_port = mt == MLXSW_REG_SVFA_MT_VID_TO_FID ? 0 : local_port; + mlxsw_reg_svfa_swid_set(payload, 0); + mlxsw_reg_svfa_local_port_set(payload, local_port); + mlxsw_reg_svfa_mapping_table_set(payload, mt); + mlxsw_reg_svfa_v_set(payload, valid); + mlxsw_reg_svfa_fid_set(payload, fid); + mlxsw_reg_svfa_vid_set(payload, vid); +} + +/* SVPE - Switch Virtual-Port Enabling Register + * -------------------------------------------- + * Enables port virtualization. + */ +#define MLXSW_REG_SVPE_ID 0x201E +#define MLXSW_REG_SVPE_LEN 0x4 + +static const struct mlxsw_reg_info mlxsw_reg_svpe = { + .id = MLXSW_REG_SVPE_ID, + .len = MLXSW_REG_SVPE_LEN, +}; + +/* reg_svpe_local_port + * Local port number + * Access: Index + * + * Note: CPU port is not supported (uses VLAN mode only). + */ +MLXSW_ITEM32(reg, svpe, local_port, 0x00, 16, 8); + +/* reg_svpe_vp_en + * Virtual port enable. + * 0 - Disable, VLAN mode (VID to FID). + * 1 - Enable, Virtual port mode ({Port, VID} to FID). + * Access: RW + */ +MLXSW_ITEM32(reg, svpe, vp_en, 0x00, 8, 1); + +static inline void mlxsw_reg_svpe_pack(char *payload, u8 local_port, + bool enable) +{ + MLXSW_REG_ZERO(svpe, payload); + mlxsw_reg_svpe_local_port_set(payload, local_port); + mlxsw_reg_svpe_vp_en_set(payload, enable); +} + +/* SFMR - Switch FID Management Register + * ------------------------------------- + * Creates and configures FIDs. + */ +#define MLXSW_REG_SFMR_ID 0x201F +#define MLXSW_REG_SFMR_LEN 0x18 + +static const struct mlxsw_reg_info mlxsw_reg_sfmr = { + .id = MLXSW_REG_SFMR_ID, + .len = MLXSW_REG_SFMR_LEN, +}; + +enum mlxsw_reg_sfmr_op { + MLXSW_REG_SFMR_OP_CREATE_FID, + MLXSW_REG_SFMR_OP_DESTROY_FID, +}; + +/* reg_sfmr_op + * Operation. + * 0 - Create or edit FID. + * 1 - Destroy FID. + * Access: WO + */ +MLXSW_ITEM32(reg, sfmr, op, 0x00, 24, 4); + +/* reg_sfmr_fid + * Filtering ID. + * Access: Index + */ +MLXSW_ITEM32(reg, sfmr, fid, 0x00, 0, 16); + +/* reg_sfmr_fid_offset + * FID offset. + * Used to point into the flooding table selected by SFGC register if + * the table is of type FID-Offset. Otherwise, this field is reserved. + * Access: RW + */ +MLXSW_ITEM32(reg, sfmr, fid_offset, 0x08, 0, 16); + +/* reg_sfmr_vtfp + * Valid Tunnel Flood Pointer. + * If not set, then nve_tunnel_flood_ptr is reserved and considered NULL. + * Access: RW + * + * Note: Reserved for 802.1Q FIDs. + */ +MLXSW_ITEM32(reg, sfmr, vtfp, 0x0C, 31, 1); + +/* reg_sfmr_nve_tunnel_flood_ptr + * Underlay Flooding and BC Pointer. + * Used as a pointer to the first entry of the group based link lists of + * flooding or BC entries (for NVE tunnels). + * Access: RW + */ +MLXSW_ITEM32(reg, sfmr, nve_tunnel_flood_ptr, 0x0C, 0, 24); + +/* reg_sfmr_vv + * VNI Valid. + * If not set, then vni is reserved. + * Access: RW + * + * Note: Reserved for 802.1Q FIDs. + */ +MLXSW_ITEM32(reg, sfmr, vv, 0x10, 31, 1); + +/* reg_sfmr_vni + * Virtual Network Identifier. + * Access: RW + * + * Note: A given VNI can only be assigned to one FID. + */ +MLXSW_ITEM32(reg, sfmr, vni, 0x10, 0, 24); + +static inline void mlxsw_reg_sfmr_pack(char *payload, + enum mlxsw_reg_sfmr_op op, u16 fid, + u16 fid_offset) +{ + MLXSW_REG_ZERO(sfmr, payload); + mlxsw_reg_sfmr_op_set(payload, op); + mlxsw_reg_sfmr_fid_set(payload, fid); + mlxsw_reg_sfmr_fid_offset_set(payload, fid_offset); + mlxsw_reg_sfmr_vtfp_set(payload, false); + mlxsw_reg_sfmr_vv_set(payload, false); +} + +/* SPVMLR - Switch Port VLAN MAC Learning Register + * ----------------------------------------------- + * Controls the switch MAC learning policy per {Port, VID}. + */ +#define MLXSW_REG_SPVMLR_ID 0x2020 +#define MLXSW_REG_SPVMLR_BASE_LEN 0x04 /* base length, without records */ +#define MLXSW_REG_SPVMLR_REC_LEN 0x04 /* record length */ +#define MLXSW_REG_SPVMLR_REC_MAX_COUNT 256 +#define MLXSW_REG_SPVMLR_LEN (MLXSW_REG_SPVMLR_BASE_LEN + \ + MLXSW_REG_SPVMLR_REC_LEN * \ + MLXSW_REG_SPVMLR_REC_MAX_COUNT) + +static const struct mlxsw_reg_info mlxsw_reg_spvmlr = { + .id = MLXSW_REG_SPVMLR_ID, + .len = MLXSW_REG_SPVMLR_LEN, +}; + +/* reg_spvmlr_local_port + * Local ingress port. + * Access: Index + * + * Note: CPU port is not supported. + */ +MLXSW_ITEM32(reg, spvmlr, local_port, 0x00, 16, 8); + +/* reg_spvmlr_num_rec + * Number of records to update. + * Access: OP + */ +MLXSW_ITEM32(reg, spvmlr, num_rec, 0x00, 0, 8); + +/* reg_spvmlr_rec_learn_enable + * 0 - Disable learning for {Port, VID}. + * 1 - Enable learning for {Port, VID}. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, spvmlr, rec_learn_enable, MLXSW_REG_SPVMLR_BASE_LEN, + 31, 1, MLXSW_REG_SPVMLR_REC_LEN, 0x00, false); + +/* reg_spvmlr_rec_vid + * VLAN ID to be added/removed from port or for querying. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, spvmlr, rec_vid, MLXSW_REG_SPVMLR_BASE_LEN, 0, 12, + MLXSW_REG_SPVMLR_REC_LEN, 0x00, false); + +static inline void mlxsw_reg_spvmlr_pack(char *payload, u8 local_port, + u16 vid_begin, u16 vid_end, + bool learn_enable) +{ + int num_rec = vid_end - vid_begin + 1; + int i; + + WARN_ON(num_rec < 1 || num_rec > MLXSW_REG_SPVMLR_REC_MAX_COUNT); + + MLXSW_REG_ZERO(spvmlr, payload); + mlxsw_reg_spvmlr_local_port_set(payload, local_port); + mlxsw_reg_spvmlr_num_rec_set(payload, num_rec); + + for (i = 0; i < num_rec; i++) { + mlxsw_reg_spvmlr_rec_learn_enable_set(payload, i, learn_enable); + mlxsw_reg_spvmlr_rec_vid_set(payload, i, vid_begin + i); + } +} + /* PMLP - Ports Module to Local Port Register * ------------------------------------------ * Configures the assignment of modules to local ports. @@ -1008,12 +1732,88 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port) mlxsw_reg_ppcnt_prio_tc_set(payload, 0); } +/* PBMC - Port Buffer Management Control Register + * ---------------------------------------------- + * The PBMC register configures and retrieves the port packet buffer + * allocation for different Prios, and the Pause threshold management. + */ +#define MLXSW_REG_PBMC_ID 0x500C +#define MLXSW_REG_PBMC_LEN 0x68 + +static const struct mlxsw_reg_info mlxsw_reg_pbmc = { + .id = MLXSW_REG_PBMC_ID, + .len = MLXSW_REG_PBMC_LEN, +}; + +/* reg_pbmc_local_port + * Local port number. + * Access: Index + */ +MLXSW_ITEM32(reg, pbmc, local_port, 0x00, 16, 8); + +/* reg_pbmc_xoff_timer_value + * When device generates a pause frame, it uses this value as the pause + * timer (time for the peer port to pause in quota-512 bit time). + * Access: RW + */ +MLXSW_ITEM32(reg, pbmc, xoff_timer_value, 0x04, 16, 16); + +/* reg_pbmc_xoff_refresh + * The time before a new pause frame should be sent to refresh the pause RW + * state. Using the same units as xoff_timer_value above (in quota-512 bit + * time). + * Access: RW + */ +MLXSW_ITEM32(reg, pbmc, xoff_refresh, 0x04, 0, 16); + +/* reg_pbmc_buf_lossy + * The field indicates if the buffer is lossy. + * 0 - Lossless + * 1 - Lossy + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, pbmc, buf_lossy, 0x0C, 25, 1, 0x08, 0x00, false); + +/* reg_pbmc_buf_epsb + * Eligible for Port Shared buffer. + * If epsb is set, packets assigned to buffer are allowed to insert the port + * shared buffer. + * When buf_lossy is MLXSW_REG_PBMC_LOSSY_LOSSY this field is reserved. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, pbmc, buf_epsb, 0x0C, 24, 1, 0x08, 0x00, false); + +/* reg_pbmc_buf_size + * The part of the packet buffer array is allocated for the specific buffer. + * Units are represented in cells. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, pbmc, buf_size, 0x0C, 0, 16, 0x08, 0x00, false); + +static inline void mlxsw_reg_pbmc_pack(char *payload, u8 local_port, + u16 xoff_timer_value, u16 xoff_refresh) +{ + MLXSW_REG_ZERO(pbmc, payload); + mlxsw_reg_pbmc_local_port_set(payload, local_port); + mlxsw_reg_pbmc_xoff_timer_value_set(payload, xoff_timer_value); + mlxsw_reg_pbmc_xoff_refresh_set(payload, xoff_refresh); +} + +static inline void mlxsw_reg_pbmc_lossy_buffer_pack(char *payload, + int buf_index, + u16 size) +{ + mlxsw_reg_pbmc_buf_lossy_set(payload, buf_index, 1); + mlxsw_reg_pbmc_buf_epsb_set(payload, buf_index, 0); + mlxsw_reg_pbmc_buf_size_set(payload, buf_index, size); +} + /* PSPA - Port Switch Partition Allocation * --------------------------------------- * Controls the association of a port with a switch partition and enables * configuring ports as stacking ports. */ -#define MLXSW_REG_PSPA_ID 0x500d +#define MLXSW_REG_PSPA_ID 0x500D #define MLXSW_REG_PSPA_LEN 0x8 static const struct mlxsw_reg_info mlxsw_reg_pspa = { @@ -1074,8 +1874,11 @@ MLXSW_ITEM32(reg, htgt, swid, 0x00, 24, 8); */ MLXSW_ITEM32(reg, htgt, type, 0x00, 8, 4); -#define MLXSW_REG_HTGT_TRAP_GROUP_EMAD 0x0 -#define MLXSW_REG_HTGT_TRAP_GROUP_RX 0x1 +enum mlxsw_reg_htgt_trap_group { + MLXSW_REG_HTGT_TRAP_GROUP_EMAD, + MLXSW_REG_HTGT_TRAP_GROUP_RX, + MLXSW_REG_HTGT_TRAP_GROUP_CTRL, +}; /* reg_htgt_trap_group * Trap group number. User defined number specifying which trap groups @@ -1142,6 +1945,7 @@ MLXSW_ITEM32(reg, htgt, local_path_cpu_tclass, 0x10, 16, 6); #define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD 0x15 #define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX 0x14 +#define MLXSW_REG_HTGT_LOCAL_PATH_RDQ_CTRL 0x13 /* reg_htgt_local_path_rdq * Receive descriptor queue (RDQ) to use for the trap group. @@ -1149,21 +1953,29 @@ MLXSW_ITEM32(reg, htgt, local_path_cpu_tclass, 0x10, 16, 6); */ MLXSW_ITEM32(reg, htgt, local_path_rdq, 0x10, 0, 6); -static inline void mlxsw_reg_htgt_pack(char *payload, u8 trap_group) +static inline void mlxsw_reg_htgt_pack(char *payload, + enum mlxsw_reg_htgt_trap_group group) { u8 swid, rdq; MLXSW_REG_ZERO(htgt, payload); - if (MLXSW_REG_HTGT_TRAP_GROUP_EMAD == trap_group) { + switch (group) { + case MLXSW_REG_HTGT_TRAP_GROUP_EMAD: swid = MLXSW_PORT_SWID_ALL_SWIDS; rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_EMAD; - } else { + break; + case MLXSW_REG_HTGT_TRAP_GROUP_RX: swid = 0; rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_RX; + break; + case MLXSW_REG_HTGT_TRAP_GROUP_CTRL: + swid = 0; + rdq = MLXSW_REG_HTGT_LOCAL_PATH_RDQ_CTRL; + break; } mlxsw_reg_htgt_swid_set(payload, swid); mlxsw_reg_htgt_type_set(payload, MLXSW_REG_HTGT_PATH_TYPE_LOCAL); - mlxsw_reg_htgt_trap_group_set(payload, trap_group); + mlxsw_reg_htgt_trap_group_set(payload, group); mlxsw_reg_htgt_pide_set(payload, MLXSW_REG_HTGT_POLICER_DISABLE); mlxsw_reg_htgt_pid_set(payload, 0); mlxsw_reg_htgt_mirror_action_set(payload, MLXSW_REG_HTGT_TRAP_TO_CPU); @@ -1254,17 +2066,290 @@ enum { */ MLXSW_ITEM32(reg, hpkt, ctrl, 0x04, 16, 2); -static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, - u8 trap_group, u16 trap_id) +static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) { + enum mlxsw_reg_htgt_trap_group trap_group; + MLXSW_REG_ZERO(hpkt, payload); mlxsw_reg_hpkt_ack_set(payload, MLXSW_REG_HPKT_ACK_NOT_REQUIRED); mlxsw_reg_hpkt_action_set(payload, action); + switch (trap_id) { + case MLXSW_TRAP_ID_ETHEMAD: + case MLXSW_TRAP_ID_PUDE: + trap_group = MLXSW_REG_HTGT_TRAP_GROUP_EMAD; + break; + default: + trap_group = MLXSW_REG_HTGT_TRAP_GROUP_RX; + break; + } mlxsw_reg_hpkt_trap_group_set(payload, trap_group); mlxsw_reg_hpkt_trap_id_set(payload, trap_id); mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT); } +/* SBPR - Shared Buffer Pools Register + * ----------------------------------- + * The SBPR configures and retrieves the shared buffer pools and configuration. + */ +#define MLXSW_REG_SBPR_ID 0xB001 +#define MLXSW_REG_SBPR_LEN 0x14 + +static const struct mlxsw_reg_info mlxsw_reg_sbpr = { + .id = MLXSW_REG_SBPR_ID, + .len = MLXSW_REG_SBPR_LEN, +}; + +enum mlxsw_reg_sbpr_dir { + MLXSW_REG_SBPR_DIR_INGRESS, + MLXSW_REG_SBPR_DIR_EGRESS, +}; + +/* reg_sbpr_dir + * Direction. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpr, dir, 0x00, 24, 2); + +/* reg_sbpr_pool + * Pool index. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpr, pool, 0x00, 0, 4); + +/* reg_sbpr_size + * Pool size in buffer cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbpr, size, 0x04, 0, 24); + +enum mlxsw_reg_sbpr_mode { + MLXSW_REG_SBPR_MODE_STATIC, + MLXSW_REG_SBPR_MODE_DYNAMIC, +}; + +/* reg_sbpr_mode + * Pool quota calculation mode. + * Access: RW + */ +MLXSW_ITEM32(reg, sbpr, mode, 0x08, 0, 4); + +static inline void mlxsw_reg_sbpr_pack(char *payload, u8 pool, + enum mlxsw_reg_sbpr_dir dir, + enum mlxsw_reg_sbpr_mode mode, u32 size) +{ + MLXSW_REG_ZERO(sbpr, payload); + mlxsw_reg_sbpr_pool_set(payload, pool); + mlxsw_reg_sbpr_dir_set(payload, dir); + mlxsw_reg_sbpr_mode_set(payload, mode); + mlxsw_reg_sbpr_size_set(payload, size); +} + +/* SBCM - Shared Buffer Class Management Register + * ---------------------------------------------- + * The SBCM register configures and retrieves the shared buffer allocation + * and configuration according to Port-PG, including the binding to pool + * and definition of the associated quota. + */ +#define MLXSW_REG_SBCM_ID 0xB002 +#define MLXSW_REG_SBCM_LEN 0x28 + +static const struct mlxsw_reg_info mlxsw_reg_sbcm = { + .id = MLXSW_REG_SBCM_ID, + .len = MLXSW_REG_SBCM_LEN, +}; + +/* reg_sbcm_local_port + * Local port number. + * For Ingress: excludes CPU port and Router port + * For Egress: excludes IP Router + * Access: Index + */ +MLXSW_ITEM32(reg, sbcm, local_port, 0x00, 16, 8); + +/* reg_sbcm_pg_buff + * PG buffer - Port PG (dir=ingress) / traffic class (dir=egress) + * For PG buffer: range is 0..cap_max_pg_buffers - 1 + * For traffic class: range is 0..cap_max_tclass - 1 + * Note that when traffic class is in MC aware mode then the traffic + * classes which are MC aware cannot be configured. + * Access: Index + */ +MLXSW_ITEM32(reg, sbcm, pg_buff, 0x00, 8, 6); + +enum mlxsw_reg_sbcm_dir { + MLXSW_REG_SBCM_DIR_INGRESS, + MLXSW_REG_SBCM_DIR_EGRESS, +}; + +/* reg_sbcm_dir + * Direction. + * Access: Index + */ +MLXSW_ITEM32(reg, sbcm, dir, 0x00, 0, 2); + +/* reg_sbcm_min_buff + * Minimum buffer size for the limiter, in cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbcm, min_buff, 0x18, 0, 24); + +/* reg_sbcm_max_buff + * When the pool associated to the port-pg/tclass is configured to + * static, Maximum buffer size for the limiter configured in cells. + * When the pool associated to the port-pg/tclass is configured to + * dynamic, the max_buff holds the "alpha" parameter, supporting + * the following values: + * 0: 0 + * i: (1/128)*2^(i-1), for i=1..14 + * 0xFF: Infinity + * Access: RW + */ +MLXSW_ITEM32(reg, sbcm, max_buff, 0x1C, 0, 24); + +/* reg_sbcm_pool + * Association of the port-priority to a pool. + * Access: RW + */ +MLXSW_ITEM32(reg, sbcm, pool, 0x24, 0, 4); + +static inline void mlxsw_reg_sbcm_pack(char *payload, u8 local_port, u8 pg_buff, + enum mlxsw_reg_sbcm_dir dir, + u32 min_buff, u32 max_buff, u8 pool) +{ + MLXSW_REG_ZERO(sbcm, payload); + mlxsw_reg_sbcm_local_port_set(payload, local_port); + mlxsw_reg_sbcm_pg_buff_set(payload, pg_buff); + mlxsw_reg_sbcm_dir_set(payload, dir); + mlxsw_reg_sbcm_min_buff_set(payload, min_buff); + mlxsw_reg_sbcm_max_buff_set(payload, max_buff); + mlxsw_reg_sbcm_pool_set(payload, pool); +} + +/* SBPM - Shared Buffer Class Management Register + * ---------------------------------------------- + * The SBPM register configures and retrieves the shared buffer allocation + * and configuration according to Port-Pool, including the definition + * of the associated quota. + */ +#define MLXSW_REG_SBPM_ID 0xB003 +#define MLXSW_REG_SBPM_LEN 0x28 + +static const struct mlxsw_reg_info mlxsw_reg_sbpm = { + .id = MLXSW_REG_SBPM_ID, + .len = MLXSW_REG_SBPM_LEN, +}; + +/* reg_sbpm_local_port + * Local port number. + * For Ingress: excludes CPU port and Router port + * For Egress: excludes IP Router + * Access: Index + */ +MLXSW_ITEM32(reg, sbpm, local_port, 0x00, 16, 8); + +/* reg_sbpm_pool + * The pool associated to quota counting on the local_port. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpm, pool, 0x00, 8, 4); + +enum mlxsw_reg_sbpm_dir { + MLXSW_REG_SBPM_DIR_INGRESS, + MLXSW_REG_SBPM_DIR_EGRESS, +}; + +/* reg_sbpm_dir + * Direction. + * Access: Index + */ +MLXSW_ITEM32(reg, sbpm, dir, 0x00, 0, 2); + +/* reg_sbpm_min_buff + * Minimum buffer size for the limiter, in cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbpm, min_buff, 0x18, 0, 24); + +/* reg_sbpm_max_buff + * When the pool associated to the port-pg/tclass is configured to + * static, Maximum buffer size for the limiter configured in cells. + * When the pool associated to the port-pg/tclass is configured to + * dynamic, the max_buff holds the "alpha" parameter, supporting + * the following values: + * 0: 0 + * i: (1/128)*2^(i-1), for i=1..14 + * 0xFF: Infinity + * Access: RW + */ +MLXSW_ITEM32(reg, sbpm, max_buff, 0x1C, 0, 24); + +static inline void mlxsw_reg_sbpm_pack(char *payload, u8 local_port, u8 pool, + enum mlxsw_reg_sbpm_dir dir, + u32 min_buff, u32 max_buff) +{ + MLXSW_REG_ZERO(sbpm, payload); + mlxsw_reg_sbpm_local_port_set(payload, local_port); + mlxsw_reg_sbpm_pool_set(payload, pool); + mlxsw_reg_sbpm_dir_set(payload, dir); + mlxsw_reg_sbpm_min_buff_set(payload, min_buff); + mlxsw_reg_sbpm_max_buff_set(payload, max_buff); +} + +/* SBMM - Shared Buffer Multicast Management Register + * -------------------------------------------------- + * The SBMM register configures and retrieves the shared buffer allocation + * and configuration for MC packets according to Switch-Priority, including + * the binding to pool and definition of the associated quota. + */ +#define MLXSW_REG_SBMM_ID 0xB004 +#define MLXSW_REG_SBMM_LEN 0x28 + +static const struct mlxsw_reg_info mlxsw_reg_sbmm = { + .id = MLXSW_REG_SBMM_ID, + .len = MLXSW_REG_SBMM_LEN, +}; + +/* reg_sbmm_prio + * Switch Priority. + * Access: Index + */ +MLXSW_ITEM32(reg, sbmm, prio, 0x00, 8, 4); + +/* reg_sbmm_min_buff + * Minimum buffer size for the limiter, in cells. + * Access: RW + */ +MLXSW_ITEM32(reg, sbmm, min_buff, 0x18, 0, 24); + +/* reg_sbmm_max_buff + * When the pool associated to the port-pg/tclass is configured to + * static, Maximum buffer size for the limiter configured in cells. + * When the pool associated to the port-pg/tclass is configured to + * dynamic, the max_buff holds the "alpha" parameter, supporting + * the following values: + * 0: 0 + * i: (1/128)*2^(i-1), for i=1..14 + * 0xFF: Infinity + * Access: RW + */ +MLXSW_ITEM32(reg, sbmm, max_buff, 0x1C, 0, 24); + +/* reg_sbmm_pool + * Association of the port-priority to a pool. + * Access: RW + */ +MLXSW_ITEM32(reg, sbmm, pool, 0x24, 0, 4); + +static inline void mlxsw_reg_sbmm_pack(char *payload, u8 prio, u32 min_buff, + u32 max_buff, u8 pool) +{ + MLXSW_REG_ZERO(sbmm, payload); + mlxsw_reg_sbmm_prio_set(payload, prio); + mlxsw_reg_sbmm_min_buff_set(payload, min_buff); + mlxsw_reg_sbmm_max_buff_set(payload, max_buff); + mlxsw_reg_sbmm_pool_set(payload, pool); +} + static inline const char *mlxsw_reg_id_str(u16 reg_id) { switch (reg_id) { @@ -1272,18 +2357,34 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SGCR"; case MLXSW_REG_SPAD_ID: return "SPAD"; - case MLXSW_REG_SMID_ID: - return "SMID"; case MLXSW_REG_SSPR_ID: return "SSPR"; + case MLXSW_REG_SFDAT_ID: + return "SFDAT"; + case MLXSW_REG_SFD_ID: + return "SFD"; + case MLXSW_REG_SFN_ID: + return "SFN"; case MLXSW_REG_SPMS_ID: return "SPMS"; + case MLXSW_REG_SPVID_ID: + return "SPVID"; + case MLXSW_REG_SPVM_ID: + return "SPVM"; case MLXSW_REG_SFGC_ID: return "SFGC"; case MLXSW_REG_SFTR_ID: return "SFTR"; case MLXSW_REG_SPMLR_ID: return "SPMLR"; + case MLXSW_REG_SVFA_ID: + return "SVFA"; + case MLXSW_REG_SVPE_ID: + return "SVPE"; + case MLXSW_REG_SFMR_ID: + return "SFMR"; + case MLXSW_REG_SPVMLR_ID: + return "SPVMLR"; case MLXSW_REG_PMLP_ID: return "PMLP"; case MLXSW_REG_PMTU_ID: @@ -1296,12 +2397,22 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "PAOS"; case MLXSW_REG_PPCNT_ID: return "PPCNT"; + case MLXSW_REG_PBMC_ID: + return "PBMC"; case MLXSW_REG_PSPA_ID: return "PSPA"; case MLXSW_REG_HTGT_ID: return "HTGT"; case MLXSW_REG_HPKT_ID: return "HPKT"; + case MLXSW_REG_SBPR_ID: + return "SBPR"; + case MLXSW_REG_SBCM_ID: + return "SBCM"; + case MLXSW_REG_SBPM_ID: + return "SBPM"; + case MLXSW_REG_SBMM_ID: + return "SBMM"; default: return "*UNKNOWN*"; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c new file mode 100644 index 000000000000..6e9906d8d149 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -0,0 +1,1948 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum.c + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com> + * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com> + * Copyright (c) 2015 Elad Raz <eladr@mellanox.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/skbuff.h> +#include <linux/if_vlan.h> +#include <linux/if_bridge.h> +#include <linux/workqueue.h> +#include <linux/jiffies.h> +#include <linux/bitops.h> +#include <net/switchdev.h> +#include <generated/utsrelease.h> + +#include "spectrum.h" +#include "core.h" +#include "reg.h" +#include "port.h" +#include "trap.h" +#include "txheader.h" + +static const char mlxsw_sp_driver_name[] = "mlxsw_spectrum"; +static const char mlxsw_sp_driver_version[] = "1.0"; + +/* tx_hdr_version + * Tx header version. + * Must be set to 1. + */ +MLXSW_ITEM32(tx, hdr, version, 0x00, 28, 4); + +/* tx_hdr_ctl + * Packet control type. + * 0 - Ethernet control (e.g. EMADs, LACP) + * 1 - Ethernet data + */ +MLXSW_ITEM32(tx, hdr, ctl, 0x00, 26, 2); + +/* tx_hdr_proto + * Packet protocol type. Must be set to 1 (Ethernet). + */ +MLXSW_ITEM32(tx, hdr, proto, 0x00, 21, 3); + +/* tx_hdr_rx_is_router + * Packet is sent from the router. Valid for data packets only. + */ +MLXSW_ITEM32(tx, hdr, rx_is_router, 0x00, 19, 1); + +/* tx_hdr_fid_valid + * Indicates if the 'fid' field is valid and should be used for + * forwarding lookup. Valid for data packets only. + */ +MLXSW_ITEM32(tx, hdr, fid_valid, 0x00, 16, 1); + +/* tx_hdr_swid + * Switch partition ID. Must be set to 0. + */ +MLXSW_ITEM32(tx, hdr, swid, 0x00, 12, 3); + +/* tx_hdr_control_tclass + * Indicates if the packet should use the control TClass and not one + * of the data TClasses. + */ +MLXSW_ITEM32(tx, hdr, control_tclass, 0x00, 6, 1); + +/* tx_hdr_etclass + * Egress TClass to be used on the egress device on the egress port. + */ +MLXSW_ITEM32(tx, hdr, etclass, 0x00, 0, 4); + +/* tx_hdr_port_mid + * Destination local port for unicast packets. + * Destination multicast ID for multicast packets. + * + * Control packets are directed to a specific egress port, while data + * packets are transmitted through the CPU port (0) into the switch partition, + * where forwarding rules are applied. + */ +MLXSW_ITEM32(tx, hdr, port_mid, 0x04, 16, 16); + +/* tx_hdr_fid + * Forwarding ID used for L2 forwarding lookup. Valid only if 'fid_valid' is + * set, otherwise calculated based on the packet's VID using VID to FID mapping. + * Valid for data packets only. + */ +MLXSW_ITEM32(tx, hdr, fid, 0x08, 0, 16); + +/* tx_hdr_type + * 0 - Data packets + * 6 - Control packets + */ +MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4); + +static void mlxsw_sp_txhdr_construct(struct sk_buff *skb, + const struct mlxsw_tx_info *tx_info) +{ + char *txhdr = skb_push(skb, MLXSW_TXHDR_LEN); + + memset(txhdr, 0, MLXSW_TXHDR_LEN); + + mlxsw_tx_hdr_version_set(txhdr, MLXSW_TXHDR_VERSION_1); + mlxsw_tx_hdr_ctl_set(txhdr, MLXSW_TXHDR_ETH_CTL); + mlxsw_tx_hdr_proto_set(txhdr, MLXSW_TXHDR_PROTO_ETH); + mlxsw_tx_hdr_swid_set(txhdr, 0); + mlxsw_tx_hdr_control_tclass_set(txhdr, 1); + mlxsw_tx_hdr_port_mid_set(txhdr, tx_info->local_port); + mlxsw_tx_hdr_type_set(txhdr, MLXSW_TXHDR_TYPE_CONTROL); +} + +static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp) +{ + char spad_pl[MLXSW_REG_SPAD_LEN]; + int err; + + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(spad), spad_pl); + if (err) + return err; + mlxsw_reg_spad_base_mac_memcpy_from(spad_pl, mlxsw_sp->base_mac); + return 0; +} + +static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool is_up) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char paos_pl[MLXSW_REG_PAOS_LEN]; + + mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port, + is_up ? MLXSW_PORT_ADMIN_STATUS_UP : + MLXSW_PORT_ADMIN_STATUS_DOWN); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(paos), paos_pl); +} + +static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port, + bool *p_is_up) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char paos_pl[MLXSW_REG_PAOS_LEN]; + u8 oper_status; + int err; + + mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(paos), paos_pl); + if (err) + return err; + oper_status = mlxsw_reg_paos_oper_status_get(paos_pl); + *p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP ? true : false; + return 0; +} + +static int mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + int err; + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, + MLXSW_SP_VFID_BASE + vfid, 0); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); + + if (err) + return err; + + set_bit(vfid, mlxsw_sp->active_vfids); + return 0; +} + +static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + + clear_bit(vfid, mlxsw_sp->active_vfids); + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, + MLXSW_SP_VFID_BASE + vfid, 0); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); +} + +static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port, + unsigned char *addr) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ppad_pl[MLXSW_REG_PPAD_LEN]; + + mlxsw_reg_ppad_pack(ppad_pl, true, mlxsw_sp_port->local_port); + mlxsw_reg_ppad_mac_memcpy_to(ppad_pl, addr); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppad), ppad_pl); +} + +static int mlxsw_sp_port_dev_addr_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + unsigned char *addr = mlxsw_sp_port->dev->dev_addr; + + ether_addr_copy(addr, mlxsw_sp->base_mac); + addr[ETH_ALEN - 1] += mlxsw_sp_port->local_port; + return mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr); +} + +static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid, enum mlxsw_reg_spms_state state) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *spms_pl; + int err; + + spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL); + if (!spms_pl) + return -ENOMEM; + mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port); + mlxsw_reg_spms_vid_pack(spms_pl, vid, state); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl); + kfree(spms_pl); + return err; +} + +static int mlxsw_sp_port_mtu_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char pmtu_pl[MLXSW_REG_PMTU_LEN]; + int max_mtu; + int err; + + mtu += MLXSW_TXHDR_LEN + ETH_HLEN; + mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl); + if (err) + return err; + max_mtu = mlxsw_reg_pmtu_max_mtu_get(pmtu_pl); + + if (mtu > max_mtu) + return -EINVAL; + + mlxsw_reg_pmtu_pack(pmtu_pl, mlxsw_sp_port->local_port, mtu); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pmtu), pmtu_pl); +} + +static int mlxsw_sp_port_swid_set(struct mlxsw_sp_port *mlxsw_sp_port, u8 swid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char pspa_pl[MLXSW_REG_PSPA_LEN]; + + mlxsw_reg_pspa_pack(pspa_pl, swid, mlxsw_sp_port->local_port); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pspa), pspa_pl); +} + +static int mlxsw_sp_port_vp_mode_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool enable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char svpe_pl[MLXSW_REG_SVPE_LEN]; + + mlxsw_reg_svpe_pack(svpe_pl, mlxsw_sp_port->local_port, enable); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svpe), svpe_pl); +} + +int mlxsw_sp_port_vid_to_fid_set(struct mlxsw_sp_port *mlxsw_sp_port, + enum mlxsw_reg_svfa_mt mt, bool valid, u16 fid, + u16 vid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char svfa_pl[MLXSW_REG_SVFA_LEN]; + + mlxsw_reg_svfa_pack(svfa_pl, mlxsw_sp_port->local_port, mt, valid, + fid, vid); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl); +} + +static int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid, bool learn_enable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *spvmlr_pl; + int err; + + spvmlr_pl = kmalloc(MLXSW_REG_SPVMLR_LEN, GFP_KERNEL); + if (!spvmlr_pl) + return -ENOMEM; + mlxsw_reg_spvmlr_pack(spvmlr_pl, mlxsw_sp_port->local_port, vid, vid, + learn_enable); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvmlr), spvmlr_pl); + kfree(spvmlr_pl); + return err; +} + +static int +mlxsw_sp_port_system_port_mapping_set(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char sspr_pl[MLXSW_REG_SSPR_LEN]; + + mlxsw_reg_sspr_pack(sspr_pl, mlxsw_sp_port->local_port); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sspr), sspr_pl); +} + +static int mlxsw_sp_port_module_check(struct mlxsw_sp_port *mlxsw_sp_port, + bool *p_usable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char pmlp_pl[MLXSW_REG_PMLP_LEN]; + int err; + + mlxsw_reg_pmlp_pack(pmlp_pl, mlxsw_sp_port->local_port); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pmlp), pmlp_pl); + if (err) + return err; + *p_usable = mlxsw_reg_pmlp_width_get(pmlp_pl) ? true : false; + return 0; +} + +static int mlxsw_sp_port_open(struct net_device *dev) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err; + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + if (err) + return err; + netif_start_queue(dev); + return 0; +} + +static int mlxsw_sp_port_stop(struct net_device *dev) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + + netif_stop_queue(dev); + return mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); +} + +static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct mlxsw_sp_port_pcpu_stats *pcpu_stats; + const struct mlxsw_tx_info tx_info = { + .local_port = mlxsw_sp_port->local_port, + .is_emad = false, + }; + u64 len; + int err; + + if (mlxsw_core_skb_transmit_busy(mlxsw_sp, &tx_info)) + return NETDEV_TX_BUSY; + + if (unlikely(skb_headroom(skb) < MLXSW_TXHDR_LEN)) { + struct sk_buff *skb_orig = skb; + + skb = skb_realloc_headroom(skb, MLXSW_TXHDR_LEN); + if (!skb) { + this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); + dev_kfree_skb_any(skb_orig); + return NETDEV_TX_OK; + } + } + + if (eth_skb_pad(skb)) { + this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); + return NETDEV_TX_OK; + } + + mlxsw_sp_txhdr_construct(skb, &tx_info); + len = skb->len; + /* Due to a race we might fail here because of a full queue. In that + * unlikely case we simply drop the packet. + */ + err = mlxsw_core_skb_transmit(mlxsw_sp, skb, &tx_info); + + if (!err) { + pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats); + u64_stats_update_begin(&pcpu_stats->syncp); + pcpu_stats->tx_packets++; + pcpu_stats->tx_bytes += len; + u64_stats_update_end(&pcpu_stats->syncp); + } else { + this_cpu_inc(mlxsw_sp_port->pcpu_stats->tx_dropped); + dev_kfree_skb_any(skb); + } + return NETDEV_TX_OK; +} + +static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct sockaddr *addr = p; + int err; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + err = mlxsw_sp_port_dev_addr_set(mlxsw_sp_port, addr->sa_data); + if (err) + return err; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + return 0; +} + +static int mlxsw_sp_port_change_mtu(struct net_device *dev, int mtu) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err; + + err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, mtu); + if (err) + return err; + dev->mtu = mtu; + return 0; +} + +static struct rtnl_link_stats64 * +mlxsw_sp_port_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp_port_pcpu_stats *p; + u64 rx_packets, rx_bytes, tx_packets, tx_bytes; + u32 tx_dropped = 0; + unsigned int start; + int i; + + for_each_possible_cpu(i) { + p = per_cpu_ptr(mlxsw_sp_port->pcpu_stats, i); + do { + start = u64_stats_fetch_begin_irq(&p->syncp); + rx_packets = p->rx_packets; + rx_bytes = p->rx_bytes; + tx_packets = p->tx_packets; + tx_bytes = p->tx_bytes; + } while (u64_stats_fetch_retry_irq(&p->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; + stats->tx_packets += tx_packets; + stats->tx_bytes += tx_bytes; + /* tx_dropped is u32, updated without syncp protection. */ + tx_dropped += p->tx_dropped; + } + stats->tx_dropped = tx_dropped; + return stats; +} + +int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, + u16 vid_end, bool is_member, bool untagged) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *spvm_pl; + int err; + + spvm_pl = kmalloc(MLXSW_REG_SPVM_LEN, GFP_KERNEL); + if (!spvm_pl) + return -ENOMEM; + + mlxsw_reg_spvm_pack(spvm_pl, mlxsw_sp_port->local_port, vid_begin, + vid_end, is_member, untagged); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvm), spvm_pl); + kfree(spvm_pl); + return err; +} + +static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) +{ + enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + u16 vid, last_visited_vid; + int err; + + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, true, vid, + vid); + if (err) { + last_visited_vid = vid; + goto err_port_vid_to_fid_set; + } + } + + err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true); + if (err) { + last_visited_vid = VLAN_N_VID; + goto err_port_vid_to_fid_set; + } + + return 0; + +err_port_vid_to_fid_set: + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, last_visited_vid) + mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, vid, + vid); + return err; +} + +static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) +{ + enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + u16 vid; + int err; + + err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false); + if (err) + return err; + + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, + vid, vid); + if (err) + return err; + } + + return 0; +} + +int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, + u16 vid) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *sftr_pl; + int err; + + /* VLAN 0 is added to HW filter when device goes up, but it is + * reserved in our case, so simply return. + */ + if (!vid) + return 0; + + if (test_bit(vid, mlxsw_sp_port->active_vfids)) { + netdev_warn(dev, "VID=%d already configured\n", vid); + return 0; + } + + if (!test_bit(vid, mlxsw_sp->active_vfids)) { + err = mlxsw_sp_vfid_create(mlxsw_sp, vid); + if (err) { + netdev_err(dev, "Failed to create vFID=%d\n", + MLXSW_SP_VFID_BASE + vid); + return err; + } + + sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); + if (!sftr_pl) { + err = -ENOMEM; + goto err_flood_table_alloc; + } + mlxsw_reg_sftr_pack(sftr_pl, 0, vid, + MLXSW_REG_SFGC_TABLE_TYPE_FID, 0, + MLXSW_PORT_CPU_PORT, true); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + kfree(sftr_pl); + if (err) { + netdev_err(dev, "Failed to configure flood table\n"); + goto err_flood_table_config; + } + } + + /* In case we fail in the following steps, we intentionally do not + * destroy the associated vFID. + */ + + /* When adding the first VLAN interface on a bridged port we need to + * transition all the active 802.1Q bridge VLANs to use explicit + * {Port, VID} to FID mappings and set the port's mode to Virtual mode. + */ + if (!mlxsw_sp_port->nr_vfids) { + err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); + if (err) { + netdev_err(dev, "Failed to set to Virtual mode\n"); + return err; + } + } + + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + true, MLXSW_SP_VFID_BASE + vid, vid); + if (err) { + netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n", + vid, MLXSW_SP_VFID_BASE + vid); + goto err_port_vid_to_fid_set; + } + + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); + if (err) { + netdev_err(dev, "Failed to disable learning for VID=%d\n", vid); + goto err_port_vid_learning_set; + } + + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, false); + if (err) { + netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", + vid); + goto err_port_add_vid; + } + + err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, + MLXSW_REG_SPMS_STATE_FORWARDING); + if (err) { + netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); + goto err_port_stp_state_set; + } + + mlxsw_sp_port->nr_vfids++; + set_bit(vid, mlxsw_sp_port->active_vfids); + + return 0; + +err_flood_table_config: +err_flood_table_alloc: + mlxsw_sp_vfid_destroy(mlxsw_sp, vid); + return err; + +err_port_stp_state_set: + mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); +err_port_add_vid: + mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); +err_port_vid_learning_set: + mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false, + MLXSW_SP_VFID_BASE + vid, vid); +err_port_vid_to_fid_set: + mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); + return err; +} + +int mlxsw_sp_port_kill_vid(struct net_device *dev, + __be16 __always_unused proto, u16 vid) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err; + + /* VLAN 0 is removed from HW filter when device goes down, but + * it is reserved in our case, so simply return. + */ + if (!vid) + return 0; + + if (!test_bit(vid, mlxsw_sp_port->active_vfids)) { + netdev_warn(dev, "VID=%d does not exist\n", vid); + return 0; + } + + err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, + MLXSW_REG_SPMS_STATE_DISCARDING); + if (err) { + netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); + return err; + } + + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); + if (err) { + netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", + vid); + return err; + } + + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); + if (err) { + netdev_err(dev, "Failed to enable learning for VID=%d\n", vid); + return err; + } + + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + false, MLXSW_SP_VFID_BASE + vid, + vid); + if (err) { + netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n", + vid, MLXSW_SP_VFID_BASE + vid); + return err; + } + + /* When removing the last VLAN interface on a bridged port we need to + * transition all active 802.1Q bridge VLANs to use VID to FID + * mappings and set port's mode to VLAN mode. + */ + if (mlxsw_sp_port->nr_vfids == 1) { + err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); + if (err) { + netdev_err(dev, "Failed to set to VLAN mode\n"); + return err; + } + } + + mlxsw_sp_port->nr_vfids--; + clear_bit(vid, mlxsw_sp_port->active_vfids); + + return 0; +} + +static const struct net_device_ops mlxsw_sp_port_netdev_ops = { + .ndo_open = mlxsw_sp_port_open, + .ndo_stop = mlxsw_sp_port_stop, + .ndo_start_xmit = mlxsw_sp_port_xmit, + .ndo_set_mac_address = mlxsw_sp_port_set_mac_address, + .ndo_change_mtu = mlxsw_sp_port_change_mtu, + .ndo_get_stats64 = mlxsw_sp_port_get_stats64, + .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, + .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, + .ndo_fdb_add = switchdev_port_fdb_add, + .ndo_fdb_del = switchdev_port_fdb_del, + .ndo_fdb_dump = switchdev_port_fdb_dump, + .ndo_bridge_setlink = switchdev_port_bridge_setlink, + .ndo_bridge_getlink = switchdev_port_bridge_getlink, + .ndo_bridge_dellink = switchdev_port_bridge_dellink, +}; + +static void mlxsw_sp_port_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + + strlcpy(drvinfo->driver, mlxsw_sp_driver_name, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, mlxsw_sp_driver_version, + sizeof(drvinfo->version)); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%d", + mlxsw_sp->bus_info->fw_rev.major, + mlxsw_sp->bus_info->fw_rev.minor, + mlxsw_sp->bus_info->fw_rev.subminor); + strlcpy(drvinfo->bus_info, mlxsw_sp->bus_info->device_name, + sizeof(drvinfo->bus_info)); +} + +struct mlxsw_sp_port_hw_stats { + char str[ETH_GSTRING_LEN]; + u64 (*getter)(char *payload); +}; + +static const struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = { + { + .str = "a_frames_transmitted_ok", + .getter = mlxsw_reg_ppcnt_a_frames_transmitted_ok_get, + }, + { + .str = "a_frames_received_ok", + .getter = mlxsw_reg_ppcnt_a_frames_received_ok_get, + }, + { + .str = "a_frame_check_sequence_errors", + .getter = mlxsw_reg_ppcnt_a_frame_check_sequence_errors_get, + }, + { + .str = "a_alignment_errors", + .getter = mlxsw_reg_ppcnt_a_alignment_errors_get, + }, + { + .str = "a_octets_transmitted_ok", + .getter = mlxsw_reg_ppcnt_a_octets_transmitted_ok_get, + }, + { + .str = "a_octets_received_ok", + .getter = mlxsw_reg_ppcnt_a_octets_received_ok_get, + }, + { + .str = "a_multicast_frames_xmitted_ok", + .getter = mlxsw_reg_ppcnt_a_multicast_frames_xmitted_ok_get, + }, + { + .str = "a_broadcast_frames_xmitted_ok", + .getter = mlxsw_reg_ppcnt_a_broadcast_frames_xmitted_ok_get, + }, + { + .str = "a_multicast_frames_received_ok", + .getter = mlxsw_reg_ppcnt_a_multicast_frames_received_ok_get, + }, + { + .str = "a_broadcast_frames_received_ok", + .getter = mlxsw_reg_ppcnt_a_broadcast_frames_received_ok_get, + }, + { + .str = "a_in_range_length_errors", + .getter = mlxsw_reg_ppcnt_a_in_range_length_errors_get, + }, + { + .str = "a_out_of_range_length_field", + .getter = mlxsw_reg_ppcnt_a_out_of_range_length_field_get, + }, + { + .str = "a_frame_too_long_errors", + .getter = mlxsw_reg_ppcnt_a_frame_too_long_errors_get, + }, + { + .str = "a_symbol_error_during_carrier", + .getter = mlxsw_reg_ppcnt_a_symbol_error_during_carrier_get, + }, + { + .str = "a_mac_control_frames_transmitted", + .getter = mlxsw_reg_ppcnt_a_mac_control_frames_transmitted_get, + }, + { + .str = "a_mac_control_frames_received", + .getter = mlxsw_reg_ppcnt_a_mac_control_frames_received_get, + }, + { + .str = "a_unsupported_opcodes_received", + .getter = mlxsw_reg_ppcnt_a_unsupported_opcodes_received_get, + }, + { + .str = "a_pause_mac_ctrl_frames_received", + .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_received_get, + }, + { + .str = "a_pause_mac_ctrl_frames_xmitted", + .getter = mlxsw_reg_ppcnt_a_pause_mac_ctrl_frames_transmitted_get, + }, +}; + +#define MLXSW_SP_PORT_HW_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_stats) + +static void mlxsw_sp_port_get_strings(struct net_device *dev, + u32 stringset, u8 *data) +{ + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) { + memcpy(p, mlxsw_sp_port_hw_stats[i].str, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + } +} + +static void mlxsw_sp_port_get_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; + int i; + int err; + + mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl); + for (i = 0; i < MLXSW_SP_PORT_HW_STATS_LEN; i++) + data[i] = !err ? mlxsw_sp_port_hw_stats[i].getter(ppcnt_pl) : 0; +} + +static int mlxsw_sp_port_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return MLXSW_SP_PORT_HW_STATS_LEN; + default: + return -EOPNOTSUPP; + } +} + +struct mlxsw_sp_port_link_mode { + u32 mask; + u32 supported; + u32 advertised; + u32 speed; +}; + +static const struct mlxsw_sp_port_link_mode mlxsw_sp_port_link_mode[] = { + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_T, + .supported = SUPPORTED_100baseT_Full, + .advertised = ADVERTISED_100baseT_Full, + .speed = 100, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_100BASE_TX, + .speed = 100, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_SGMII | + MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX, + .supported = SUPPORTED_1000baseKX_Full, + .advertised = ADVERTISED_1000baseKX_Full, + .speed = 1000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_T, + .supported = SUPPORTED_10000baseT_Full, + .advertised = ADVERTISED_10000baseT_Full, + .speed = 10000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CX4 | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4, + .supported = SUPPORTED_10000baseKX4_Full, + .advertised = ADVERTISED_10000baseKX4_Full, + .speed = 10000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_ER_LR, + .supported = SUPPORTED_10000baseKR_Full, + .advertised = ADVERTISED_10000baseKR_Full, + .speed = 10000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_20GBASE_KR2, + .supported = SUPPORTED_20000baseKR2_Full, + .advertised = ADVERTISED_20000baseKR2_Full, + .speed = 20000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4, + .supported = SUPPORTED_40000baseCR4_Full, + .advertised = ADVERTISED_40000baseCR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4, + .supported = SUPPORTED_40000baseKR4_Full, + .advertised = ADVERTISED_40000baseKR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4, + .supported = SUPPORTED_40000baseSR4_Full, + .advertised = ADVERTISED_40000baseSR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_40GBASE_LR4_ER4, + .supported = SUPPORTED_40000baseLR4_Full, + .advertised = ADVERTISED_40000baseLR4_Full, + .speed = 40000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_25GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_25GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_25GBASE_SR, + .speed = 25000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_50GBASE_CR2 | + MLXSW_REG_PTYS_ETH_SPEED_50GBASE_KR2, + .speed = 50000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_56GBASE_R4, + .supported = SUPPORTED_56000baseKR4_Full, + .advertised = ADVERTISED_56000baseKR4_Full, + .speed = 56000, + }, + { + .mask = MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_LR4_ER4, + .speed = 100000, + }, +}; + +#define MLXSW_SP_PORT_LINK_MODE_LEN ARRAY_SIZE(mlxsw_sp_port_link_mode) + +static u32 mlxsw_sp_from_ptys_supported_port(u32 ptys_eth_proto) +{ + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_SGMII)) + return SUPPORTED_FIBRE; + + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_1000BASE_KX)) + return SUPPORTED_Backplane; + return 0; +} + +static u32 mlxsw_sp_from_ptys_supported_link(u32 ptys_eth_proto) +{ + u32 modes = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask) + modes |= mlxsw_sp_port_link_mode[i].supported; + } + return modes; +} + +static u32 mlxsw_sp_from_ptys_advert_link(u32 ptys_eth_proto) +{ + u32 modes = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask) + modes |= mlxsw_sp_port_link_mode[i].advertised; + } + return modes; +} + +static void mlxsw_sp_from_ptys_speed_duplex(bool carrier_ok, u32 ptys_eth_proto, + struct ethtool_cmd *cmd) +{ + u32 speed = SPEED_UNKNOWN; + u8 duplex = DUPLEX_UNKNOWN; + int i; + + if (!carrier_ok) + goto out; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (ptys_eth_proto & mlxsw_sp_port_link_mode[i].mask) { + speed = mlxsw_sp_port_link_mode[i].speed; + duplex = DUPLEX_FULL; + break; + } + } +out: + ethtool_cmd_speed_set(cmd, speed); + cmd->duplex = duplex; +} + +static u8 mlxsw_sp_port_connector_port(u32 ptys_eth_proto) +{ + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_SR | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_SR4 | + MLXSW_REG_PTYS_ETH_SPEED_SGMII)) + return PORT_FIBRE; + + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_CR | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_CR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_CR4)) + return PORT_DA; + + if (ptys_eth_proto & (MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KR | + MLXSW_REG_PTYS_ETH_SPEED_10GBASE_KX4 | + MLXSW_REG_PTYS_ETH_SPEED_40GBASE_KR4 | + MLXSW_REG_PTYS_ETH_SPEED_100GBASE_KR4)) + return PORT_NONE; + + return PORT_OTHER; +} + +static int mlxsw_sp_port_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 eth_proto_cap; + u32 eth_proto_admin; + u32 eth_proto_oper; + int err; + + mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); + if (err) { + netdev_err(dev, "Failed to get proto"); + return err; + } + mlxsw_reg_ptys_unpack(ptys_pl, ð_proto_cap, + ð_proto_admin, ð_proto_oper); + + cmd->supported = mlxsw_sp_from_ptys_supported_port(eth_proto_cap) | + mlxsw_sp_from_ptys_supported_link(eth_proto_cap) | + SUPPORTED_Pause | SUPPORTED_Asym_Pause; + cmd->advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_admin); + mlxsw_sp_from_ptys_speed_duplex(netif_carrier_ok(dev), + eth_proto_oper, cmd); + + eth_proto_oper = eth_proto_oper ? eth_proto_oper : eth_proto_cap; + cmd->port = mlxsw_sp_port_connector_port(eth_proto_oper); + cmd->lp_advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_oper); + + cmd->transceiver = XCVR_INTERNAL; + return 0; +} + +static u32 mlxsw_sp_to_ptys_advert_link(u32 advertising) +{ + u32 ptys_proto = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (advertising & mlxsw_sp_port_link_mode[i].advertised) + ptys_proto |= mlxsw_sp_port_link_mode[i].mask; + } + return ptys_proto; +} + +static u32 mlxsw_sp_to_ptys_speed(u32 speed) +{ + u32 ptys_proto = 0; + int i; + + for (i = 0; i < MLXSW_SP_PORT_LINK_MODE_LEN; i++) { + if (speed == mlxsw_sp_port_link_mode[i].speed) + ptys_proto |= mlxsw_sp_port_link_mode[i].mask; + } + return ptys_proto; +} + +static int mlxsw_sp_port_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 speed; + u32 eth_proto_new; + u32 eth_proto_cap; + u32 eth_proto_admin; + bool is_up; + int err; + + speed = ethtool_cmd_speed(cmd); + + eth_proto_new = cmd->autoneg == AUTONEG_ENABLE ? + mlxsw_sp_to_ptys_advert_link(cmd->advertising) : + mlxsw_sp_to_ptys_speed(speed); + + mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, 0); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); + if (err) { + netdev_err(dev, "Failed to get proto"); + return err; + } + mlxsw_reg_ptys_unpack(ptys_pl, ð_proto_cap, ð_proto_admin, NULL); + + eth_proto_new = eth_proto_new & eth_proto_cap; + if (!eth_proto_new) { + netdev_err(dev, "Not supported proto admin requested"); + return -EINVAL; + } + if (eth_proto_new == eth_proto_admin) + return 0; + + mlxsw_reg_ptys_pack(ptys_pl, mlxsw_sp_port->local_port, eth_proto_new); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); + if (err) { + netdev_err(dev, "Failed to set proto admin"); + return err; + } + + err = mlxsw_sp_port_oper_status_get(mlxsw_sp_port, &is_up); + if (err) { + netdev_err(dev, "Failed to get oper status"); + return err; + } + if (!is_up) + return 0; + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + if (err) { + netdev_err(dev, "Failed to set admin status"); + return err; + } + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, true); + if (err) { + netdev_err(dev, "Failed to set admin status"); + return err; + } + + return 0; +} + +static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { + .get_drvinfo = mlxsw_sp_port_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = mlxsw_sp_port_get_strings, + .get_ethtool_stats = mlxsw_sp_port_get_stats, + .get_sset_count = mlxsw_sp_port_get_sset_count, + .get_settings = mlxsw_sp_port_get_settings, + .set_settings = mlxsw_sp_port_set_settings, +}; + +static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + struct net_device *dev; + bool usable; + int err; + + dev = alloc_etherdev(sizeof(struct mlxsw_sp_port)); + if (!dev) + return -ENOMEM; + mlxsw_sp_port = netdev_priv(dev); + mlxsw_sp_port->dev = dev; + mlxsw_sp_port->mlxsw_sp = mlxsw_sp; + mlxsw_sp_port->local_port = local_port; + mlxsw_sp_port->learning = 1; + mlxsw_sp_port->learning_sync = 1; + mlxsw_sp_port->pvid = 1; + + mlxsw_sp_port->pcpu_stats = + netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats); + if (!mlxsw_sp_port->pcpu_stats) { + err = -ENOMEM; + goto err_alloc_stats; + } + + dev->netdev_ops = &mlxsw_sp_port_netdev_ops; + dev->ethtool_ops = &mlxsw_sp_port_ethtool_ops; + + err = mlxsw_sp_port_dev_addr_init(mlxsw_sp_port); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Unable to init port mac address\n", + mlxsw_sp_port->local_port); + goto err_dev_addr_init; + } + + netif_carrier_off(dev); + + dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_LLTX | NETIF_F_SG | + NETIF_F_HW_VLAN_CTAG_FILTER; + + /* Each packet needs to have a Tx header (metadata) on top all other + * headers. + */ + dev->hard_header_len += MLXSW_TXHDR_LEN; + + err = mlxsw_sp_port_module_check(mlxsw_sp_port, &usable); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to check module\n", + mlxsw_sp_port->local_port); + goto err_port_module_check; + } + + if (!usable) { + dev_dbg(mlxsw_sp->bus_info->dev, "Port %d: Not usable, skipping initialization\n", + mlxsw_sp_port->local_port); + goto port_not_usable; + } + + err = mlxsw_sp_port_system_port_mapping_set(mlxsw_sp_port); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set system port mapping\n", + mlxsw_sp_port->local_port); + goto err_port_system_port_mapping_set; + } + + err = mlxsw_sp_port_swid_set(mlxsw_sp_port, 0); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set SWID\n", + mlxsw_sp_port->local_port); + goto err_port_swid_set; + } + + err = mlxsw_sp_port_mtu_set(mlxsw_sp_port, ETH_DATA_LEN); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to set MTU\n", + mlxsw_sp_port->local_port); + goto err_port_mtu_set; + } + + err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false); + if (err) + goto err_port_admin_status_set; + + err = mlxsw_sp_port_buffers_init(mlxsw_sp_port); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to initialize buffers\n", + mlxsw_sp_port->local_port); + goto err_port_buffers_init; + } + + mlxsw_sp_port_switchdev_init(mlxsw_sp_port); + err = register_netdev(dev); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Port %d: Failed to register netdev\n", + mlxsw_sp_port->local_port); + goto err_register_netdev; + } + + err = mlxsw_sp_port_vlan_init(mlxsw_sp_port); + if (err) + goto err_port_vlan_init; + + mlxsw_sp->ports[local_port] = mlxsw_sp_port; + return 0; + +err_port_vlan_init: + unregister_netdev(dev); +err_register_netdev: +err_port_buffers_init: +err_port_admin_status_set: +err_port_mtu_set: +err_port_swid_set: +err_port_system_port_mapping_set: +port_not_usable: +err_port_module_check: +err_dev_addr_init: + free_percpu(mlxsw_sp_port->pcpu_stats); +err_alloc_stats: + free_netdev(dev); + return err; +} + +static void mlxsw_sp_vfids_fini(struct mlxsw_sp *mlxsw_sp) +{ + u16 vfid; + + for_each_set_bit(vfid, mlxsw_sp->active_vfids, VLAN_N_VID) + mlxsw_sp_vfid_destroy(mlxsw_sp, vfid); +} + +static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) +{ + struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; + + if (!mlxsw_sp_port) + return; + mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1); + unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ + mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); + free_percpu(mlxsw_sp_port->pcpu_stats); + free_netdev(mlxsw_sp_port->dev); +} + +static void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp) +{ + int i; + + for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) + mlxsw_sp_port_remove(mlxsw_sp, i); + kfree(mlxsw_sp->ports); +} + +static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) +{ + size_t alloc_size; + int i; + int err; + + alloc_size = sizeof(struct mlxsw_sp_port *) * MLXSW_PORT_MAX_PORTS; + mlxsw_sp->ports = kzalloc(alloc_size, GFP_KERNEL); + if (!mlxsw_sp->ports) + return -ENOMEM; + + for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) { + err = mlxsw_sp_port_create(mlxsw_sp, i); + if (err) + goto err_port_create; + } + return 0; + +err_port_create: + for (i--; i >= 1; i--) + mlxsw_sp_port_remove(mlxsw_sp, i); + kfree(mlxsw_sp->ports); + return err; +} + +static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg, + char *pude_pl, void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + struct mlxsw_sp_port *mlxsw_sp_port; + enum mlxsw_reg_pude_oper_status status; + u8 local_port; + + local_port = mlxsw_reg_pude_local_port_get(pude_pl); + mlxsw_sp_port = mlxsw_sp->ports[local_port]; + if (!mlxsw_sp_port) { + dev_warn(mlxsw_sp->bus_info->dev, "Port %d: Link event received for non-existent port\n", + local_port); + return; + } + + status = mlxsw_reg_pude_oper_status_get(pude_pl); + if (status == MLXSW_PORT_OPER_STATUS_UP) { + netdev_info(mlxsw_sp_port->dev, "link up\n"); + netif_carrier_on(mlxsw_sp_port->dev); + } else { + netdev_info(mlxsw_sp_port->dev, "link down\n"); + netif_carrier_off(mlxsw_sp_port->dev); + } +} + +static struct mlxsw_event_listener mlxsw_sp_pude_event = { + .func = mlxsw_sp_pude_event_func, + .trap_id = MLXSW_TRAP_ID_PUDE, +}; + +static int mlxsw_sp_event_register(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_event_trap_id trap_id) +{ + struct mlxsw_event_listener *el; + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + int err; + + switch (trap_id) { + case MLXSW_TRAP_ID_PUDE: + el = &mlxsw_sp_pude_event; + break; + } + err = mlxsw_core_event_listener_register(mlxsw_sp->core, el, mlxsw_sp); + if (err) + return err; + + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); + if (err) + goto err_event_trap_set; + + return 0; + +err_event_trap_set: + mlxsw_core_event_listener_unregister(mlxsw_sp->core, el, mlxsw_sp); + return err; +} + +static void mlxsw_sp_event_unregister(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_event_trap_id trap_id) +{ + struct mlxsw_event_listener *el; + + switch (trap_id) { + case MLXSW_TRAP_ID_PUDE: + el = &mlxsw_sp_pude_event; + break; + } + mlxsw_core_event_listener_unregister(mlxsw_sp->core, el, mlxsw_sp); +} + +static void mlxsw_sp_rx_listener_func(struct sk_buff *skb, u8 local_port, + void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; + struct mlxsw_sp_port_pcpu_stats *pcpu_stats; + + if (unlikely(!mlxsw_sp_port)) { + dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: skb received for non-existent port\n", + local_port); + return; + } + + skb->dev = mlxsw_sp_port->dev; + + pcpu_stats = this_cpu_ptr(mlxsw_sp_port->pcpu_stats); + u64_stats_update_begin(&pcpu_stats->syncp); + pcpu_stats->rx_packets++; + pcpu_stats->rx_bytes += skb->len; + u64_stats_update_end(&pcpu_stats->syncp); + + skb->protocol = eth_type_trans(skb, skb->dev); + netif_receive_skb(skb); +} + +static const struct mlxsw_rx_listener mlxsw_sp_rx_listener[] = { + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_FDB_MC, + }, + /* Traps for specific L2 packet types, not trapped as FDB MC */ + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_STP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_LACP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_EAPOL, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_LLDP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_MMRP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_MVRP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_RPVST, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_DHCP, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_QUERY, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V1_REPORT, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V2_REPORT, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V2_LEAVE, + }, + { + .func = mlxsw_sp_rx_listener_func, + .local_port = MLXSW_PORT_DONT_CARE, + .trap_id = MLXSW_TRAP_ID_IGMP_V3_REPORT, + }, +}; + +static int mlxsw_sp_traps_init(struct mlxsw_sp *mlxsw_sp) +{ + char htgt_pl[MLXSW_REG_HTGT_LEN]; + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + int i; + int err; + + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_RX); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl); + if (err) + return err; + + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(htgt), htgt_pl); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { + err = mlxsw_core_rx_listener_register(mlxsw_sp->core, + &mlxsw_sp_rx_listener[i], + mlxsw_sp); + if (err) + goto err_rx_listener_register; + + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, + mlxsw_sp_rx_listener[i].trap_id); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); + if (err) + goto err_rx_trap_set; + } + return 0; + +err_rx_trap_set: + mlxsw_core_rx_listener_unregister(mlxsw_sp->core, + &mlxsw_sp_rx_listener[i], + mlxsw_sp); +err_rx_listener_register: + for (i--; i >= 0; i--) { + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, + mlxsw_sp_rx_listener[i].trap_id); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); + + mlxsw_core_rx_listener_unregister(mlxsw_sp->core, + &mlxsw_sp_rx_listener[i], + mlxsw_sp); + } + return err; +} + +static void mlxsw_sp_traps_fini(struct mlxsw_sp *mlxsw_sp) +{ + char hpkt_pl[MLXSW_REG_HPKT_LEN]; + int i; + + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_rx_listener); i++) { + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, + mlxsw_sp_rx_listener[i].trap_id); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(hpkt), hpkt_pl); + + mlxsw_core_rx_listener_unregister(mlxsw_sp->core, + &mlxsw_sp_rx_listener[i], + mlxsw_sp); + } +} + +static int __mlxsw_sp_flood_init(struct mlxsw_core *mlxsw_core, + enum mlxsw_reg_sfgc_type type, + enum mlxsw_reg_sfgc_bridge_type bridge_type) +{ + enum mlxsw_flood_table_type table_type; + enum mlxsw_sp_flood_table flood_table; + char sfgc_pl[MLXSW_REG_SFGC_LEN]; + + if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID) { + table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID; + flood_table = 0; + } else { + table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST; + if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST) + flood_table = MLXSW_SP_FLOOD_TABLE_UC; + else + flood_table = MLXSW_SP_FLOOD_TABLE_BM; + } + + mlxsw_reg_sfgc_pack(sfgc_pl, type, bridge_type, table_type, + flood_table); + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(sfgc), sfgc_pl); +} + +static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp) +{ + int type, err; + + /* For non-offloaded netdevs, flood all traffic types to CPU + * port. + */ + for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) { + if (type == MLXSW_REG_SFGC_TYPE_RESERVED) + continue; + + err = __mlxsw_sp_flood_init(mlxsw_sp->core, type, + MLXSW_REG_SFGC_BRIDGE_TYPE_VFID); + if (err) + return err; + } + + /* For bridged ports, use one flooding table for unknown unicast + * traffic and a second table for unregistered multicast and + * broadcast. + */ + for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) { + if (type == MLXSW_REG_SFGC_TYPE_RESERVED) + continue; + + err = __mlxsw_sp_flood_init(mlxsw_sp->core, type, + MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID); + if (err) + return err; + } + + return 0; +} + +static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info) +{ + struct mlxsw_sp *mlxsw_sp = priv; + int err; + + mlxsw_sp->core = mlxsw_core; + mlxsw_sp->bus_info = mlxsw_bus_info; + + err = mlxsw_sp_base_mac_get(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to get base mac\n"); + return err; + } + + err = mlxsw_sp_ports_create(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); + goto err_ports_create; + } + + err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to register for PUDE events\n"); + goto err_event_register; + } + + err = mlxsw_sp_traps_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to set traps for RX\n"); + goto err_rx_listener_register; + } + + err = mlxsw_sp_flood_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize flood tables\n"); + goto err_flood_init; + } + + err = mlxsw_sp_buffers_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize buffers\n"); + goto err_buffers_init; + } + + err = mlxsw_sp_switchdev_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n"); + goto err_switchdev_init; + } + + return 0; + +err_switchdev_init: +err_buffers_init: +err_flood_init: + mlxsw_sp_traps_fini(mlxsw_sp); +err_rx_listener_register: + mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); +err_event_register: + mlxsw_sp_ports_remove(mlxsw_sp); +err_ports_create: + mlxsw_sp_vfids_fini(mlxsw_sp); + return err; +} + +static void mlxsw_sp_fini(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + mlxsw_sp_switchdev_fini(mlxsw_sp); + mlxsw_sp_traps_fini(mlxsw_sp); + mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); + mlxsw_sp_ports_remove(mlxsw_sp); + mlxsw_sp_vfids_fini(mlxsw_sp); +} + +static struct mlxsw_config_profile mlxsw_sp_config_profile = { + .used_max_vepa_channels = 1, + .max_vepa_channels = 0, + .used_max_lag = 1, + .max_lag = 64, + .used_max_port_per_lag = 1, + .max_port_per_lag = 16, + .used_max_mid = 1, + .max_mid = 7000, + .used_max_pgt = 1, + .max_pgt = 0, + .used_max_system_port = 1, + .max_system_port = 64, + .used_max_vlan_groups = 1, + .max_vlan_groups = 127, + .used_max_regions = 1, + .max_regions = 400, + .used_flood_tables = 1, + .used_flood_mode = 1, + .flood_mode = 3, + .max_fid_offset_flood_tables = 2, + .fid_offset_flood_table_size = VLAN_N_VID - 1, + .max_fid_flood_tables = 1, + .fid_flood_table_size = VLAN_N_VID, + .used_max_ib_mc = 1, + .max_ib_mc = 0, + .used_max_pkey = 1, + .max_pkey = 0, + .swid_config = { + { + .used_type = 1, + .type = MLXSW_PORT_SWID_TYPE_ETH, + } + }, +}; + +static struct mlxsw_driver mlxsw_sp_driver = { + .kind = MLXSW_DEVICE_KIND_SPECTRUM, + .owner = THIS_MODULE, + .priv_size = sizeof(struct mlxsw_sp), + .init = mlxsw_sp_init, + .fini = mlxsw_sp_fini, + .txhdr_construct = mlxsw_sp_txhdr_construct, + .txhdr_len = MLXSW_TXHDR_LEN, + .profile = &mlxsw_sp_config_profile, +}; + +static bool mlxsw_sp_port_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &mlxsw_sp_port_netdev_ops; +} + +static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct net_device *dev = mlxsw_sp_port->dev; + int err; + + /* When port is not bridged untagged packets are tagged with + * PVID=VID=1, thereby creating an implicit VLAN interface in + * the device. Remove it and let bridge code take care of its + * own VLANs. + */ + err = mlxsw_sp_port_kill_vid(dev, 0, 1); + if (err) + netdev_err(dev, "Failed to remove VID 1\n"); + + return err; +} + +static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct net_device *dev = mlxsw_sp_port->dev; + int err; + + /* Add implicit VLAN interface in the device, so that untagged + * packets will be classified to the default vFID. + */ + err = mlxsw_sp_port_add_vid(dev, 0, 1); + if (err) + netdev_err(dev, "Failed to add VID 1\n"); + + return err; +} + +static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp, + struct net_device *br_dev) +{ + return !mlxsw_sp->master_bridge.dev || + mlxsw_sp->master_bridge.dev == br_dev; +} + +static void mlxsw_sp_master_bridge_inc(struct mlxsw_sp *mlxsw_sp, + struct net_device *br_dev) +{ + mlxsw_sp->master_bridge.dev = br_dev; + mlxsw_sp->master_bridge.ref_count++; +} + +static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp, + struct net_device *br_dev) +{ + if (--mlxsw_sp->master_bridge.ref_count == 0) + mlxsw_sp->master_bridge.dev = NULL; +} + +static int mlxsw_sp_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info; + struct mlxsw_sp_port *mlxsw_sp_port; + struct net_device *upper_dev; + struct mlxsw_sp *mlxsw_sp; + int err; + + if (!mlxsw_sp_port_dev_check(dev)) + return NOTIFY_DONE; + + mlxsw_sp_port = netdev_priv(dev); + mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + info = ptr; + + switch (event) { + case NETDEV_PRECHANGEUPPER: + upper_dev = info->upper_dev; + /* HW limitation forbids to put ports to multiple bridges. */ + if (info->master && info->linking && + netif_is_bridge_master(upper_dev) && + !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev)) + return NOTIFY_BAD; + break; + case NETDEV_CHANGEUPPER: + upper_dev = info->upper_dev; + if (info->master && + netif_is_bridge_master(upper_dev)) { + if (info->linking) { + err = mlxsw_sp_port_bridge_join(mlxsw_sp_port); + if (err) + netdev_err(dev, "Failed to join bridge\n"); + mlxsw_sp_master_bridge_inc(mlxsw_sp, upper_dev); + mlxsw_sp_port->bridged = true; + } else { + err = mlxsw_sp_port_bridge_leave(mlxsw_sp_port); + if (err) + netdev_err(dev, "Failed to leave bridge\n"); + mlxsw_sp_port->bridged = false; + mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev); + } + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = { + .notifier_call = mlxsw_sp_netdevice_event, +}; + +static int __init mlxsw_sp_module_init(void) +{ + int err; + + register_netdevice_notifier(&mlxsw_sp_netdevice_nb); + err = mlxsw_core_driver_register(&mlxsw_sp_driver); + if (err) + goto err_core_driver_register; + return 0; + +err_core_driver_register: + unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); + return err; +} + +static void __exit mlxsw_sp_module_exit(void) +{ + mlxsw_core_driver_unregister(&mlxsw_sp_driver); + unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); +} + +module_init(mlxsw_sp_module_init); +module_exit(mlxsw_sp_module_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>"); +MODULE_DESCRIPTION("Mellanox Spectrum driver"); +MODULE_MLXSW_DRIVER_ALIAS(MLXSW_DEVICE_KIND_SPECTRUM); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h new file mode 100644 index 000000000000..fc0074902ab5 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -0,0 +1,121 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum.h + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com> + * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com> + * Copyright (c) 2015 Elad Raz <eladr@mellanox.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MLXSW_SPECTRUM_H +#define _MLXSW_SPECTRUM_H + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/bitops.h> +#include <linux/if_vlan.h> +#include <net/switchdev.h> + +#include "core.h" + +#define MLXSW_SP_VFID_BASE VLAN_N_VID + +struct mlxsw_sp_port; + +struct mlxsw_sp { + unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; + unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)]; + struct mlxsw_sp_port **ports; + struct mlxsw_core *core; + const struct mlxsw_bus_info *bus_info; + unsigned char base_mac[ETH_ALEN]; + struct { + struct delayed_work dw; +#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100 + unsigned int interval; /* ms */ + } fdb_notify; +#define MLXSW_SP_DEFAULT_AGEING_TIME 300 + u32 ageing_time; + struct { + struct net_device *dev; + unsigned int ref_count; + } master_bridge; +}; + +struct mlxsw_sp_port_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; + struct u64_stats_sync syncp; + u32 tx_dropped; +}; + +struct mlxsw_sp_port { + struct net_device *dev; + struct mlxsw_sp_port_pcpu_stats __percpu *pcpu_stats; + struct mlxsw_sp *mlxsw_sp; + u8 local_port; + u8 stp_state; + u8 learning:1; + u8 learning_sync:1; + u16 pvid; + bool bridged; + /* 802.1Q bridge VLANs */ + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + /* VLAN interfaces */ + unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; + u16 nr_vfids; +}; + +enum mlxsw_sp_flood_table { + MLXSW_SP_FLOOD_TABLE_UC, + MLXSW_SP_FLOOD_TABLE_BM, +}; + +int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp); +int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port); + +int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp); +void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp); +int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port); +void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port); +void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port); +int mlxsw_sp_port_vid_to_fid_set(struct mlxsw_sp_port *mlxsw_sp_port, + enum mlxsw_reg_svfa_mt mt, bool valid, u16 fid, + u16 vid); +int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, + u16 vid_end, bool is_member, bool untagged); +int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, + u16 vid); +int mlxsw_sp_port_kill_vid(struct net_device *dev, + __be16 __always_unused proto, u16 vid); + +#endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c new file mode 100644 index 000000000000..d59195e3f7fb --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -0,0 +1,422 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/types.h> + +#include "spectrum.h" +#include "core.h" +#include "port.h" +#include "reg.h" + +struct mlxsw_sp_pb { + u8 index; + u16 size; +}; + +#define MLXSW_SP_PB(_index, _size) \ + { \ + .index = _index, \ + .size = _size, \ + } + +static const struct mlxsw_sp_pb mlxsw_sp_pbs[] = { + MLXSW_SP_PB(0, 208), + MLXSW_SP_PB(1, 208), + MLXSW_SP_PB(2, 208), + MLXSW_SP_PB(3, 208), + MLXSW_SP_PB(4, 208), + MLXSW_SP_PB(5, 208), + MLXSW_SP_PB(6, 208), + MLXSW_SP_PB(7, 208), + MLXSW_SP_PB(9, 208), +}; + +#define MLXSW_SP_PBS_LEN ARRAY_SIZE(mlxsw_sp_pbs) + +static int mlxsw_sp_port_pb_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + char pbmc_pl[MLXSW_REG_PBMC_LEN]; + int i; + + mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, + 0xffff, 0xffff / 2); + for (i = 0; i < MLXSW_SP_PBS_LEN; i++) { + const struct mlxsw_sp_pb *pb; + + pb = &mlxsw_sp_pbs[i]; + mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pb->index, pb->size); + } + return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, + MLXSW_REG(pbmc), pbmc_pl); +} + +#define MLXSW_SP_SB_BYTES_PER_CELL 96 + +struct mlxsw_sp_sb_pool { + u8 pool; + enum mlxsw_reg_sbpr_dir dir; + enum mlxsw_reg_sbpr_mode mode; + u32 size; +}; + +#define MLXSW_SP_SB_POOL_INGRESS_SIZE \ + ((15000000 - (2 * 20000 * MLXSW_PORT_MAX_PORTS)) / \ + MLXSW_SP_SB_BYTES_PER_CELL) +#define MLXSW_SP_SB_POOL_EGRESS_SIZE \ + ((14000000 - (8 * 1500 * MLXSW_PORT_MAX_PORTS)) / \ + MLXSW_SP_SB_BYTES_PER_CELL) + +#define MLXSW_SP_SB_POOL(_pool, _dir, _mode, _size) \ + { \ + .pool = _pool, \ + .dir = _dir, \ + .mode = _mode, \ + .size = _size, \ + } + +#define MLXSW_SP_SB_POOL_INGRESS(_pool, _size) \ + MLXSW_SP_SB_POOL(_pool, MLXSW_REG_SBPR_DIR_INGRESS, \ + MLXSW_REG_SBPR_MODE_DYNAMIC, _size) + +#define MLXSW_SP_SB_POOL_EGRESS(_pool, _size) \ + MLXSW_SP_SB_POOL(_pool, MLXSW_REG_SBPR_DIR_EGRESS, \ + MLXSW_REG_SBPR_MODE_DYNAMIC, _size) + +static const struct mlxsw_sp_sb_pool mlxsw_sp_sb_pools[] = { + MLXSW_SP_SB_POOL_INGRESS(0, MLXSW_SP_SB_POOL_INGRESS_SIZE), + MLXSW_SP_SB_POOL_INGRESS(1, 0), + MLXSW_SP_SB_POOL_INGRESS(2, 0), + MLXSW_SP_SB_POOL_INGRESS(3, 0), + MLXSW_SP_SB_POOL_EGRESS(0, MLXSW_SP_SB_POOL_EGRESS_SIZE), + MLXSW_SP_SB_POOL_EGRESS(1, 0), + MLXSW_SP_SB_POOL_EGRESS(2, 0), + MLXSW_SP_SB_POOL_EGRESS(2, MLXSW_SP_SB_POOL_EGRESS_SIZE), +}; + +#define MLXSW_SP_SB_POOLS_LEN ARRAY_SIZE(mlxsw_sp_sb_pools) + +static int mlxsw_sp_sb_pools_init(struct mlxsw_sp *mlxsw_sp) +{ + char sbpr_pl[MLXSW_REG_SBPR_LEN]; + int i; + int err; + + for (i = 0; i < MLXSW_SP_SB_POOLS_LEN; i++) { + const struct mlxsw_sp_sb_pool *pool; + + pool = &mlxsw_sp_sb_pools[i]; + mlxsw_reg_sbpr_pack(sbpr_pl, pool->pool, pool->dir, + pool->mode, pool->size); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbpr), sbpr_pl); + if (err) + return err; + } + return 0; +} + +struct mlxsw_sp_sb_cm { + union { + u8 pg; + u8 tc; + } u; + enum mlxsw_reg_sbcm_dir dir; + u32 min_buff; + u32 max_buff; + u8 pool; +}; + +#define MLXSW_SP_SB_CM(_pg_tc, _dir, _min_buff, _max_buff, _pool) \ + { \ + .u.pg = _pg_tc, \ + .dir = _dir, \ + .min_buff = _min_buff, \ + .max_buff = _max_buff, \ + .pool = _pool, \ + } + +#define MLXSW_SP_SB_CM_INGRESS(_pg, _min_buff, _max_buff) \ + MLXSW_SP_SB_CM(_pg, MLXSW_REG_SBCM_DIR_INGRESS, \ + _min_buff, _max_buff, 0) + +#define MLXSW_SP_SB_CM_EGRESS(_tc, _min_buff, _max_buff) \ + MLXSW_SP_SB_CM(_tc, MLXSW_REG_SBCM_DIR_EGRESS, \ + _min_buff, _max_buff, 0) + +#define MLXSW_SP_CPU_PORT_SB_CM_EGRESS(_tc) \ + MLXSW_SP_SB_CM(_tc, MLXSW_REG_SBCM_DIR_EGRESS, 104, 2, 3) + +static const struct mlxsw_sp_sb_cm mlxsw_sp_sb_cms[] = { + MLXSW_SP_SB_CM_INGRESS(0, 10000 / MLXSW_SP_SB_BYTES_PER_CELL, 8), + MLXSW_SP_SB_CM_INGRESS(1, 0, 0), + MLXSW_SP_SB_CM_INGRESS(2, 0, 0), + MLXSW_SP_SB_CM_INGRESS(3, 0, 0), + MLXSW_SP_SB_CM_INGRESS(4, 0, 0), + MLXSW_SP_SB_CM_INGRESS(5, 0, 0), + MLXSW_SP_SB_CM_INGRESS(6, 0, 0), + MLXSW_SP_SB_CM_INGRESS(7, 0, 0), + MLXSW_SP_SB_CM_INGRESS(9, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff), + MLXSW_SP_SB_CM_EGRESS(0, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(1, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(2, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(3, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(4, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(5, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(6, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(7, 1500 / MLXSW_SP_SB_BYTES_PER_CELL, 9), + MLXSW_SP_SB_CM_EGRESS(8, 0, 0), + MLXSW_SP_SB_CM_EGRESS(9, 0, 0), + MLXSW_SP_SB_CM_EGRESS(10, 0, 0), + MLXSW_SP_SB_CM_EGRESS(11, 0, 0), + MLXSW_SP_SB_CM_EGRESS(12, 0, 0), + MLXSW_SP_SB_CM_EGRESS(13, 0, 0), + MLXSW_SP_SB_CM_EGRESS(14, 0, 0), + MLXSW_SP_SB_CM_EGRESS(15, 0, 0), + MLXSW_SP_SB_CM_EGRESS(16, 1, 0xff), +}; + +#define MLXSW_SP_SB_CMS_LEN ARRAY_SIZE(mlxsw_sp_sb_cms) + +static const struct mlxsw_sp_sb_cm mlxsw_sp_cpu_port_sb_cms[] = { + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(0), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(1), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(2), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(3), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(4), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(5), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(6), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(7), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(8), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(9), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(10), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(11), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(12), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(13), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(14), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(15), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(16), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(17), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(18), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(19), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(20), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(21), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(22), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(23), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(24), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(25), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(26), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(27), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(28), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(29), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(30), + MLXSW_SP_CPU_PORT_SB_CM_EGRESS(31), +}; + +#define MLXSW_SP_CPU_PORT_SB_MCS_LEN \ + ARRAY_SIZE(mlxsw_sp_cpu_port_sb_cms) + +static int mlxsw_sp_sb_cms_init(struct mlxsw_sp *mlxsw_sp, u8 local_port, + const struct mlxsw_sp_sb_cm *cms, + size_t cms_len) +{ + char sbcm_pl[MLXSW_REG_SBCM_LEN]; + int i; + int err; + + for (i = 0; i < cms_len; i++) { + const struct mlxsw_sp_sb_cm *cm; + + cm = &cms[i]; + mlxsw_reg_sbcm_pack(sbcm_pl, local_port, cm->u.pg, cm->dir, + cm->min_buff, cm->max_buff, cm->pool); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbcm), sbcm_pl); + if (err) + return err; + } + return 0; +} + +static int mlxsw_sp_port_sb_cms_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + return mlxsw_sp_sb_cms_init(mlxsw_sp_port->mlxsw_sp, + mlxsw_sp_port->local_port, mlxsw_sp_sb_cms, + MLXSW_SP_SB_CMS_LEN); +} + +static int mlxsw_sp_cpu_port_sb_cms_init(struct mlxsw_sp *mlxsw_sp) +{ + return mlxsw_sp_sb_cms_init(mlxsw_sp, 0, mlxsw_sp_cpu_port_sb_cms, + MLXSW_SP_CPU_PORT_SB_MCS_LEN); +} + +struct mlxsw_sp_sb_pm { + u8 pool; + enum mlxsw_reg_sbpm_dir dir; + u32 min_buff; + u32 max_buff; +}; + +#define MLXSW_SP_SB_PM(_pool, _dir, _min_buff, _max_buff) \ + { \ + .pool = _pool, \ + .dir = _dir, \ + .min_buff = _min_buff, \ + .max_buff = _max_buff, \ + } + +#define MLXSW_SP_SB_PM_INGRESS(_pool, _min_buff, _max_buff) \ + MLXSW_SP_SB_PM(_pool, MLXSW_REG_SBPM_DIR_INGRESS, \ + _min_buff, _max_buff) + +#define MLXSW_SP_SB_PM_EGRESS(_pool, _min_buff, _max_buff) \ + MLXSW_SP_SB_PM(_pool, MLXSW_REG_SBPM_DIR_EGRESS, \ + _min_buff, _max_buff) + +static const struct mlxsw_sp_sb_pm mlxsw_sp_sb_pms[] = { + MLXSW_SP_SB_PM_INGRESS(0, 0, 0xff), + MLXSW_SP_SB_PM_INGRESS(1, 0, 0), + MLXSW_SP_SB_PM_INGRESS(2, 0, 0), + MLXSW_SP_SB_PM_INGRESS(3, 0, 0), + MLXSW_SP_SB_PM_EGRESS(0, 0, 7), + MLXSW_SP_SB_PM_EGRESS(1, 0, 0), + MLXSW_SP_SB_PM_EGRESS(2, 0, 0), + MLXSW_SP_SB_PM_EGRESS(3, 0, 0), +}; + +#define MLXSW_SP_SB_PMS_LEN ARRAY_SIZE(mlxsw_sp_sb_pms) + +static int mlxsw_sp_port_sb_pms_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + char sbpm_pl[MLXSW_REG_SBPM_LEN]; + int i; + int err; + + for (i = 0; i < MLXSW_SP_SB_PMS_LEN; i++) { + const struct mlxsw_sp_sb_pm *pm; + + pm = &mlxsw_sp_sb_pms[i]; + mlxsw_reg_sbpm_pack(sbpm_pl, mlxsw_sp_port->local_port, + pm->pool, pm->dir, + pm->min_buff, pm->max_buff); + err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, + MLXSW_REG(sbpm), sbpm_pl); + if (err) + return err; + } + return 0; +} + +struct mlxsw_sp_sb_mm { + u8 prio; + u32 min_buff; + u32 max_buff; + u8 pool; +}; + +#define MLXSW_SP_SB_MM(_prio, _min_buff, _max_buff, _pool) \ + { \ + .prio = _prio, \ + .min_buff = _min_buff, \ + .max_buff = _max_buff, \ + .pool = _pool, \ + } + +static const struct mlxsw_sp_sb_mm mlxsw_sp_sb_mms[] = { + MLXSW_SP_SB_MM(0, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(1, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(2, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(3, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(4, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(5, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(6, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(7, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(8, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(9, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(10, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(11, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(12, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(13, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), + MLXSW_SP_SB_MM(14, 20000 / MLXSW_SP_SB_BYTES_PER_CELL, 0xff, 0), +}; + +#define MLXSW_SP_SB_MMS_LEN ARRAY_SIZE(mlxsw_sp_sb_mms) + +static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp) +{ + char sbmm_pl[MLXSW_REG_SBMM_LEN]; + int i; + int err; + + for (i = 0; i < MLXSW_SP_SB_MMS_LEN; i++) { + const struct mlxsw_sp_sb_mm *mc; + + mc = &mlxsw_sp_sb_mms[i]; + mlxsw_reg_sbmm_pack(sbmm_pl, mc->prio, mc->min_buff, + mc->max_buff, mc->pool); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbmm), sbmm_pl); + if (err) + return err; + } + return 0; +} + +int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp) +{ + int err; + + err = mlxsw_sp_sb_pools_init(mlxsw_sp); + if (err) + return err; + err = mlxsw_sp_cpu_port_sb_cms_init(mlxsw_sp); + if (err) + return err; + err = mlxsw_sp_sb_mms_init(mlxsw_sp); + + return err; +} + +int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + int err; + + err = mlxsw_sp_port_pb_init(mlxsw_sp_port); + if (err) + return err; + err = mlxsw_sp_port_sb_cms_init(mlxsw_sp_port); + if (err) + return err; + err = mlxsw_sp_port_sb_pms_init(mlxsw_sp_port); + + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c new file mode 100644 index 000000000000..c39b7a188726 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -0,0 +1,863 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com> + * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com> + * Copyright (c) 2015 Elad Raz <eladr@mellanox.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/skbuff.h> +#include <linux/if_vlan.h> +#include <linux/if_bridge.h> +#include <linux/workqueue.h> +#include <linux/jiffies.h> +#include <net/switchdev.h> + +#include "spectrum.h" +#include "core.h" +#include "reg.h" + +static int mlxsw_sp_port_attr_get(struct net_device *dev, + struct switchdev_attr *attr) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: + attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac); + memcpy(&attr->u.ppid.id, &mlxsw_sp->base_mac, + attr->u.ppid.id_len); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + attr->u.brport_flags = + (mlxsw_sp_port->learning ? BR_LEARNING : 0) | + (mlxsw_sp_port->learning_sync ? BR_LEARNING_SYNC : 0); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, + u8 state) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + enum mlxsw_reg_spms_state spms_state; + char *spms_pl; + u16 vid; + int err; + + switch (state) { + case BR_STATE_DISABLED: /* fall-through */ + case BR_STATE_FORWARDING: + spms_state = MLXSW_REG_SPMS_STATE_FORWARDING; + break; + case BR_STATE_LISTENING: /* fall-through */ + case BR_STATE_LEARNING: + spms_state = MLXSW_REG_SPMS_STATE_LEARNING; + break; + case BR_STATE_BLOCKING: + spms_state = MLXSW_REG_SPMS_STATE_DISCARDING; + break; + default: + BUG(); + } + + spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL); + if (!spms_pl) + return -ENOMEM; + mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port); + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) + mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state); + + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl); + kfree(spms_pl); + return err; +} + +static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_trans *trans, + u8 state) +{ + if (switchdev_trans_ph_prepare(trans)) + return 0; + + mlxsw_sp_port->stp_state = state; + return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state); +} + +static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_trans *trans, + unsigned long brport_flags) +{ + if (switchdev_trans_ph_prepare(trans)) + return 0; + + mlxsw_sp_port->learning = brport_flags & BR_LEARNING ? 1 : 0; + mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0; + return 0; +} + +static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time) +{ + char sfdat_pl[MLXSW_REG_SFDAT_LEN]; + int err; + + mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl); + if (err) + return err; + mlxsw_sp->ageing_time = ageing_time; + return 0; +} + +static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_trans *trans, + unsigned long ageing_jiffies) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time); +} + +static int mlxsw_sp_port_attr_set(struct net_device *dev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans, + attr->u.stp_state); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans, + attr->u.brport_flags); + break; + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: + err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans, + attr->u.ageing_time); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char spvid_pl[MLXSW_REG_SPVID_LEN]; + + mlxsw_reg_spvid_pack(spvid_pl, mlxsw_sp_port->local_port, vid); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvid), spvid_pl); +} + +static int mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + int err; + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid, fid); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); + + if (err) + return err; + + set_bit(fid, mlxsw_sp->active_fids); + return 0; +} + +static void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, u16 fid) +{ + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + + clear_bit(fid, mlxsw_sp->active_fids); + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, + fid, fid); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); +} + +static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) +{ + enum mlxsw_reg_svfa_mt mt; + + if (mlxsw_sp_port->nr_vfids) + mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + else + mt = MLXSW_REG_SVFA_MT_VID_TO_FID; + + return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, true, fid, fid); +} + +static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) +{ + enum mlxsw_reg_svfa_mt mt; + + if (!mlxsw_sp_port->nr_vfids) + return 0; + + mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, fid, fid); +} + +static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 fid, bool set, bool only_uc) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *sftr_pl; + int err; + + sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); + if (!sftr_pl) + return -ENOMEM; + + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid, + MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, 0, + mlxsw_sp_port->local_port, set); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + if (err) + goto buffer_out; + + /* Flooding control allows one to decide whether a given port will + * flood unicast traffic for which there is no FDB entry. + */ + if (only_uc) + goto buffer_out; + + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid, + MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, 0, + mlxsw_sp_port->local_port, set); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + +buffer_out: + kfree(sftr_pl); + return err; +} + +static int mlxsw_sp_port_add_vids(struct net_device *dev, u16 vid_begin, + u16 vid_end) +{ + u16 vid; + int err; + + for (vid = vid_begin; vid <= vid_end; vid++) { + err = mlxsw_sp_port_add_vid(dev, 0, vid); + if (err) + goto err_port_add_vid; + } + return 0; + +err_port_add_vid: + for (vid--; vid >= vid_begin; vid--) + mlxsw_sp_port_kill_vid(dev, 0, vid); + return err; +} + +static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid_begin, u16 vid_end, + bool flag_untagged, bool flag_pvid) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct net_device *dev = mlxsw_sp_port->dev; + enum mlxsw_reg_svfa_mt mt; + u16 vid, vid_e; + int err; + + /* In case this is invoked with BRIDGE_FLAGS_SELF and port is + * not bridged, then packets ingressing through the port with + * the specified VIDs will be directed to CPU. + */ + if (!mlxsw_sp_port->bridged) + return mlxsw_sp_port_add_vids(dev, vid_begin, vid_end); + + for (vid = vid_begin; vid <= vid_end; vid++) { + if (!test_bit(vid, mlxsw_sp->active_fids)) { + err = mlxsw_sp_fid_create(mlxsw_sp, vid); + if (err) { + netdev_err(dev, "Failed to create FID=%d\n", + vid); + return err; + } + + /* When creating a FID, we set a VID to FID mapping + * regardless of the port's mode. + */ + mt = MLXSW_REG_SVFA_MT_VID_TO_FID; + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, + true, vid, vid); + if (err) { + netdev_err(dev, "Failed to create FID=VID=%d mapping\n", + vid); + return err; + } + } + + /* Set FID mapping according to port's mode */ + err = mlxsw_sp_port_fid_map(mlxsw_sp_port, vid); + if (err) { + netdev_err(dev, "Failed to map FID=%d", vid); + return err; + } + + err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, true, + false); + if (err) { + netdev_err(dev, "Failed to set flooding for FID=%d", + vid); + return err; + } + } + + for (vid = vid_begin; vid <= vid_end; + vid += MLXSW_REG_SPVM_REC_MAX_COUNT) { + vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1), + vid_end); + + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, true, + flag_untagged); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to add VIDs %d-%d\n", + vid, vid_e); + return err; + } + } + + vid = vid_begin; + if (flag_pvid && mlxsw_sp_port->pvid != vid) { + err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to add PVID %d\n", + vid); + return err; + } + mlxsw_sp_port->pvid = vid; + } + + /* Changing activity bits only if HW operation succeded */ + for (vid = vid_begin; vid <= vid_end; vid++) + set_bit(vid, mlxsw_sp_port->active_vlans); + + return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, + mlxsw_sp_port->stp_state); +} + +static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return __mlxsw_sp_port_vlans_add(mlxsw_sp_port, + vlan->vid_begin, vlan->vid_end, + untagged_flag, pvid_flag); +} + +static int mlxsw_sp_port_fdb_op(struct mlxsw_sp_port *mlxsw_sp_port, + const char *mac, u16 vid, bool adding, + bool dynamic) +{ + enum mlxsw_reg_sfd_rec_policy policy; + enum mlxsw_reg_sfd_op op; + char *sfd_pl; + int err; + + if (!vid) + vid = mlxsw_sp_port->pvid; + + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); + if (!sfd_pl) + return -ENOMEM; + + policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS : + MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY; + op = adding ? MLXSW_REG_SFD_OP_WRITE_EDIT : + MLXSW_REG_SFD_OP_WRITE_REMOVE; + mlxsw_reg_sfd_pack(sfd_pl, op, 0); + mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy, + mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP, + mlxsw_sp_port->local_port); + err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd), + sfd_pl); + kfree(sfd_pl); + + return err; +} + +static int +mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) +{ + if (switchdev_trans_ph_prepare(trans)) + return 0; + + return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid, + true, false); +} + +static int mlxsw_sp_port_obj_add(struct net_device *dev, + const struct switchdev_obj *obj, + struct switchdev_trans *trans) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), + trans); + break; + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = mlxsw_sp_port_fdb_static_add(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_FDB(obj), + trans); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int mlxsw_sp_port_kill_vids(struct net_device *dev, u16 vid_begin, + u16 vid_end) +{ + u16 vid; + int err; + + for (vid = vid_begin; vid <= vid_end; vid++) { + err = mlxsw_sp_port_kill_vid(dev, 0, vid); + if (err) + return err; + } + + return 0; +} + +static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid_begin, u16 vid_end, bool init) +{ + struct net_device *dev = mlxsw_sp_port->dev; + u16 vid, vid_e; + int err; + + /* In case this is invoked with BRIDGE_FLAGS_SELF and port is + * not bridged, then prevent packets ingressing through the + * port with the specified VIDs from being trapped to CPU. + */ + if (!init && !mlxsw_sp_port->bridged) + return mlxsw_sp_port_kill_vids(dev, vid_begin, vid_end); + + for (vid = vid_begin; vid <= vid_end; + vid += MLXSW_REG_SPVM_REC_MAX_COUNT) { + vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1), + vid_end); + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false, + false); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to del VIDs %d-%d\n", + vid, vid_e); + return err; + } + } + + if ((mlxsw_sp_port->pvid >= vid_begin) && + (mlxsw_sp_port->pvid <= vid_end)) { + /* Default VLAN is always 1 */ + mlxsw_sp_port->pvid = 1; + err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, + mlxsw_sp_port->pvid); + if (err) { + netdev_err(mlxsw_sp_port->dev, "Unable to del PVID %d\n", + vid); + return err; + } + } + + if (init) + goto out; + + for (vid = vid_begin; vid <= vid_end; vid++) { + err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, false, + false); + if (err) { + netdev_err(dev, "Failed to clear flooding for FID=%d", + vid); + return err; + } + + /* Remove FID mapping in case of Virtual mode */ + err = mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid); + if (err) { + netdev_err(dev, "Failed to unmap FID=%d", vid); + return err; + } + } + +out: + /* Changing activity bits only if HW operation succeded */ + for (vid = vid_begin; vid <= vid_end; vid++) + clear_bit(vid, mlxsw_sp_port->active_vlans); + + return 0; +} + +static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_vlan *vlan) +{ + return __mlxsw_sp_port_vlans_del(mlxsw_sp_port, + vlan->vid_begin, vlan->vid_end, false); +} + +static int +mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_fdb *fdb) +{ + return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid, + false, false); +} + +static int mlxsw_sp_port_obj_del(struct net_device *dev, + const struct switchdev_obj *obj) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = mlxsw_sp_port_vlans_del(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_VLAN(obj)); + break; + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_FDB(obj)); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_obj_port_fdb *fdb, + switchdev_obj_dump_cb_t *cb) +{ + char *sfd_pl; + char mac[ETH_ALEN]; + u16 vid; + u8 local_port; + u8 num_rec; + int stored_err = 0; + int i; + int err; + + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); + if (!sfd_pl) + return -ENOMEM; + + mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0); + do { + mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT); + err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core, + MLXSW_REG(sfd), sfd_pl); + if (err) + goto out; + + num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); + + /* Even in case of error, we have to run the dump to the end + * so the session in firmware is finished. + */ + if (stored_err) + continue; + + for (i = 0; i < num_rec; i++) { + switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) { + case MLXSW_REG_SFD_REC_TYPE_UNICAST: + mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &vid, + &local_port); + if (local_port == mlxsw_sp_port->local_port) { + ether_addr_copy(fdb->addr, mac); + fdb->ndm_state = NUD_REACHABLE; + fdb->vid = vid; + err = cb(&fdb->obj); + if (err) + stored_err = err; + } + } + } + } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT); + +out: + kfree(sfd_pl); + return stored_err ? stored_err : err; +} + +static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_obj_port_vlan *vlan, + switchdev_obj_dump_cb_t *cb) +{ + u16 vid; + int err = 0; + + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { + vlan->flags = 0; + if (vid == mlxsw_sp_port->pvid) + vlan->flags |= BRIDGE_VLAN_INFO_PVID; + vlan->vid_begin = vid; + vlan->vid_end = vid; + err = cb(&vlan->obj); + if (err) + break; + } + return err; +} + +static int mlxsw_sp_port_obj_dump(struct net_device *dev, + struct switchdev_obj *obj, + switchdev_obj_dump_cb_t *cb) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + int err = 0; + + switch (obj->id) { + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), cb); + break; + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = mlxsw_sp_port_fdb_dump(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_FDB(obj), cb); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +const struct switchdev_ops mlxsw_sp_port_switchdev_ops = { + .switchdev_port_attr_get = mlxsw_sp_port_attr_get, + .switchdev_port_attr_set = mlxsw_sp_port_attr_set, + .switchdev_port_obj_add = mlxsw_sp_port_obj_add, + .switchdev_port_obj_del = mlxsw_sp_port_obj_del, + .switchdev_port_obj_dump = mlxsw_sp_port_obj_dump, +}; + +static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, + char *sfn_pl, int rec_index, + bool adding) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + char mac[ETH_ALEN]; + u8 local_port; + u16 vid; + int err; + + mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &vid, &local_port); + mlxsw_sp_port = mlxsw_sp->ports[local_port]; + if (!mlxsw_sp_port) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n"); + return; + } + + err = mlxsw_sp_port_fdb_op(mlxsw_sp_port, mac, vid, + adding && mlxsw_sp_port->learning, true); + if (err) { + if (net_ratelimit()) + netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n"); + return; + } + + if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) { + struct switchdev_notifier_fdb_info info; + unsigned long notifier_type; + + info.addr = mac; + info.vid = vid; + notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL; + call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev, + &info.info); + } +} + +static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, + char *sfn_pl, int rec_index) +{ + switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) { + case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC: + mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, + rec_index, true); + break; + case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC: + mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, + rec_index, false); + break; + } +} + +static void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp) +{ + schedule_delayed_work(&mlxsw_sp->fdb_notify.dw, + msecs_to_jiffies(mlxsw_sp->fdb_notify.interval)); +} + +static void mlxsw_sp_fdb_notify_work(struct work_struct *work) +{ + struct mlxsw_sp *mlxsw_sp; + char *sfn_pl; + u8 num_rec; + int i; + int err; + + sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL); + if (!sfn_pl) + return; + + mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work); + + do { + mlxsw_reg_sfn_pack(sfn_pl); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl); + if (err) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n"); + break; + } + num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl); + for (i = 0; i < num_rec; i++) + mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i); + + } while (num_rec); + + kfree(sfn_pl); + mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); +} + +static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) +{ + int err; + + err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n"); + return err; + } + INIT_DELAYED_WORK(&mlxsw_sp->fdb_notify.dw, mlxsw_sp_fdb_notify_work); + mlxsw_sp->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; + mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); + return 0; +} + +static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) +{ + cancel_delayed_work_sync(&mlxsw_sp->fdb_notify.dw); +} + +static void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp) +{ + u16 fid; + + for_each_set_bit(fid, mlxsw_sp->active_fids, VLAN_N_VID) + mlxsw_sp_fid_destroy(mlxsw_sp, fid); +} + +int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) +{ + return mlxsw_sp_fdb_init(mlxsw_sp); +} + +void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp_fdb_fini(mlxsw_sp); + mlxsw_sp_fids_fini(mlxsw_sp); +} + +int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct net_device *dev = mlxsw_sp_port->dev; + int err; + + /* Allow only untagged packets to ingress and tag them internally + * with VID 1. + */ + mlxsw_sp_port->pvid = 1; + err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true); + if (err) { + netdev_err(dev, "Unable to init VLANs\n"); + return err; + } + + /* Add implicit VLAN interface in the device, so that untagged + * packets will be classified to the default vFID. + */ + err = mlxsw_sp_port_add_vid(dev, 0, 1); + if (err) + netdev_err(dev, "Failed to configure default vFID\n"); + + return err; +} + +void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port) +{ + mlxsw_sp_port->dev->switchdev_ops = &mlxsw_sp_port_switchdev_ops; +} + +void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port) +{ +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 62cbbd1ada8d..50e29c4879db 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -57,13 +57,11 @@ static const char mlxsw_sx_driver_version[] = "1.0"; struct mlxsw_sx_port; -#define MLXSW_SW_HW_ID_LEN 6 - struct mlxsw_sx { struct mlxsw_sx_port **ports; struct mlxsw_core *core; const struct mlxsw_bus_info *bus_info; - u8 hw_id[MLXSW_SW_HW_ID_LEN]; + u8 hw_id[ETH_ALEN]; }; struct mlxsw_sx_port_pcpu_stats { @@ -868,7 +866,7 @@ static int mlxsw_sx_port_attr_get(struct net_device *dev, struct mlxsw_sx *mlxsw_sx = mlxsw_sx_port->mlxsw_sx; switch (attr->id) { - case SWITCHDEV_ATTR_PORT_PARENT_ID: + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: attr->u.ppid.id_len = sizeof(mlxsw_sx->hw_id); memcpy(&attr->u.ppid.id, &mlxsw_sx->hw_id, attr->u.ppid.id_len); break; @@ -925,7 +923,8 @@ static int mlxsw_sx_port_stp_state_set(struct mlxsw_sx_port *mlxsw_sx_port, spms_pl = kmalloc(MLXSW_REG_SPMS_LEN, GFP_KERNEL); if (!spms_pl) return -ENOMEM; - mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port, vid, state); + mlxsw_reg_spms_pack(spms_pl, mlxsw_sx_port->local_port); + mlxsw_reg_spms_vid_pack(spms_pl, vid, state); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(spms), spms_pl); kfree(spms_pl); return err; @@ -1178,8 +1177,7 @@ static int mlxsw_sx_event_register(struct mlxsw_sx *mlxsw_sx, if (err) return err; - mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - MLXSW_REG_HTGT_TRAP_GROUP_EMAD, trap_id); + mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, trap_id); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); if (err) goto err_event_trap_set; @@ -1212,9 +1210,8 @@ static void mlxsw_sx_rx_listener_func(struct sk_buff *skb, u8 local_port, struct mlxsw_sx_port_pcpu_stats *pcpu_stats; if (unlikely(!mlxsw_sx_port)) { - if (net_ratelimit()) - dev_warn(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n", - local_port); + dev_warn_ratelimited(mlxsw_sx->bus_info->dev, "Port %d: skb received for non-existent port\n", + local_port); return; } @@ -1316,6 +1313,11 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) if (err) return err; + mlxsw_reg_htgt_pack(htgt_pl, MLXSW_REG_HTGT_TRAP_GROUP_CTRL); + err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(htgt), htgt_pl); + if (err) + return err; + for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) { err = mlxsw_core_rx_listener_register(mlxsw_sx->core, &mlxsw_sx_rx_listener[i], @@ -1324,7 +1326,6 @@ static int mlxsw_sx_traps_init(struct mlxsw_sx *mlxsw_sx) goto err_rx_listener_register; mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_TRAP_TO_CPU, - MLXSW_REG_HTGT_TRAP_GROUP_RX, mlxsw_sx_rx_listener[i].trap_id); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); if (err) @@ -1339,7 +1340,6 @@ err_rx_trap_set: err_rx_listener_register: for (i--; i >= 0; i--) { mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - MLXSW_REG_HTGT_TRAP_GROUP_RX, mlxsw_sx_rx_listener[i].trap_id); mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); @@ -1357,7 +1357,6 @@ static void mlxsw_sx_traps_fini(struct mlxsw_sx *mlxsw_sx) for (i = 0; i < ARRAY_SIZE(mlxsw_sx_rx_listener); i++) { mlxsw_reg_hpkt_pack(hpkt_pl, MLXSW_REG_HPKT_ACTION_FORWARD, - MLXSW_REG_HTGT_TRAP_GROUP_RX, mlxsw_sx_rx_listener[i].trap_id); mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(hpkt), hpkt_pl); @@ -1371,25 +1370,15 @@ static int mlxsw_sx_flood_init(struct mlxsw_sx *mlxsw_sx) { char sfgc_pl[MLXSW_REG_SFGC_LEN]; char sgcr_pl[MLXSW_REG_SGCR_LEN]; - char *smid_pl; char *sftr_pl; int err; - /* Due to FW bug, we must configure SMID. */ - smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL); - if (!smid_pl) - return -ENOMEM; - mlxsw_reg_smid_pack(smid_pl, MLXSW_PORT_MID); - err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(smid), smid_pl); - kfree(smid_pl); - if (err) - return err; - /* Configure a flooding table, which includes only CPU port. */ sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); if (!sftr_pl) return -ENOMEM; - mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0); + mlxsw_reg_sftr_pack(sftr_pl, 0, 0, MLXSW_REG_SFGC_TABLE_TYPE_SINGLE, 0, + MLXSW_PORT_CPU_PORT, true); err = mlxsw_reg_write(mlxsw_sx->core, MLXSW_REG(sftr), sftr_pl); kfree(sftr_pl); if (err) diff --git a/drivers/net/ethernet/mellanox/mlxsw/txheader.h b/drivers/net/ethernet/mellanox/mlxsw/txheader.h index 06fc46c78a0b..fdf94720ca62 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/txheader.h +++ b/drivers/net/ethernet/mellanox/mlxsw/txheader.h @@ -38,6 +38,7 @@ #define MLXSW_TXHDR_LEN 0x10 #define MLXSW_TXHDR_VERSION_0 0 +#define MLXSW_TXHDR_VERSION_1 1 enum { MLXSW_TXHDR_ETH_CTL, diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 3fd8ca6d4e7c..36a09d94b368 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -33,4 +33,13 @@ config ENC28J60_WRITEVERIFY Enable the verify after the buffer write useful for debugging purpose. If unsure, say N. +config ENCX24J600 + tristate "ENCX24J600 support" + depends on SPI + ---help--- + Support for the Microchip ENC424J600/624J600 ethernet chip. + + To compile this driver as a module, choose M here. The module will be + called encx24j600. + endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index 573d4292b9ea..ff78f621b59a 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_ENC28J60) += enc28j60.o +obj-$(CONFIG_ENCX24J600) += encx24j600.o encx24j600-regmap.o diff --git a/drivers/net/ethernet/microchip/encx24j600-regmap.c b/drivers/net/ethernet/microchip/encx24j600-regmap.c new file mode 100644 index 000000000000..f3bb9055a292 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600-regmap.c @@ -0,0 +1,513 @@ +/** + * Register map access API - ENCX24J600 support + * + * Copyright 2015 Gridpoint + * + * Author: Jon Ringle <jringle@gridpoint.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "encx24j600_hw.h" + +static inline bool is_bits_set(int value, int mask) +{ + return (value & mask) == mask; +} + +static int encx24j600_switch_bank(struct encx24j600_context *ctx, + int bank) +{ + int ret = 0; + + int bank_opcode = BANK_SELECT(bank); + ret = spi_write(ctx->spi, &bank_opcode, 1); + if (ret == 0) + ctx->bank = bank; + + return ret; +} + +static int encx24j600_cmdn(struct encx24j600_context *ctx, u8 opcode, + const void *buf, size_t len) +{ + struct spi_message m; + struct spi_transfer t[2] = { { .tx_buf = &opcode, .len = 1, }, + { .tx_buf = buf, .len = len }, }; + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + return spi_sync(ctx->spi, &m); +} + +static void regmap_lock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_lock(&ctx->mutex); +} + +static void regmap_unlock_mutex(void *context) +{ + struct encx24j600_context *ctx = context; + mutex_unlock(&ctx->mutex); +} + +static int regmap_encx24j600_sfr_read(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = RCRU; + int ret = 0; + int i = 0; + u8 tx_buf[2]; + + if (reg < 0x80) { + cmd = RCRCODE | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = RGPRDPT; break; + case EGPWRPT: + cmd = RGPWRPT; break; + case ERXRDPT: + cmd = RRXRDPT; break; + case ERXWRPT: + cmd = RRXWRPT; break; + case EUDARDPT: + cmd = RUDARDPT; break; + case EUDAWRPT: + cmd = RUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + tx_buf[i++] = cmd; + if (cmd == RCRU) + tx_buf[i++] = reg; + + ret = spi_write_then_read(ctx->spi, tx_buf, i, val, len); + + return ret; +} + +static int regmap_encx24j600_sfr_update(struct encx24j600_context *ctx, + u8 reg, u8 *val, size_t len, + u8 unbanked_cmd, u8 banked_code) +{ + u8 banked_reg = reg & ADDR_MASK; + u8 bank = ((reg & BANK_MASK) >> BANK_SHIFT); + u8 cmd = unbanked_cmd; + struct spi_message m; + struct spi_transfer t[3] = { { .tx_buf = &cmd, .len = sizeof(cmd), }, + { .tx_buf = ®, .len = sizeof(reg), }, + { .tx_buf = val, .len = len }, }; + + if (reg < 0x80) { + int ret = 0; + cmd = banked_code | banked_reg; + if ((banked_reg < 0x16) && (ctx->bank != bank)) + ret = encx24j600_switch_bank(ctx, bank); + if (unlikely(ret)) + return ret; + } else { + /* Translate registers that are more effecient using + * 3-byte SPI commands + */ + switch (reg) { + case EGPRDPT: + cmd = WGPRDPT; break; + case EGPWRPT: + cmd = WGPWRPT; break; + case ERXRDPT: + cmd = WRXRDPT; break; + case ERXWRPT: + cmd = WRXWRPT; break; + case EUDARDPT: + cmd = WUDARDPT; break; + case EUDAWRPT: + cmd = WUDAWRPT; break; + case EGPDATA: + case ERXDATA: + case EUDADATA: + default: + return -EINVAL; + } + } + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + + if (cmd == unbanked_cmd) { + t[1].tx_buf = ® + spi_message_add_tail(&t[1], &m); + } + + spi_message_add_tail(&t[2], &m); + return spi_sync(ctx->spi, &m); +} + +static int regmap_encx24j600_sfr_write(void *context, u8 reg, u8 *val, + size_t len) +{ + struct encx24j600_context *ctx = context; + return regmap_encx24j600_sfr_update(ctx, reg, val, len, WCRU, WCRCODE); +} + +static int regmap_encx24j600_sfr_set_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFSU, BFSCODE); +} + +static int regmap_encx24j600_sfr_clr_bits(struct encx24j600_context *ctx, + u8 reg, u8 val) +{ + return regmap_encx24j600_sfr_update(ctx, reg, &val, 1, BFCU, BFCCODE); +} + +static int regmap_encx24j600_reg_update_bits(void *context, unsigned int reg, + unsigned int mask, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + + int ret = 0; + unsigned int set_mask = mask & val; + unsigned int clr_mask = mask & ~val; + + if ((reg >= 0x40 && reg < 0x6c) || reg >= 0x80) + return -EINVAL; + + if (set_mask & 0xff) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg, set_mask); + + set_mask = (set_mask & 0xff00) >> 8; + + if ((set_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_set_bits(ctx, reg + 1, set_mask); + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg, clr_mask); + + clr_mask = (clr_mask & 0xff00) >> 8; + + if ((clr_mask & 0xff) && (ret == 0)) + ret = regmap_encx24j600_sfr_clr_bits(ctx, reg + 1, clr_mask); + + return ret; +} + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg < 0xc0) + return encx24j600_cmdn(ctx, reg, data, count); + else + /* SPI 1-byte command. Ignore data */ + return spi_write(ctx->spi, ®, 1); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_write); + +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count) +{ + struct encx24j600_context *ctx = context; + + if (reg == RBSEL && count > 1) + count = 1; + + return spi_write_then_read(ctx->spi, ®, sizeof(reg), data, count); +} +EXPORT_SYMBOL_GPL(regmap_encx24j600_spi_read); + +static int regmap_encx24j600_write(void *context, const void *data, + size_t len) +{ + u8 *dout = (u8 *)data; + u8 reg = dout[0]; + ++dout; + --len; + + if (reg > 0xa0) + return regmap_encx24j600_spi_write(context, reg, dout, len); + + if (len > 2) + return -EINVAL; + + return regmap_encx24j600_sfr_write(context, reg, dout, len); +} + +static int regmap_encx24j600_read(void *context, + const void *reg_buf, size_t reg_size, + void *val, size_t val_size) +{ + u8 reg = *(const u8 *)reg_buf; + + if (reg_size != 1) { + pr_err("%s: reg=%02x reg_size=%zu\n", __func__, reg, reg_size); + return -EINVAL; + } + + if (reg > 0xa0) + return regmap_encx24j600_spi_read(context, reg, val, val_size); + + if (val_size > 2) { + pr_err("%s: reg=%02x val_size=%zu\n", __func__, reg, val_size); + return -EINVAL; + } + + return regmap_encx24j600_sfr_read(context, reg, val, val_size); +} + +static bool encx24j600_regmap_readable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x36) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x66)) || + ((reg >= 0x68) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + (reg == 0xc8)) + return true; + else + return false; +} + +static bool encx24j600_regmap_writeable(struct device *dev, unsigned int reg) +{ + if ((reg < 0x12) || + ((reg >= 0x14) && (reg < 0x1a)) || + ((reg >= 0x1c) && (reg < 0x36)) || + ((reg >= 0x40) && (reg < 0x4c)) || + ((reg >= 0x52) && (reg < 0x56)) || + ((reg >= 0x60) && (reg < 0x68)) || + ((reg >= 0x6c) && (reg < 0x80)) || + ((reg >= 0x86) && (reg < 0x92)) || + ((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static bool encx24j600_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ERXHEAD: + case EDMACS: + case ETXSTAT: + case ETXWIRE: + case ECON1: /* Can be modified via single byte cmds */ + case ECON2: /* Can be modified via single byte cmds */ + case ESTAT: + case EIR: /* Can be modified via single byte cmds */ + case MIRD: + case MISTAT: + return true; + default: + break; + } + + return false; +} + +static bool encx24j600_regmap_precious(struct device *dev, unsigned int reg) +{ + /* single byte cmds are precious */ + if (((reg >= 0xc0) && (reg < 0xc8)) || + ((reg >= 0xca) && (reg < 0xf0))) + return true; + else + return false; +} + +static int regmap_encx24j600_phy_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, MIIRD); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MICMD, 0); + if (unlikely(ret)) + goto err_out; + + ret = regmap_read(ctx->regmap, MIRD, val); + +err_out: + if (ret) + pr_err("%s: error %d reading reg %02x\n", __func__, ret, + reg & PHREG_MASK); + + return ret; +} + +static int regmap_encx24j600_phy_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct encx24j600_context *ctx = context; + int ret; + unsigned int mistat; + + reg = MIREGADR_VAL | (reg & PHREG_MASK); + ret = regmap_write(ctx->regmap, MIREGADR, reg); + if (unlikely(ret)) + goto err_out; + + ret = regmap_write(ctx->regmap, MIWR, val); + if (unlikely(ret)) + goto err_out; + + usleep_range(26, 100); + while ((ret = regmap_read(ctx->regmap, MISTAT, &mistat) != 0) && + (mistat & BUSY)) + cpu_relax(); + +err_out: + if (ret) + pr_err("%s: error %d writing reg %02x=%04x\n", __func__, ret, + reg & PHREG_MASK, val); + + return ret; +} + +static bool encx24j600_phymap_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHSTAT1: + case PHANA: + case PHANLPA: + case PHANE: + case PHCON2: + case PHSTAT2: + case PHSTAT3: + return true; + default: + return false; + } +} + +static bool encx24j600_phymap_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHCON1: + case PHCON2: + case PHANA: + return true; + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + default: + return false; + } +} + +static bool encx24j600_phymap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PHSTAT1: + case PHSTAT2: + case PHSTAT3: + case PHANLPA: + case PHANE: + case PHCON2: + return true; + default: + return false; + } +} + +static struct regmap_config regcfg = { + .name = "reg", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xee, + .reg_stride = 2, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_regmap_readable, + .writeable_reg = encx24j600_regmap_writeable, + .volatile_reg = encx24j600_regmap_volatile, + .precious_reg = encx24j600_regmap_precious, + .lock = regmap_lock_mutex, + .unlock = regmap_unlock_mutex, +}; + +static struct regmap_bus regmap_encx24j600 = { + .write = regmap_encx24j600_write, + .read = regmap_encx24j600_read, + .reg_update_bits = regmap_encx24j600_reg_update_bits, +}; + +static struct regmap_config phycfg = { + .name = "phy", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0x1f, + .cache_type = REGCACHE_RBTREE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .readable_reg = encx24j600_phymap_readable, + .writeable_reg = encx24j600_phymap_writeable, + .volatile_reg = encx24j600_phymap_volatile, +}; +static struct regmap_bus phymap_encx24j600 = { + .reg_write = regmap_encx24j600_phy_reg_write, + .reg_read = regmap_encx24j600_phy_reg_read, +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx) +{ + mutex_init(&ctx->mutex); + regcfg.lock_arg = ctx; + ctx->regmap = devm_regmap_init(dev, ®map_encx24j600, ctx, ®cfg); + ctx->phymap = devm_regmap_init(dev, &phymap_encx24j600, ctx, &phycfg); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_encx24j600); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c new file mode 100644 index 000000000000..e1329d9c2acc --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -0,0 +1,1127 @@ +/** + * Microchip ENCX24J600 ethernet driver + * + * Copyright (C) 2015 Gridpoint + * Author: Jon Ringle <jringle@gridpoint.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/regmap.h> +#include <linux/skbuff.h> +#include <linux/spi/spi.h> + +#include "encx24j600_hw.h" + +#define DRV_NAME "encx24j600" +#define DRV_VERSION "1.0" + +#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +/* SRAM memory layout: + * + * 0x0000-0x05ff TX buffers 1.5KB (1*1536) reside in the GP area in SRAM + * 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM + */ +#define ENC_TX_BUF_START 0x0000U +#define ENC_RX_BUF_START 0x0600U +#define ENC_RX_BUF_END 0x5fffU +#define ENC_SRAM_SIZE 0x6000U + +enum { + RXFILTER_NORMAL, + RXFILTER_MULTI, + RXFILTER_PROMISC +}; + +struct encx24j600_priv { + struct net_device *ndev; + struct mutex lock; /* device access lock */ + struct encx24j600_context ctx; + struct sk_buff *tx_skb; + struct task_struct *kworker_task; + struct kthread_worker kworker; + struct kthread_work tx_work; + struct kthread_work setrx_work; + u16 next_packet; + bool hw_enabled; + bool full_duplex; + bool autoneg; + u16 speed; + int rxfilter; + u32 msg_enable; +}; + +static void dump_packet(const char *msg, int len, const char *data) +{ + pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len); + print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len); +} + +static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + + netdev_info(dev, "RX packet Len:%d\n", rsv->len); + netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg, + rsv->next_packet); + netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXOK), + RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE)); + netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_CRCERROR), + RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR), + RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE)); + netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST), + RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST), + RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV), + RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV)); + netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n", + RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME), + RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE), + RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN)); +} + +static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.regmap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg, + u16 mask, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n", + __func__, ret, reg, val, mask); +} + +static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg) +{ + struct net_device *dev = priv->ndev; + unsigned int val = 0; + int ret = regmap_read(priv->ctx.phymap, reg, &val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d reading %02x\n", + __func__, ret, reg); + return val; +} + +static void encx24j600_write_phy(struct encx24j600_priv *priv, u8 reg, u16 val) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.phymap, reg, val); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n", + __func__, ret, reg, val); +} + +static void encx24j600_clr_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, 0); +} + +static void encx24j600_set_bits(struct encx24j600_priv *priv, u8 reg, u16 mask) +{ + encx24j600_update_reg(priv, reg, mask, mask); +} + +static void encx24j600_cmd(struct encx24j600_priv *priv, u8 cmd) +{ + struct net_device *dev = priv->ndev; + int ret = regmap_write(priv->ctx.regmap, cmd, 0); + if (unlikely(ret)) + netif_err(priv, drv, dev, "%s: error %d with cmd %02x\n", + __func__, ret, cmd); +} + +static int encx24j600_raw_read(struct encx24j600_priv *priv, u8 reg, u8 *data, + size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_read(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static int encx24j600_raw_write(struct encx24j600_priv *priv, u8 reg, + const u8 *data, size_t count) +{ + int ret; + mutex_lock(&priv->ctx.mutex); + ret = regmap_encx24j600_spi_write(&priv->ctx, reg, data, count); + mutex_unlock(&priv->ctx.mutex); + + return ret; +} + +static void encx24j600_update_phcon1(struct encx24j600_priv *priv) +{ + u16 phcon1 = encx24j600_read_phy(priv, PHCON1); + if (priv->autoneg == AUTONEG_ENABLE) { + phcon1 |= ANEN | RENEG; + } else { + phcon1 &= ~ANEN; + if (priv->speed == SPEED_100) + phcon1 |= SPD100; + else + phcon1 &= ~SPD100; + + if (priv->full_duplex) + phcon1 |= PFULDPX; + else + phcon1 &= ~PFULDPX; + } + encx24j600_write_phy(priv, PHCON1, phcon1); +} + +/* Waits for autonegotiation to complete. */ +static int encx24j600_wait_for_autoneg(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long timeout = jiffies + msecs_to_jiffies(2000); + u16 phstat1; + u16 estat; + int ret = 0; + + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + while ((phstat1 & ANDONE) == 0) { + if (time_after(jiffies, timeout)) { + u16 phstat3; + + netif_notice(priv, drv, dev, "timeout waiting for autoneg done\n"); + + priv->autoneg = AUTONEG_DISABLE; + phstat3 = encx24j600_read_phy(priv, PHSTAT3); + priv->speed = (phstat3 & PHY3SPD100) + ? SPEED_100 : SPEED_10; + priv->full_duplex = (phstat3 & PHY3DPX) ? 1 : 0; + encx24j600_update_phcon1(priv); + netif_notice(priv, drv, dev, "Using parallel detection: %s/%s", + priv->speed == SPEED_100 ? "100" : "10", + priv->full_duplex ? "Full" : "Half"); + + return -ETIMEDOUT; + } + cpu_relax(); + phstat1 = encx24j600_read_phy(priv, PHSTAT1); + } + + estat = encx24j600_read_reg(priv, ESTAT); + if (estat & PHYDPX) { + encx24j600_set_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x15); + } else { + encx24j600_clr_bits(priv, MACON2, FULDPX); + encx24j600_write_reg(priv, MABBIPG, 0x12); + /* Max retransmittions attempt */ + encx24j600_write_reg(priv, MACLCON, 0x370f); + } + + return ret; +} + +/* Access the PHY to determine link status */ +static void encx24j600_check_link_status(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + u16 estat; + + estat = encx24j600_read_reg(priv, ESTAT); + + if (estat & PHYLNK) { + if (priv->autoneg == AUTONEG_ENABLE) + encx24j600_wait_for_autoneg(priv); + + netif_carrier_on(dev); + netif_info(priv, ifup, dev, "link up\n"); + } else { + netif_info(priv, ifdown, dev, "link down\n"); + + /* Re-enable autoneg since we won't know what we might be + * connected to when the link is brought back up again. + */ + priv->autoneg = AUTONEG_ENABLE; + priv->full_duplex = true; + priv->speed = SPEED_100; + netif_carrier_off(dev); + } +} + +static void encx24j600_int_link_handler(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + + netif_dbg(priv, intr, dev, "%s", __func__); + encx24j600_check_link_status(priv); + encx24j600_clr_bits(priv, EIR, LINKIF); +} + +static void encx24j600_tx_complete(struct encx24j600_priv *priv, bool err) +{ + struct net_device *dev = priv->ndev; + + if (!priv->tx_skb) { + BUG(); + return; + } + + mutex_lock(&priv->lock); + + if (err) + dev->stats.tx_errors++; + else + dev->stats.tx_packets++; + + dev->stats.tx_bytes += priv->tx_skb->len; + + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + netif_dbg(priv, tx_done, dev, "TX Done%s\n", err ? ": Err" : ""); + + dev_kfree_skb(priv->tx_skb); + priv->tx_skb = NULL; + + netif_wake_queue(dev); + + mutex_unlock(&priv->lock); +} + +static int encx24j600_receive_packet(struct encx24j600_priv *priv, + struct rsv *rsv) +{ + struct net_device *dev = priv->ndev; + struct sk_buff *skb = netdev_alloc_skb(dev, rsv->len + NET_IP_ALIGN); + if (!skb) { + pr_err_ratelimited("RX: OOM: packet dropped\n"); + dev->stats.rx_dropped++; + return -ENOMEM; + } + skb_reserve(skb, NET_IP_ALIGN); + encx24j600_raw_read(priv, RRXDATA, skb_put(skb, rsv->len), rsv->len); + + if (netif_msg_pktdata(priv)) + dump_packet("RX", skb->len, skb->data); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_COMPLETE; + + /* Maintain stats */ + dev->stats.rx_packets++; + dev->stats.rx_bytes += rsv->len; + priv->next_packet = rsv->next_packet; + + netif_rx(skb); + + return 0; +} + +static void encx24j600_rx_packets(struct encx24j600_priv *priv, u8 packet_count) +{ + struct net_device *dev = priv->ndev; + + while (packet_count--) { + struct rsv rsv; + u16 newrxtail; + + encx24j600_write_reg(priv, ERXRDPT, priv->next_packet); + encx24j600_raw_read(priv, RRXDATA, (u8 *)&rsv, sizeof(rsv)); + + if (netif_msg_rx_status(priv)) + encx24j600_dump_rsv(priv, __func__, &rsv); + + if (!RSV_GETBIT(rsv.rxstat, RSV_RXOK) || + (rsv.len > MAX_FRAMELEN)) { + netif_err(priv, rx_err, dev, "RX Error %04x\n", + rsv.rxstat); + dev->stats.rx_errors++; + + if (RSV_GETBIT(rsv.rxstat, RSV_CRCERROR)) + dev->stats.rx_crc_errors++; + if (RSV_GETBIT(rsv.rxstat, RSV_LENCHECKERR)) + dev->stats.rx_frame_errors++; + if (rsv.len > MAX_FRAMELEN) + dev->stats.rx_over_errors++; + } else { + encx24j600_receive_packet(priv, &rsv); + } + + newrxtail = priv->next_packet - 2; + if (newrxtail == ENC_RX_BUF_START) + newrxtail = SRAM_SIZE - 2; + + encx24j600_cmd(priv, SETPKTDEC); + encx24j600_write_reg(priv, ERXTAIL, newrxtail); + } +} + +static irqreturn_t encx24j600_isr(int irq, void *dev_id) +{ + struct encx24j600_priv *priv = dev_id; + struct net_device *dev = priv->ndev; + int eir; + + /* Clear interrupts */ + encx24j600_cmd(priv, CLREIE); + + eir = encx24j600_read_reg(priv, EIR); + + if (eir & LINKIF) + encx24j600_int_link_handler(priv); + + if (eir & TXIF) + encx24j600_tx_complete(priv, false); + + if (eir & TXABTIF) + encx24j600_tx_complete(priv, true); + + if (eir & RXABTIF) { + if (eir & PCFULIF) { + /* Packet counter is full */ + netif_err(priv, rx_err, dev, "Packet counter full\n"); + } + dev->stats.rx_dropped++; + encx24j600_clr_bits(priv, EIR, RXABTIF); + } + + if (eir & PKTIF) { + u8 packet_count; + + mutex_lock(&priv->lock); + + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + while (packet_count) { + encx24j600_rx_packets(priv, packet_count); + packet_count = encx24j600_read_reg(priv, ESTAT) & 0xff; + } + + mutex_unlock(&priv->lock); + } + + /* Enable interrupts */ + encx24j600_cmd(priv, SETEIE); + + return IRQ_HANDLED; +} + +static int encx24j600_soft_reset(struct encx24j600_priv *priv) +{ + int ret = 0; + int timeout; + u16 eudast; + + /* Write and verify a test value to EUDAST */ + regcache_cache_bypass(priv->ctx.regmap, true); + timeout = 10; + do { + encx24j600_write_reg(priv, EUDAST, EUDAST_TEST_VAL); + eudast = encx24j600_read_reg(priv, EUDAST); + usleep_range(25, 100); + } while ((eudast != EUDAST_TEST_VAL) && --timeout); + regcache_cache_bypass(priv->ctx.regmap, false); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Wait for CLKRDY to become set */ + timeout = 10; + while (!(encx24j600_read_reg(priv, ESTAT) & CLKRDY) && --timeout) + usleep_range(25, 100); + + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err_out; + } + + /* Issue a System Reset command */ + encx24j600_cmd(priv, SETETHRST); + usleep_range(25, 100); + + /* Confirm that EUDAST has 0000h after system reset */ + if (encx24j600_read_reg(priv, EUDAST) != 0) { + ret = -EINVAL; + goto err_out; + } + + /* Wait for PHY register and status bits to become available */ + usleep_range(256, 1000); + +err_out: + return ret; +} + +static int encx24j600_hw_reset(struct encx24j600_priv *priv) +{ + int ret; + + mutex_lock(&priv->lock); + ret = encx24j600_soft_reset(priv); + mutex_unlock(&priv->lock); + + return ret; +} + +static void encx24j600_reset_hw_tx(struct encx24j600_priv *priv) +{ + encx24j600_set_bits(priv, ECON2, TXRST); + encx24j600_clr_bits(priv, ECON2, TXRST); +} + +static void encx24j600_hw_init_tx(struct encx24j600_priv *priv) +{ + /* Reset TX */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF | TXABTIF); + + /* Write the Tx Buffer pointer */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); +} + +static void encx24j600_hw_init_rx(struct encx24j600_priv *priv) +{ + encx24j600_cmd(priv, DISABLERX); + + /* Set up RX packet start address in the SRAM */ + encx24j600_write_reg(priv, ERXST, ENC_RX_BUF_START); + + /* Preload the RX Data pointer to the beginning of the RX area */ + encx24j600_write_reg(priv, ERXRDPT, ENC_RX_BUF_START); + + priv->next_packet = ENC_RX_BUF_START; + + /* Set up RX end address in the SRAM */ + encx24j600_write_reg(priv, ERXTAIL, ENC_SRAM_SIZE - 2); + + /* Reset the user data pointers */ + encx24j600_write_reg(priv, EUDAST, ENC_SRAM_SIZE); + encx24j600_write_reg(priv, EUDAND, ENC_SRAM_SIZE + 1); + + /* Set Max Frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); +} + +static void encx24j600_dump_config(struct encx24j600_priv *priv, + const char *msg) +{ + pr_info(DRV_NAME ": %s\n", msg); + + /* CHIP configuration */ + pr_info(DRV_NAME " ECON1: %04X\n", encx24j600_read_reg(priv, ECON1)); + pr_info(DRV_NAME " ECON2: %04X\n", encx24j600_read_reg(priv, ECON2)); + pr_info(DRV_NAME " ERXFCON: %04X\n", encx24j600_read_reg(priv, + ERXFCON)); + pr_info(DRV_NAME " ESTAT: %04X\n", encx24j600_read_reg(priv, ESTAT)); + pr_info(DRV_NAME " EIR: %04X\n", encx24j600_read_reg(priv, EIR)); + pr_info(DRV_NAME " EIDLED: %04X\n", encx24j600_read_reg(priv, EIDLED)); + + /* MAC layer configuration */ + pr_info(DRV_NAME " MACON1: %04X\n", encx24j600_read_reg(priv, MACON1)); + pr_info(DRV_NAME " MACON2: %04X\n", encx24j600_read_reg(priv, MACON2)); + pr_info(DRV_NAME " MAIPG: %04X\n", encx24j600_read_reg(priv, MAIPG)); + pr_info(DRV_NAME " MACLCON: %04X\n", encx24j600_read_reg(priv, + MACLCON)); + pr_info(DRV_NAME " MABBIPG: %04X\n", encx24j600_read_reg(priv, + MABBIPG)); + + /* PHY configuation */ + pr_info(DRV_NAME " PHCON1: %04X\n", encx24j600_read_phy(priv, PHCON1)); + pr_info(DRV_NAME " PHCON2: %04X\n", encx24j600_read_phy(priv, PHCON2)); + pr_info(DRV_NAME " PHANA: %04X\n", encx24j600_read_phy(priv, PHANA)); + pr_info(DRV_NAME " PHANLPA: %04X\n", encx24j600_read_phy(priv, + PHANLPA)); + pr_info(DRV_NAME " PHANE: %04X\n", encx24j600_read_phy(priv, PHANE)); + pr_info(DRV_NAME " PHSTAT1: %04X\n", encx24j600_read_phy(priv, + PHSTAT1)); + pr_info(DRV_NAME " PHSTAT2: %04X\n", encx24j600_read_phy(priv, + PHSTAT2)); + pr_info(DRV_NAME " PHSTAT3: %04X\n", encx24j600_read_phy(priv, + PHSTAT3)); +} + +static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv) +{ + switch (priv->rxfilter) { + case RXFILTER_PROMISC: + encx24j600_set_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | MCEN | NOTMEEN); + break; + case RXFILTER_MULTI: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN | MCEN); + break; + case RXFILTER_NORMAL: + default: + encx24j600_clr_bits(priv, MACON1, PASSALL); + encx24j600_write_reg(priv, ERXFCON, UCEN | CRCEN | BCEN); + break; + } +} + +static int encx24j600_hw_init(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + int ret = 0; + u16 eidled; + u16 macon2; + + priv->hw_enabled = false; + + eidled = encx24j600_read_reg(priv, EIDLED); + if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { + ret = -EINVAL; + goto err_out; + } + + netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n", + (eidled & REVID_MASK) >> REVID_SHIFT); + + /* PHY Leds: link status, + * LEDA: Link + transmit/receive events + * LEDB: Link State + colision events + */ + encx24j600_update_reg(priv, EIDLED, 0xbc00, 0xbc00); + + /* Loopback disabled */ + encx24j600_write_reg(priv, MACON1, 0x9); + + /* interpacket gap value */ + encx24j600_write_reg(priv, MAIPG, 0x0c12); + + /* Write the auto negotiation pattern */ + encx24j600_write_phy(priv, PHANA, PHANA_DEFAULT); + + encx24j600_update_phcon1(priv); + encx24j600_check_link_status(priv); + + macon2 = MACON2_RSV1 | TXCRCEN | PADCFG0 | PADCFG2 | MACON2_DEFER; + if ((priv->autoneg == AUTONEG_DISABLE) && priv->full_duplex) + macon2 |= FULDPX; + + encx24j600_set_bits(priv, MACON2, macon2); + + priv->rxfilter = RXFILTER_NORMAL; + encx24j600_set_rxfilter_mode(priv); + + /* Program the Maximum frame length */ + encx24j600_write_reg(priv, MAMXFL, MAX_FRAMELEN); + + /* Init Tx pointers */ + encx24j600_hw_init_tx(priv); + + /* Init Rx pointers */ + encx24j600_hw_init_rx(priv); + + if (netif_msg_hw(priv)) + encx24j600_dump_config(priv, "Hw is initialized"); + +err_out: + return ret; +} + +static void encx24j600_hw_enable(struct encx24j600_priv *priv) +{ + /* Clear the interrupt flags in case was set */ + encx24j600_clr_bits(priv, EIR, (PCFULIF | RXABTIF | TXABTIF | TXIF | + PKTIF | LINKIF)); + + /* Enable the interrupts */ + encx24j600_write_reg(priv, EIE, (PCFULIE | RXABTIE | TXABTIE | TXIE | + PKTIE | LINKIE | INTIE)); + + /* Enable RX */ + encx24j600_cmd(priv, ENABLERX); + + priv->hw_enabled = true; +} + +static void encx24j600_hw_disable(struct encx24j600_priv *priv) +{ + /* Disable all interrupts */ + encx24j600_write_reg(priv, EIE, 0); + + /* Disable RX */ + encx24j600_cmd(priv, DISABLERX); + + priv->hw_enabled = false; +} + +static int encx24j600_setlink(struct net_device *dev, u8 autoneg, u16 speed, + u8 duplex) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int ret = 0; + + if (!priv->hw_enabled) { + /* link is in low power mode now; duplex setting + * will take effect on next encx24j600_hw_init() + */ + if (speed == SPEED_10 || speed == SPEED_100) { + priv->autoneg = (autoneg == AUTONEG_ENABLE); + priv->full_duplex = (duplex == DUPLEX_FULL); + priv->speed = (speed == SPEED_100); + } else { + netif_warn(priv, link, dev, "unsupported link speed setting\n"); + /*speeds other than SPEED_10 and SPEED_100 */ + /*are not supported by chip */ + ret = -EOPNOTSUPP; + } + } else { + netif_warn(priv, link, dev, "Warning: hw must be disabled to set link mode\n"); + ret = -EBUSY; + } + return ret; +} + +static void encx24j600_hw_get_macaddr(struct encx24j600_priv *priv, + unsigned char *ethaddr) +{ + unsigned short val; + + val = encx24j600_read_reg(priv, MAADR1); + + ethaddr[0] = val & 0x00ff; + ethaddr[1] = (val & 0xff00) >> 8; + + val = encx24j600_read_reg(priv, MAADR2); + + ethaddr[2] = val & 0x00ffU; + ethaddr[3] = (val & 0xff00U) >> 8; + + val = encx24j600_read_reg(priv, MAADR3); + + ethaddr[4] = val & 0x00ffU; + ethaddr[5] = (val & 0xff00U) >> 8; +} + +/* Program the hardware MAC address from dev->dev_addr.*/ +static int encx24j600_set_hw_macaddr(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + if (priv->hw_enabled) { + netif_info(priv, drv, dev, "Hardware must be disabled to set Mac address\n"); + return -EBUSY; + } + + mutex_lock(&priv->lock); + + netif_info(priv, drv, dev, "%s: Setting MAC address to %pM\n", + dev->name, dev->dev_addr); + + encx24j600_write_reg(priv, MAADR3, (dev->dev_addr[4] | + dev->dev_addr[5] << 8)); + encx24j600_write_reg(priv, MAADR2, (dev->dev_addr[2] | + dev->dev_addr[3] << 8)); + encx24j600_write_reg(priv, MAADR1, (dev->dev_addr[0] | + dev->dev_addr[1] << 8)); + + mutex_unlock(&priv->lock); + + return 0; +} + +/* Store the new hardware address in dev->dev_addr, and update the MAC.*/ +static int encx24j600_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(dev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(dev->dev_addr, address->sa_data, dev->addr_len); + return encx24j600_set_hw_macaddr(dev); +} + +static int encx24j600_open(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + int ret = request_threaded_irq(priv->ctx.spi->irq, NULL, encx24j600_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + DRV_NAME, priv); + if (unlikely(ret < 0)) { + netdev_err(dev, "request irq %d failed (ret = %d)\n", + priv->ctx.spi->irq, ret); + return ret; + } + + encx24j600_hw_disable(priv); + encx24j600_hw_init(priv); + encx24j600_hw_enable(priv); + netif_start_queue(dev); + + return 0; +} + +static int encx24j600_stop(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + free_irq(priv->ctx.spi->irq, priv); + return 0; +} + +static void encx24j600_setrx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, setrx_work); + + mutex_lock(&priv->lock); + encx24j600_set_rxfilter_mode(priv); + mutex_unlock(&priv->lock); +} + +static void encx24j600_set_multicast_list(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + int oldfilter = priv->rxfilter; + + if (dev->flags & IFF_PROMISC) { + netif_dbg(priv, link, dev, "promiscuous mode\n"); + priv->rxfilter = RXFILTER_PROMISC; + } else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + netif_dbg(priv, link, dev, "%smulticast mode\n", + (dev->flags & IFF_ALLMULTI) ? "all-" : ""); + priv->rxfilter = RXFILTER_MULTI; + } else { + netif_dbg(priv, link, dev, "normal mode\n"); + priv->rxfilter = RXFILTER_NORMAL; + } + + if (oldfilter != priv->rxfilter) + queue_kthread_work(&priv->kworker, &priv->setrx_work); +} + +static void encx24j600_hw_tx(struct encx24j600_priv *priv) +{ + struct net_device *dev = priv->ndev; + netif_info(priv, tx_queued, dev, "TX Packet Len:%d\n", + priv->tx_skb->len); + + if (netif_msg_pktdata(priv)) + dump_packet("TX", priv->tx_skb->len, priv->tx_skb->data); + + if (encx24j600_read_reg(priv, EIR) & TXABTIF) + /* Last transmition aborted due to error. Reset TX interface */ + encx24j600_reset_hw_tx(priv); + + /* Clear the TXIF flag if were previously set */ + encx24j600_clr_bits(priv, EIR, TXIF); + + /* Set the data pointer to the TX buffer address in the SRAM */ + encx24j600_write_reg(priv, EGPWRPT, ENC_TX_BUF_START); + + /* Copy the packet into the SRAM */ + encx24j600_raw_write(priv, WGPDATA, (u8 *)priv->tx_skb->data, + priv->tx_skb->len); + + /* Program the Tx buffer start pointer */ + encx24j600_write_reg(priv, ETXST, ENC_TX_BUF_START); + + /* Program the packet length */ + encx24j600_write_reg(priv, ETXLEN, priv->tx_skb->len); + + /* Start the transmission */ + encx24j600_cmd(priv, SETTXRTS); +} + +static void encx24j600_tx_proc(struct kthread_work *ws) +{ + struct encx24j600_priv *priv = + container_of(ws, struct encx24j600_priv, tx_work); + + mutex_lock(&priv->lock); + encx24j600_hw_tx(priv); + mutex_unlock(&priv->lock); +} + +static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + /* save the timestamp */ + dev->trans_start = jiffies; + + /* Remember the skb for deferred processing */ + priv->tx_skb = skb; + + queue_kthread_work(&priv->kworker, &priv->tx_work); + + return NETDEV_TX_OK; +} + +/* Deal with a transmit timeout */ +static void encx24j600_tx_timeout(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n", + jiffies, jiffies - dev->trans_start); + + dev->stats.tx_errors++; + netif_wake_queue(dev); + return; +} + +static int encx24j600_get_regs_len(struct net_device *dev) +{ + return SFR_REG_COUNT; +} + +static void encx24j600_get_regs(struct net_device *dev, + struct ethtool_regs *regs, void *p) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + u16 *buff = p; + u8 reg; + + regs->version = 1; + mutex_lock(&priv->lock); + for (reg = 0; reg < SFR_REG_COUNT; reg += 2) { + unsigned int val = 0; + /* ignore errors for unreadable registers */ + regmap_read(priv->ctx.regmap, reg, &val); + buff[reg] = val & 0xffff; + } + mutex_unlock(&priv->lock); +} + +static void encx24j600_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, dev_name(dev->dev.parent), + sizeof(info->bus_info)); +} + +static int encx24j600_get_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + + cmd->transceiver = XCVR_INTERNAL; + cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_TP; + + ethtool_cmd_speed_set(cmd, priv->speed); + cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + cmd->port = PORT_TP; + cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + + return 0; +} + +static int encx24j600_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + return encx24j600_setlink(dev, cmd->autoneg, + ethtool_cmd_speed(cmd), cmd->duplex); +} + +static u32 encx24j600_get_msglevel(struct net_device *dev) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void encx24j600_set_msglevel(struct net_device *dev, u32 val) +{ + struct encx24j600_priv *priv = netdev_priv(dev); + priv->msg_enable = val; +} + +static const struct ethtool_ops encx24j600_ethtool_ops = { + .get_settings = encx24j600_get_settings, + .set_settings = encx24j600_set_settings, + .get_drvinfo = encx24j600_get_drvinfo, + .get_msglevel = encx24j600_get_msglevel, + .set_msglevel = encx24j600_set_msglevel, + .get_regs_len = encx24j600_get_regs_len, + .get_regs = encx24j600_get_regs, +}; + +static const struct net_device_ops encx24j600_netdev_ops = { + .ndo_open = encx24j600_open, + .ndo_stop = encx24j600_stop, + .ndo_start_xmit = encx24j600_tx, + .ndo_set_rx_mode = encx24j600_set_multicast_list, + .ndo_set_mac_address = encx24j600_set_mac_address, + .ndo_tx_timeout = encx24j600_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int encx24j600_spi_probe(struct spi_device *spi) +{ + int ret; + + struct net_device *ndev; + struct encx24j600_priv *priv; + + ndev = alloc_etherdev(sizeof(struct encx24j600_priv)); + + if (!ndev) { + ret = -ENOMEM; + goto error_out; + } + + priv = netdev_priv(ndev); + spi_set_drvdata(spi, priv); + dev_set_drvdata(&spi->dev, priv); + SET_NETDEV_DEV(ndev, &spi->dev); + + priv->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); + priv->ndev = ndev; + + /* Default configuration PHY configuration */ + priv->full_duplex = true; + priv->autoneg = AUTONEG_ENABLE; + priv->speed = SPEED_100; + + priv->ctx.spi = spi; + devm_regmap_init_encx24j600(&spi->dev, &priv->ctx); + ndev->irq = spi->irq; + ndev->netdev_ops = &encx24j600_netdev_ops; + + mutex_init(&priv->lock); + + /* Reset device and check if it is connected */ + if (encx24j600_hw_reset(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": Chip is not detected\n"); + ret = -EIO; + goto out_free; + } + + /* Initialize the device HW to the consistent state */ + if (encx24j600_hw_init(priv)) { + netif_err(priv, probe, ndev, + DRV_NAME ": HW initialization error\n"); + ret = -EIO; + goto out_free; + } + + init_kthread_worker(&priv->kworker); + init_kthread_work(&priv->tx_work, encx24j600_tx_proc); + init_kthread_work(&priv->setrx_work, encx24j600_setrx_proc); + + priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker, + "encx24j600"); + + if (IS_ERR(priv->kworker_task)) { + ret = PTR_ERR(priv->kworker_task); + goto out_free; + } + + /* Get the MAC address from the chip */ + encx24j600_hw_get_macaddr(priv, ndev->dev_addr); + + ndev->ethtool_ops = &encx24j600_ethtool_ops; + + ret = register_netdev(ndev); + if (unlikely(ret)) { + netif_err(priv, probe, ndev, "Error %d initializing card encx24j600 card\n", + ret); + goto out_free; + } + + netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr); + + return ret; + +out_free: + free_netdev(ndev); + +error_out: + return ret; +} + +static int encx24j600_spi_remove(struct spi_device *spi) +{ + struct encx24j600_priv *priv = dev_get_drvdata(&spi->dev); + + unregister_netdev(priv->ndev); + + free_netdev(priv->ndev); + + return 0; +} + +static const struct spi_device_id encx24j600_spi_id_table = { + .name = "encx24j600" +}; + +static struct spi_driver encx24j600_spi_net_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .bus = &spi_bus_type, + }, + .probe = encx24j600_spi_probe, + .remove = encx24j600_spi_remove, + .id_table = &encx24j600_spi_id_table, +}; + +static int __init encx24j600_init(void) +{ + return spi_register_driver(&encx24j600_spi_net_driver); +} +module_init(encx24j600_init); + +static void encx24j600_exit(void) +{ + spi_unregister_driver(&encx24j600_spi_net_driver); +} +module_exit(encx24j600_exit); + +MODULE_DESCRIPTION(DRV_NAME " ethernet driver"); +MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:" DRV_NAME); diff --git a/drivers/net/ethernet/microchip/encx24j600_hw.h b/drivers/net/ethernet/microchip/encx24j600_hw.h new file mode 100644 index 000000000000..4be73d5553f8 --- /dev/null +++ b/drivers/net/ethernet/microchip/encx24j600_hw.h @@ -0,0 +1,437 @@ +/** + * encx24j600_hw.h: Register definitions + * + */ + +#ifndef _ENCX24J600_HW_H +#define _ENCX24J600_HW_H + +struct encx24j600_context { + struct spi_device *spi; + struct regmap *regmap; + struct regmap *phymap; + struct mutex mutex; /* mutex to protect access to regmap */ + int bank; +}; + +void devm_regmap_init_encx24j600(struct device *dev, + struct encx24j600_context *ctx); + +/* Single-byte instructions */ +#define BANK_SELECT(bank) (0xC0 | ((bank & (BANK_MASK >> BANK_SHIFT)) << 1)) +#define B0SEL 0xC0 /* Bank 0 Select */ +#define B1SEL 0xC2 /* Bank 1 Select */ +#define B2SEL 0xC4 /* Bank 2 Select */ +#define B3SEL 0xC6 /* Bank 3 Select */ +#define SETETHRST 0xCA /* System Reset */ +#define FCDISABLE 0xE0 /* Flow Control Disable */ +#define FCSINGLE 0xE2 /* Flow Control Single */ +#define FCMULTIPLE 0xE4 /* Flow Control Multiple */ +#define FCCLEAR 0xE6 /* Flow Control Clear */ +#define SETPKTDEC 0xCC /* Decrement Packet Counter */ +#define DMASTOP 0xD2 /* DMA Stop */ +#define DMACKSUM 0xD8 /* DMA Start Checksum */ +#define DMACKSUMS 0xDA /* DMA Start Checksum with Seed */ +#define DMACOPY 0xDC /* DMA Start Copy */ +#define DMACOPYS 0xDE /* DMA Start Copy and Checksum with Seed */ +#define SETTXRTS 0xD4 /* Request Packet Transmission */ +#define ENABLERX 0xE8 /* Enable RX */ +#define DISABLERX 0xEA /* Disable RX */ +#define SETEIE 0xEC /* Enable Interrupts */ +#define CLREIE 0xEE /* Disable Interrupts */ + +/* Two byte instructions */ +#define RBSEL 0xC8 /* Read Bank Select */ + +/* Three byte instructions */ +#define WGPRDPT 0x60 /* Write EGPRDPT */ +#define RGPRDPT 0x62 /* Read EGPRDPT */ +#define WRXRDPT 0x64 /* Write ERXRDPT */ +#define RRXRDPT 0x66 /* Read ERXRDPT */ +#define WUDARDPT 0x68 /* Write EUDARDPT */ +#define RUDARDPT 0x6A /* Read EUDARDPT */ +#define WGPWRPT 0x6C /* Write EGPWRPT */ +#define RGPWRPT 0x6E /* Read EGPWRPT */ +#define WRXWRPT 0x70 /* Write ERXWRPT */ +#define RRXWRPT 0x72 /* Read ERXWRPT */ +#define WUDAWRPT 0x74 /* Write EUDAWRPT */ +#define RUDAWRPT 0x76 /* Read EUDAWRPT */ + +/* n byte instructions */ +#define RCRCODE 0x00 +#define WCRCODE 0x40 +#define BFSCODE 0x80 +#define BFCCODE 0xA0 +#define RCR(addr) (RCRCODE | (addr & ADDR_MASK)) /* Read Control Register */ +#define WCR(addr) (WCRCODE | (addr & ADDR_MASK)) /* Write Control Register */ +#define RCRU 0x20 /* Read Control Register Unbanked */ +#define WCRU 0x22 /* Write Control Register Unbanked */ +#define BFS(addr) (BFSCODE | (addr & ADDR_MASK)) /* Bit Field Set */ +#define BFC(addr) (BFCCODE | (addr & ADDR_MASK)) /* Bit Field Clear */ +#define BFSU 0x24 /* Bit Field Set Unbanked */ +#define BFCU 0x26 /* Bit Field Clear Unbanked */ +#define RGPDATA 0x28 /* Read EGPDATA */ +#define WGPDATA 0x2A /* Write EGPDATA */ +#define RRXDATA 0x2C /* Read ERXDATA */ +#define WRXDATA 0x2E /* Write ERXDATA */ +#define RUDADATA 0x30 /* Read EUDADATA */ +#define WUDADATA 0x32 /* Write EUDADATA */ + +#define SFR_REG_COUNT 0xA0 + +/* ENC424J600 Control Registers + * Control register definitions are a combination of address + * and bank number + * - Register address (bits 0-4) + * - Bank number (bits 5-6) + */ +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define BANK_SHIFT 5 + +/* All-bank registers */ +#define EUDAST 0x16 +#define EUDAND 0x18 +#define ESTAT 0x1A +#define EIR 0x1C +#define ECON1 0x1E + +/* Bank 0 registers */ +#define ETXST (0x00 | 0x00) +#define ETXLEN (0x02 | 0x00) +#define ERXST (0x04 | 0x00) +#define ERXTAIL (0x06 | 0x00) +#define ERXHEAD (0x08 | 0x00) +#define EDMAST (0x0A | 0x00) +#define EDMALEN (0x0C | 0x00) +#define EDMADST (0x0E | 0x00) +#define EDMACS (0x10 | 0x00) +#define ETXSTAT (0x12 | 0x00) +#define ETXWIRE (0x14 | 0x00) + +/* Bank 1 registers */ +#define EHT1 (0x00 | 0x20) +#define EHT2 (0x02 | 0x20) +#define EHT3 (0x04 | 0x20) +#define EHT4 (0x06 | 0x20) +#define EPMM1 (0x08 | 0x20) +#define EPMM2 (0x0A | 0x20) +#define EPMM3 (0x0C | 0x20) +#define EPMM4 (0x0E | 0x20) +#define EPMCS (0x10 | 0x20) +#define EPMO (0x12 | 0x20) +#define ERXFCON (0x14 | 0x20) + +/* Bank 2 registers */ +#define MACON1 (0x00 | 0x40) +#define MACON2 (0x02 | 0x40) +#define MABBIPG (0x04 | 0x40) +#define MAIPG (0x06 | 0x40) +#define MACLCON (0x08 | 0x40) +#define MAMXFL (0x0A | 0x40) +#define MICMD (0x12 | 0x40) +#define MIREGADR (0x14 | 0x40) + +/* Bank 3 registers */ +#define MAADR3 (0x00 | 0x60) +#define MAADR2 (0x02 | 0x60) +#define MAADR1 (0x04 | 0x60) +#define MIWR (0x06 | 0x60) +#define MIRD (0x08 | 0x60) +#define MISTAT (0x0A | 0x60) +#define EPAUS (0x0C | 0x60) +#define ECON2 (0x0E | 0x60) +#define ERXWM (0x10 | 0x60) +#define EIE (0x12 | 0x60) +#define EIDLED (0x14 | 0x60) + +/* Unbanked registers */ +#define EGPDATA (0x00 | 0x80) +#define ERXDATA (0x02 | 0x80) +#define EUDADATA (0x04 | 0x80) +#define EGPRDPT (0x06 | 0x80) +#define EGPWRPT (0x08 | 0x80) +#define ERXRDPT (0x0A | 0x80) +#define ERXWRPT (0x0C | 0x80) +#define EUDARDPT (0x0E | 0x80) +#define EUDAWRPT (0x10 | 0x80) + + +/* Register bit definitions */ +/* ESTAT */ +#define INT (1 << 15) +#define FCIDLE (1 << 14) +#define RXBUSY (1 << 13) +#define CLKRDY (1 << 12) +#define PHYDPX (1 << 10) +#define PHYLNK (1 << 8) + +/* EIR */ +#define CRYPTEN (1 << 15) +#define MODEXIF (1 << 14) +#define HASHIF (1 << 13) +#define AESIF (1 << 12) +#define LINKIF (1 << 11) +#define PKTIF (1 << 6) +#define DMAIF (1 << 5) +#define TXIF (1 << 3) +#define TXABTIF (1 << 2) +#define RXABTIF (1 << 1) +#define PCFULIF (1 << 0) + +/* ECON1 */ +#define MODEXST (1 << 15) +#define HASHEN (1 << 14) +#define HASHOP (1 << 13) +#define HASHLST (1 << 12) +#define AESST (1 << 11) +#define AESOP1 (1 << 10) +#define AESOP0 (1 << 9) +#define PKTDEC (1 << 8) +#define FCOP1 (1 << 7) +#define FCOP0 (1 << 6) +#define DMAST (1 << 5) +#define DMACPY (1 << 4) +#define DMACSSD (1 << 3) +#define DMANOCS (1 << 2) +#define TXRTS (1 << 1) +#define RXEN (1 << 0) + +/* ETXSTAT */ +#define LATECOL (1 << 10) +#define MAXCOL (1 << 9) +#define EXDEFER (1 << 8) +#define ETXSTATL_DEFER (1 << 7) +#define CRCBAD (1 << 4) +#define COLCNT_MASK 0xF + +/* ERXFCON */ +#define HTEN (1 << 15) +#define MPEN (1 << 14) +#define NOTPM (1 << 12) +#define PMEN3 (1 << 11) +#define PMEN2 (1 << 10) +#define PMEN1 (1 << 9) +#define PMEN0 (1 << 8) +#define CRCEEN (1 << 7) +#define CRCEN (1 << 6) +#define RUNTEEN (1 << 5) +#define RUNTEN (1 << 4) +#define UCEN (1 << 3) +#define NOTMEEN (1 << 2) +#define MCEN (1 << 1) +#define BCEN (1 << 0) + +/* MACON1 */ +#define LOOPBK (1 << 4) +#define RXPAUS (1 << 2) +#define PASSALL (1 << 1) + +/* MACON2 */ +#define MACON2_DEFER (1 << 14) +#define BPEN (1 << 13) +#define NOBKOFF (1 << 12) +#define PADCFG2 (1 << 7) +#define PADCFG1 (1 << 6) +#define PADCFG0 (1 << 5) +#define TXCRCEN (1 << 4) +#define PHDREN (1 << 3) +#define HFRMEN (1 << 2) +#define MACON2_RSV1 (1 << 1) +#define FULDPX (1 << 0) + +/* MAIPG */ +/* value of the high byte is given by the reserved bits, + * value of the low byte is recomended setting of the + * IPG parameter. + */ +#define MAIPGH_VAL 0x0C +#define MAIPGL_VAL 0x12 + +/* MIREGADRH */ +#define MIREGADR_VAL (1 << 8) + +/* MIREGADRL */ +#define PHREG_MASK 0x1F + +/* MICMD */ +#define MIISCAN (1 << 1) +#define MIIRD (1 << 0) + +/* MISTAT */ +#define NVALID (1 << 2) +#define SCAN (1 << 1) +#define BUSY (1 << 0) + +/* ECON2 */ +#define ETHEN (1 << 15) +#define STRCH (1 << 14) +#define TXMAC (1 << 13) +#define SHA1MD5 (1 << 12) +#define COCON3 (1 << 11) +#define COCON2 (1 << 10) +#define COCON1 (1 << 9) +#define COCON0 (1 << 8) +#define AUTOFC (1 << 7) +#define TXRST (1 << 6) +#define RXRST (1 << 5) +#define ETHRST (1 << 4) +#define MODLEN1 (1 << 3) +#define MODLEN0 (1 << 2) +#define AESLEN1 (1 << 1) +#define AESLEN0 (1 << 0) + +/* EIE */ +#define INTIE (1 << 15) +#define MODEXIE (1 << 14) +#define HASHIE (1 << 13) +#define AESIE (1 << 12) +#define LINKIE (1 << 11) +#define PKTIE (1 << 6) +#define DMAIE (1 << 5) +#define TXIE (1 << 3) +#define TXABTIE (1 << 2) +#define RXABTIE (1 << 1) +#define PCFULIE (1 << 0) + +/* EIDLED */ +#define LACFG3 (1 << 15) +#define LACFG2 (1 << 14) +#define LACFG1 (1 << 13) +#define LACFG0 (1 << 12) +#define LBCFG3 (1 << 11) +#define LBCFG2 (1 << 10) +#define LBCFG1 (1 << 9) +#define LBCFG0 (1 << 8) +#define DEVID_SHIFT 5 +#define DEVID_MASK (0x7 << DEVID_SHIFT) +#define REVID_SHIFT 0 +#define REVID_MASK (0x1F << REVID_SHIFT) + +/* PHY registers */ +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHANA 0x04 +#define PHANLPA 0x05 +#define PHANE 0x06 +#define PHCON2 0x11 +#define PHSTAT2 0x1B +#define PHSTAT3 0x1F + +/* PHCON1 */ +#define PRST (1 << 15) +#define PLOOPBK (1 << 14) +#define SPD100 (1 << 13) +#define ANEN (1 << 12) +#define PSLEEP (1 << 11) +#define RENEG (1 << 9) +#define PFULDPX (1 << 8) + +/* PHSTAT1 */ +#define FULL100 (1 << 14) +#define HALF100 (1 << 13) +#define FULL10 (1 << 12) +#define HALF10 (1 << 11) +#define ANDONE (1 << 5) +#define LRFAULT (1 << 4) +#define ANABLE (1 << 3) +#define LLSTAT (1 << 2) +#define EXTREGS (1 << 0) + +/* PHSTAT2 */ +#define PLRITY (1 << 4) + +/* PHSTAT3 */ +#define PHY3SPD100 (1 << 3) +#define PHY3DPX (1 << 4) +#define SPDDPX_SHIFT 2 +#define SPDDPX_MASK (0x7 << SPDDPX_SHIFT) + +/* PHANA */ +/* Default value for PHY initialization*/ +#define PHANA_DEFAULT 0x05E1 + +/* PHANE */ +#define PDFLT (1 << 4) +#define LPARCD (1 << 1) +#define LPANABL (1 << 0) + +#define EUDAST_TEST_VAL 0x1234 + +#define TSV_SIZE 7 + +#define ENCX24J600_DEV_ID 0x1 + +/* Configuration */ + +/* Led is on when the link is present and driven low + * temporarily when packet is TX'd or RX'd + */ +#define LED_A_SETTINGS 0xC + +/* Led is on if the link is in 100 Mbps mode */ +#define LED_B_SETTINGS 0x8 + +/* maximum ethernet frame length + * Currently not used as a limit anywhere + * (we're using the "huge frame enable" feature of + * enc424j600). + */ +#define MAX_FRAMELEN 1518 + +/* Size in bytes of the receive buffer in enc424j600. + * Must be word aligned (even). + */ +#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN) + +/* Start of the general purpose area in sram */ +#define SRAM_GP_START 0x0 + +/* SRAM size */ +#define SRAM_SIZE 0x6000 + +/* Start of the receive buffer */ +#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE) + +#define RSV_RXLONGEVDROPEV 16 +#define RSV_CARRIEREV 18 +#define RSV_CRCERROR 20 +#define RSV_LENCHECKERR 21 +#define RSV_LENOUTOFRANGE 22 +#define RSV_RXOK 23 +#define RSV_RXMULTICAST 24 +#define RSV_RXBROADCAST 25 +#define RSV_DRIBBLENIBBLE 26 +#define RSV_RXCONTROLFRAME 27 +#define RSV_RXPAUSEFRAME 28 +#define RSV_RXUNKNOWNOPCODE 29 +#define RSV_RXTYPEVLAN 30 + +#define RSV_RUNTFILTERMATCH 31 +#define RSV_NOTMEFILTERMATCH 32 +#define RSV_HASHFILTERMATCH 33 +#define RSV_MAGICPKTFILTERMATCH 34 +#define RSV_PTRNMTCHFILTERMATCH 35 +#define RSV_UNICASTFILTERMATCH 36 + +#define RSV_SIZE 8 +#define RSV_BITMASK(x) (1 << ((x) - 16)) +#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0) + +struct rsv { + u16 next_packet; + u16 len; + u32 rxstat; +}; + +/* Put RX buffer at 0 as suggested by the Errata datasheet */ + +#define RXSTART_INIT ERXST_VAL +#define RXEND_INIT 0x5FFF + +int regmap_encx24j600_spi_write(void *context, u8 reg, const u8 *data, + size_t count); +int regmap_encx24j600_spi_read(void *context, u8 reg, u8 *data, size_t count); + + +#endif diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index 2d1b94274079..9ba975853ec6 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -5389,8 +5389,6 @@ static void s2io_ethtool_gdrvinfo(struct net_device *dev, strlcpy(info->driver, s2io_driver_name, sizeof(info->driver)); strlcpy(info->version, s2io_driver_version, sizeof(info->version)); strlcpy(info->bus_info, pci_name(sp->pdev), sizeof(info->bus_info)); - info->regdump_len = XENA_REG_SPACE; - info->eedump_len = XENA_EEPROM_SPACE; } /** diff --git a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c index be916eb2f2e7..9a2967016c18 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-ethtool.c @@ -105,10 +105,6 @@ static void vxge_ethtool_gdrvinfo(struct net_device *dev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->fw_version, vdev->fw_version, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(vdev->pdev), sizeof(info->bus_info)); - info->regdump_len = sizeof(struct vxge_hw_vpath_reg) - * vdev->no_of_vpath; - - info->n_stats = STAT_LEN; } /** diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index 7bf9c028d8d7..c177c7cec13b 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -1344,10 +1344,6 @@ static void octeon_mgmt_get_drvinfo(struct net_device *netdev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); strlcpy(info->bus_info, "N/A", sizeof(info->bus_info)); - info->n_stats = 0; - info->testinfo_len = 0; - info->regdump_len = 0; - info->eedump_len = 0; } static int octeon_mgmt_get_settings(struct net_device *netdev, diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index f6fcf7450352..b19be7c6c1f4 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -164,7 +164,6 @@ static void pch_gbe_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->version, pch_driver_version, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = pch_gbe_get_regs_len(netdev); } /** diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c index 87e073c6e291..f9034467736c 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c @@ -93,8 +93,6 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = NETXEN_NIC_REGS_LEN; - drvinfo->eedump_len = netxen_nic_get_eeprom_len(dev); } static int diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 4847713211ca..b09a6b80d107 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -1736,8 +1736,6 @@ static void ql_get_drvinfo(struct net_device *ndev, sizeof(drvinfo->version)); strlcpy(drvinfo->bus_info, pci_name(qdev->pdev), sizeof(drvinfo->bus_info)); - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } static u32 ql_get_msglevel(struct net_device *ndev) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index d6696cfa11d2..46bbea8e023c 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1092,7 +1092,7 @@ struct qlcnic_filter_hash { struct qlcnic_mailbox { struct workqueue_struct *work_q; struct qlcnic_adapter *adapter; - struct qlcnic_mbx_ops *ops; + const struct qlcnic_mbx_ops *ops; struct work_struct work; struct completion completion; struct list_head cmd_q; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 9f0bdd993955..37a731be7d39 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -4048,7 +4048,7 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work) struct qlcnic_mailbox *mbx = container_of(work, struct qlcnic_mailbox, work); struct qlcnic_adapter *adapter = mbx->adapter; - struct qlcnic_mbx_ops *mbx_ops = mbx->ops; + const struct qlcnic_mbx_ops *mbx_ops = mbx->ops; struct device *dev = &adapter->pdev->dev; atomic_t *rsp_status = &mbx->rsp_status; struct list_head *head = &mbx->cmd_q; @@ -4098,7 +4098,7 @@ static void qlcnic_83xx_mailbox_worker(struct work_struct *work) } } -static struct qlcnic_mbx_ops qlcnic_83xx_mbx_ops = { +static const struct qlcnic_mbx_ops qlcnic_83xx_mbx_ops = { .enqueue_cmd = qlcnic_83xx_enqueue_mbx_cmd, .dequeue_cmd = qlcnic_83xx_dequeue_mbx_cmd, .decode_resp = qlcnic_83xx_decode_mbx_rsp, diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c index c3c514e332b5..5dade1fd08b8 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_ethtool.c @@ -415,13 +415,6 @@ static void ql_get_drvinfo(struct net_device *ndev, (qdev->fw_rev_id & 0x000000ff)); strlcpy(drvinfo->bus_info, pci_name(qdev->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = 0; - drvinfo->testinfo_len = 0; - if (!test_bit(QL_FRC_COREDUMP, &qdev->flags)) - drvinfo->regdump_len = sizeof(struct ql_mpi_coredump); - else - drvinfo->regdump_len = sizeof(struct ql_reg_dump); - drvinfo->eedump_len = 0; } static void ql_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 686334f4588d..deae10d7426d 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -175,7 +175,7 @@ enum { LastFrag = (1 << 28), /* Final segment of a packet */ LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ MSSShift = 16, /* MSS value position */ - MSSMask = 0xfff, /* MSS value: 11 bits */ + MSSMask = 0x7ff, /* MSS value: 11 bits */ TxError = (1 << 23), /* Tx error summary */ RxError = (1 << 20), /* Rx error summary */ IPCS = (1 << 18), /* Calculate IP checksum */ @@ -754,10 +754,16 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; mss = skb_shinfo(skb)->gso_size; + if (mss > MSSMask) { + WARN_ONCE(1, "Net bug: GSO size %d too large for 8139CP\n", + mss); + goto out_dma_error; + } + opts2 = cpu_to_le32(cp_tx_vlan_tag(skb)); opts1 = DescOwn; if (mss) - opts1 |= LargeSend | ((mss & MSSMask) << MSSShift); + opts1 |= LargeSend | (mss << MSSShift); else if (skb->ip_summed == CHECKSUM_PARTIAL) { const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) @@ -1852,6 +1858,15 @@ static void cp_set_d3_state (struct cp_private *cp) pci_set_power_state (cp->pdev, PCI_D3hot); } +static netdev_features_t cp_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + if (skb_shinfo(skb)->gso_size > MSSMask) + features &= ~NETIF_F_TSO; + + return vlan_features_check(skb, features); +} static const struct net_device_ops cp_netdev_ops = { .ndo_open = cp_open, .ndo_stop = cp_close, @@ -1864,6 +1879,7 @@ static const struct net_device_ops cp_netdev_ops = { .ndo_tx_timeout = cp_tx_timeout, .ndo_set_features = cp_set_features, .ndo_change_mtu = cp_change_mtu, + .ndo_features_check = cp_features_check, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = cp_poll_controller, @@ -1983,12 +1999,12 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) dev->ethtool_ops = &cp_ethtool_ops; dev->watchdog_timeo = TX_TIMEOUT; - dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; - /* disabled by default until verified */ dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 78bb4ceb1cdd..ef668d300800 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -2388,7 +2388,6 @@ static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo * strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); - info->regdump_len = tp->regs_len; } static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index a157aaaaff6a..0623fff932e4 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -766,6 +766,11 @@ struct ravb_ptp { struct ravb_ptp_perout perout[N_PER_OUT]; }; +enum ravb_chip_id { + RCAR_GEN2, + RCAR_GEN3, +}; + struct ravb_private { struct net_device *ndev; struct platform_device *pdev; @@ -806,6 +811,8 @@ struct ravb_private { int msg_enable; int speed; int duplex; + int emac_irq; + enum ravb_chip_id chip_id; unsigned no_avb_link:1; unsigned avb_link_active_low:1; diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 450899e9cea2..8cc5ec5ed19a 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -201,7 +201,7 @@ static void ravb_ring_free(struct net_device *ndev, int q) if (priv->rx_ring[q]) { ring_size = sizeof(struct ravb_ex_rx_desc) * (priv->num_rx_ring[q] + 1); - dma_free_coherent(NULL, ring_size, priv->rx_ring[q], + dma_free_coherent(ndev->dev.parent, ring_size, priv->rx_ring[q], priv->rx_desc_dma[q]); priv->rx_ring[q] = NULL; } @@ -209,7 +209,7 @@ static void ravb_ring_free(struct net_device *ndev, int q) if (priv->tx_ring[q]) { ring_size = sizeof(struct ravb_tx_desc) * (priv->num_tx_ring[q] * NUM_TX_DESC + 1); - dma_free_coherent(NULL, ring_size, priv->tx_ring[q], + dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q], priv->tx_desc_dma[q]); priv->tx_ring[q] = NULL; } @@ -240,13 +240,13 @@ static void ravb_ring_format(struct net_device *ndev, int q) rx_desc = &priv->rx_ring[q][i]; /* The size of the buffer should be on 16-byte boundary. */ rx_desc->ds_cc = cpu_to_le16(ALIGN(PKT_BUF_SZ, 16)); - dma_addr = dma_map_single(&ndev->dev, priv->rx_skb[q][i]->data, + dma_addr = dma_map_single(ndev->dev.parent, priv->rx_skb[q][i]->data, ALIGN(PKT_BUF_SZ, 16), DMA_FROM_DEVICE); /* We just set the data size to 0 for a failed mapping which * should prevent DMA from happening... */ - if (dma_mapping_error(&ndev->dev, dma_addr)) + if (dma_mapping_error(ndev->dev.parent, dma_addr)) rx_desc->ds_cc = cpu_to_le16(0); rx_desc->dptr = cpu_to_le32(dma_addr); rx_desc->die_dt = DT_FEMPTY; @@ -309,7 +309,7 @@ static int ravb_ring_init(struct net_device *ndev, int q) /* Allocate all RX descriptors. */ ring_size = sizeof(struct ravb_ex_rx_desc) * (priv->num_rx_ring[q] + 1); - priv->rx_ring[q] = dma_alloc_coherent(NULL, ring_size, + priv->rx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size, &priv->rx_desc_dma[q], GFP_KERNEL); if (!priv->rx_ring[q]) @@ -320,7 +320,7 @@ static int ravb_ring_init(struct net_device *ndev, int q) /* Allocate all TX descriptors. */ ring_size = sizeof(struct ravb_tx_desc) * (priv->num_tx_ring[q] * NUM_TX_DESC + 1); - priv->tx_ring[q] = dma_alloc_coherent(NULL, ring_size, + priv->tx_ring[q] = dma_alloc_coherent(ndev->dev.parent, ring_size, &priv->tx_desc_dma[q], GFP_KERNEL); if (!priv->tx_ring[q]) @@ -443,7 +443,7 @@ static int ravb_tx_free(struct net_device *ndev, int q) size = le16_to_cpu(desc->ds_tagl) & TX_DS; /* Free the original skb. */ if (priv->tx_skb[q][entry / NUM_TX_DESC]) { - dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr), + dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), size, DMA_TO_DEVICE); /* Last packet descriptor? */ if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) { @@ -546,7 +546,7 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q) skb = priv->rx_skb[q][entry]; priv->rx_skb[q][entry] = NULL; - dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr), + dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), ALIGN(PKT_BUF_SZ, 16), DMA_FROM_DEVICE); get_ts &= (q == RAVB_NC) ? @@ -586,14 +586,14 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q) if (!skb) break; /* Better luck next round. */ ravb_set_buffer_align(skb); - dma_addr = dma_map_single(&ndev->dev, skb->data, + dma_addr = dma_map_single(ndev->dev.parent, skb->data, le16_to_cpu(desc->ds_cc), DMA_FROM_DEVICE); skb_checksum_none_assert(skb); /* We just set the data size to 0 for a failed mapping * which should prevent DMA from happening... */ - if (dma_mapping_error(&ndev->dev, dma_addr)) + if (dma_mapping_error(ndev->dev.parent, dma_addr)) desc->ds_cc = cpu_to_le16(0); desc->dptr = cpu_to_le32(dma_addr); priv->rx_skb[q][entry] = skb; @@ -889,6 +889,22 @@ static int ravb_phy_init(struct net_device *ndev) return -ENOENT; } + /* This driver only support 10/100Mbit speeds on Gen3 + * at this time. + */ + if (priv->chip_id == RCAR_GEN3) { + int err; + + err = phy_set_max_speed(phydev, SPEED_100); + if (err) { + netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n"); + phy_disconnect(phydev); + return err; + } + + netdev_info(ndev, "limited PHY to 100Mbit/s\n"); + } + netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n", phydev->addr, phydev->irq, phydev->drv->name); @@ -1197,6 +1213,15 @@ static int ravb_open(struct net_device *ndev) goto out_napi_off; } + if (priv->chip_id == RCAR_GEN3) { + error = request_irq(priv->emac_irq, ravb_interrupt, + IRQF_SHARED, ndev->name, ndev); + if (error) { + netdev_err(ndev, "cannot request IRQ\n"); + goto out_free_irq; + } + } + /* Device init */ error = ravb_dmac_init(ndev); if (error) @@ -1220,6 +1245,7 @@ out_ptp_stop: ravb_ptp_stop(ndev); out_free_irq: free_irq(ndev->irq, ndev); + free_irq(priv->emac_irq, ndev); out_napi_off: napi_disable(&priv->napi[RAVB_NC]); napi_disable(&priv->napi[RAVB_BE]); @@ -1300,8 +1326,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) entry / NUM_TX_DESC * DPTR_ALIGN; len = PTR_ALIGN(skb->data, DPTR_ALIGN) - skb->data; memcpy(buffer, skb->data, len); - dma_addr = dma_map_single(&ndev->dev, buffer, len, DMA_TO_DEVICE); - if (dma_mapping_error(&ndev->dev, dma_addr)) + dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE); + if (dma_mapping_error(ndev->dev.parent, dma_addr)) goto drop; desc = &priv->tx_ring[q][entry]; @@ -1310,8 +1336,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) buffer = skb->data + len; len = skb->len - len; - dma_addr = dma_map_single(&ndev->dev, buffer, len, DMA_TO_DEVICE); - if (dma_mapping_error(&ndev->dev, dma_addr)) + dma_addr = dma_map_single(ndev->dev.parent, buffer, len, DMA_TO_DEVICE); + if (dma_mapping_error(ndev->dev.parent, dma_addr)) goto unmap; desc++; @@ -1323,7 +1349,7 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) ts_skb = kmalloc(sizeof(*ts_skb), GFP_ATOMIC); if (!ts_skb) { desc--; - dma_unmap_single(&ndev->dev, dma_addr, len, + dma_unmap_single(ndev->dev.parent, dma_addr, len, DMA_TO_DEVICE); goto unmap; } @@ -1358,7 +1384,7 @@ exit: return NETDEV_TX_OK; unmap: - dma_unmap_single(&ndev->dev, le32_to_cpu(desc->dptr), + dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), le16_to_cpu(desc->ds_tagl), DMA_TO_DEVICE); drop: dev_kfree_skb_any(skb); @@ -1625,10 +1651,20 @@ static int ravb_mdio_release(struct ravb_private *priv) return 0; } +static const struct of_device_id ravb_match_table[] = { + { .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 }, + { } +}; +MODULE_DEVICE_TABLE(of, ravb_match_table); + static int ravb_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; struct ravb_private *priv; + enum ravb_chip_id chip_id; struct net_device *ndev; int error, irq, q; struct resource *res; @@ -1657,7 +1693,14 @@ static int ravb_probe(struct platform_device *pdev) /* The Ether-specific entries in the device structure. */ ndev->base_addr = res->start; ndev->dma = -1; - irq = platform_get_irq(pdev, 0); + + match = of_match_device(of_match_ptr(ravb_match_table), &pdev->dev); + chip_id = (enum ravb_chip_id)match->data; + + if (chip_id == RCAR_GEN3) + irq = platform_get_irq_byname(pdev, "ch22"); + else + irq = platform_get_irq(pdev, 0); if (irq < 0) { error = irq; goto out_release; @@ -1688,6 +1731,17 @@ static int ravb_probe(struct platform_device *pdev) priv->avb_link_active_low = of_property_read_bool(np, "renesas,ether-link-active-low"); + if (chip_id == RCAR_GEN3) { + irq = platform_get_irq_byname(pdev, "ch24"); + if (irq < 0) { + error = irq; + goto out_release; + } + priv->emac_irq = irq; + } + + priv->chip_id = chip_id; + /* Set function */ ndev->netdev_ops = &ravb_netdev_ops; ndev->ethtool_ops = &ravb_ethtool_ops; @@ -1708,7 +1762,7 @@ static int ravb_probe(struct platform_device *pdev) /* Allocate descriptor base address table */ priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM; - priv->desc_bat = dma_alloc_coherent(NULL, priv->desc_bat_size, + priv->desc_bat = dma_alloc_coherent(ndev->dev.parent, priv->desc_bat_size, &priv->desc_bat_dma, GFP_KERNEL); if (!priv->desc_bat) { dev_err(&ndev->dev, @@ -1763,7 +1817,7 @@ out_napi_del: netif_napi_del(&priv->napi[RAVB_BE]); ravb_mdio_release(priv); out_dma_free: - dma_free_coherent(NULL, priv->desc_bat_size, priv->desc_bat, + dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); out_release: if (ndev) @@ -1779,7 +1833,7 @@ static int ravb_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ravb_private *priv = netdev_priv(ndev); - dma_free_coherent(NULL, priv->desc_bat_size, priv->desc_bat, + dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); /* Set reset mode */ ravb_write(ndev, CCC_OPC_RESET, CCC); @@ -1818,13 +1872,6 @@ static const struct dev_pm_ops ravb_dev_pm_ops = { #define RAVB_PM_OPS NULL #endif -static const struct of_device_id ravb_match_table[] = { - { .compatible = "renesas,etheravb-r8a7790" }, - { .compatible = "renesas,etheravb-r8a7794" }, - { } -}; -MODULE_DEVICE_TABLE(of, ravb_match_table); - static struct platform_driver ravb_driver = { .probe = ravb_probe, .remove = ravb_remove, diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 34ac41ac9e61..32a80d2df7ff 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -152,8 +152,9 @@ struct rocker_fdb_tbl_entry { struct hlist_node entry; u32 key_crc32; /* key */ bool learned; + unsigned long touched; struct rocker_fdb_tbl_key { - u32 pport; + struct rocker_port *rocker_port; u8 addr[ETH_ALEN]; __be16 vlan_id; } key; @@ -220,13 +221,13 @@ struct rocker_port { __be16 internal_vlan_id; int stp_state; u32 brport_flags; + unsigned long ageing_time; bool ctrls[ROCKER_CTRL_MAX]; unsigned long vlan_bitmap[ROCKER_VLAN_BITMAP_LEN]; struct napi_struct napi_tx; struct napi_struct napi_rx; struct rocker_dma_ring_info tx_ring; struct rocker_dma_ring_info rx_ring; - struct list_head trans_mem; }; struct rocker { @@ -246,6 +247,7 @@ struct rocker { u64 flow_tbl_next_cookie; DECLARE_HASHTABLE(group_tbl, 16); spinlock_t group_tbl_lock; /* for group tbl accesses */ + struct timer_list fdb_cleanup_timer; DECLARE_HASHTABLE(fdb_tbl, 16); spinlock_t fdb_tbl_lock; /* for fdb tbl accesses */ unsigned long internal_vlan_bitmap[ROCKER_INTERNAL_VLAN_BITMAP_LEN]; @@ -340,74 +342,63 @@ static bool rocker_port_is_ovsed(const struct rocker_port *rocker_port) #define ROCKER_OP_FLAG_REFRESH BIT(3) static void *__rocker_port_mem_alloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t size) { - struct list_head *elem = NULL; + struct switchdev_trans_item *elem = NULL; gfp_t gfp_flags = (flags & ROCKER_OP_FLAG_NOWAIT) ? GFP_ATOMIC : GFP_KERNEL; /* If in transaction prepare phase, allocate the memory - * and enqueue it on a per-port list. If in transaction - * commit phase, dequeue the memory from the per-port list + * and enqueue it on a transaction. If in transaction + * commit phase, dequeue the memory from the transaction * rather than re-allocating the memory. The idea is the * driver code paths for prepare and commit are identical * so the memory allocated in the prepare phase is the * memory used in the commit phase. */ - switch (trans) { - case SWITCHDEV_TRANS_PREPARE: + if (!trans) { + elem = kzalloc(size + sizeof(*elem), gfp_flags); + } else if (switchdev_trans_ph_prepare(trans)) { elem = kzalloc(size + sizeof(*elem), gfp_flags); if (!elem) return NULL; - list_add_tail(elem, &rocker_port->trans_mem); - break; - case SWITCHDEV_TRANS_COMMIT: - BUG_ON(list_empty(&rocker_port->trans_mem)); - elem = rocker_port->trans_mem.next; - list_del_init(elem); - break; - case SWITCHDEV_TRANS_NONE: - elem = kzalloc(size + sizeof(*elem), gfp_flags); - if (elem) - INIT_LIST_HEAD(elem); - break; - default: - break; + switchdev_trans_item_enqueue(trans, elem, kfree, elem); + } else { + elem = switchdev_trans_item_dequeue(trans); } return elem ? elem + 1 : NULL; } static void *rocker_port_kzalloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t size) { return __rocker_port_mem_alloc(rocker_port, trans, flags, size); } static void *rocker_port_kcalloc(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, size_t n, size_t size) { return __rocker_port_mem_alloc(rocker_port, trans, flags, n * size); } -static void rocker_port_kfree(enum switchdev_trans trans, const void *mem) +static void rocker_port_kfree(struct switchdev_trans *trans, const void *mem) { - struct list_head *elem; + struct switchdev_trans_item *elem; /* Frees are ignored if in transaction prepare phase. The * memory remains on the per-port list until freed in the * commit phase. */ - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; - elem = (struct list_head *)mem - 1; - BUG_ON(!list_empty(elem)); + elem = (struct switchdev_trans_item *) mem - 1; kfree(elem); } @@ -430,7 +421,7 @@ static void rocker_wait_init(struct rocker_wait *wait) } static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags) { struct rocker_wait *wait; @@ -442,7 +433,7 @@ static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port, return wait; } -static void rocker_wait_destroy(enum switchdev_trans trans, +static void rocker_wait_destroy(struct switchdev_trans *trans, struct rocker_wait *wait) { rocker_port_kfree(trans, wait); @@ -1408,7 +1399,7 @@ static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id) wait = rocker_desc_cookie_ptr_get(desc_info); if (wait->nowait) { rocker_desc_gen_clear(desc_info); - rocker_wait_destroy(SWITCHDEV_TRANS_NONE, wait); + rocker_wait_destroy(NULL, wait); } else { rocker_wait_wake_up(wait); } @@ -1463,7 +1454,7 @@ static int rocker_event_link_change(const struct rocker *rocker, } static int rocker_port_fdb(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags); @@ -1496,8 +1487,7 @@ static int rocker_event_mac_vlan_seen(const struct rocker *rocker, rocker_port->stp_state != BR_STATE_FORWARDING) return 0; - return rocker_port_fdb(rocker_port, SWITCHDEV_TRANS_NONE, - addr, vlan_id, flags); + return rocker_port_fdb(rocker_port, NULL, addr, vlan_id, flags); } static int rocker_event_process(const struct rocker *rocker, @@ -1582,7 +1572,7 @@ typedef int (*rocker_cmd_proc_cb_t)(const struct rocker_port *rocker_port, void *priv); static int rocker_cmd_exec(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, rocker_cmd_prep_cb_t prepare, void *prepare_priv, rocker_cmd_proc_cb_t process, void *process_priv) { @@ -1615,7 +1605,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port, rocker_desc_cookie_ptr_set(desc_info, wait); - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info); spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); @@ -1623,7 +1613,7 @@ static int rocker_cmd_exec(struct rocker_port *rocker_port, if (nowait) return 0; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) if (!rocker_wait_event_timeout(wait, HZ / 10)) return -EIO; @@ -1875,7 +1865,7 @@ rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port, static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, struct ethtool_cmd *ecmd) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_ethtool_proc, ecmd); @@ -1884,7 +1874,7 @@ static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, unsigned char *macaddr) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_macaddr_proc, macaddr); @@ -1893,7 +1883,7 @@ static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, struct ethtool_cmd *ecmd) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_ethtool_prep, ecmd, NULL, NULL); } @@ -1901,7 +1891,7 @@ static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, unsigned char *macaddr) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_macaddr_prep, macaddr, NULL, NULL); } @@ -1909,13 +1899,13 @@ static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port, int mtu) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_set_port_settings_mtu_prep, &mtu, NULL, NULL); } static int rocker_port_set_learning(struct rocker_port *rocker_port, - enum switchdev_trans trans) + struct switchdev_trans *trans) { return rocker_cmd_exec(rocker_port, trans, 0, rocker_cmd_set_port_learning_prep, @@ -2433,7 +2423,7 @@ rocker_flow_tbl_find(const struct rocker *rocker, } static int rocker_flow_tbl_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2449,7 +2439,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, if (found) { match->cookie = found->cookie; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); rocker_port_kfree(trans, found); found = match; @@ -2460,7 +2450,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD; } - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_add(rocker->flow_tbl, &found->entry, found->key_crc32); spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags); @@ -2470,7 +2460,7 @@ static int rocker_flow_tbl_add(struct rocker_port *rocker_port, } static int rocker_flow_tbl_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2486,7 +2476,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port, found = rocker_flow_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL; } @@ -2506,7 +2496,7 @@ static int rocker_flow_tbl_del(struct rocker_port *rocker_port, } static int rocker_flow_tbl_do(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_flow_tbl_entry *entry) { if (flags & ROCKER_OP_FLAG_REMOVE) @@ -2516,7 +2506,7 @@ static int rocker_flow_tbl_do(struct rocker_port *rocker_port, } static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, u32 in_pport_mask, enum rocker_of_dpa_table_id goto_tbl) { @@ -2536,7 +2526,7 @@ static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, } static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, __be16 vlan_id, __be16 vlan_id_mask, enum rocker_of_dpa_table_id goto_tbl, @@ -2562,7 +2552,7 @@ static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port, } static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, u32 in_pport, u32 in_pport_mask, __be16 eth_type, const u8 *eth_dst, const u8 *eth_dst_mask, __be16 vlan_id, @@ -2599,7 +2589,7 @@ static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port, } static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const u8 *eth_dst, const u8 *eth_dst_mask, __be16 vlan_id, u32 tunnel_id, enum rocker_of_dpa_table_id goto_tbl, @@ -2653,7 +2643,7 @@ static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port, } static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, __be16 eth_type, __be32 dst, __be32 dst_mask, u32 priority, enum rocker_of_dpa_table_id goto_tbl, @@ -2679,7 +2669,7 @@ static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port, } static int rocker_flow_tbl_acl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 in_pport, u32 in_pport_mask, const u8 *eth_src, const u8 *eth_src_mask, const u8 *eth_dst, const u8 *eth_dst_mask, @@ -2744,7 +2734,7 @@ rocker_group_tbl_find(const struct rocker *rocker, return NULL; } -static void rocker_group_tbl_entry_free(enum switchdev_trans trans, +static void rocker_group_tbl_entry_free(struct switchdev_trans *trans, struct rocker_group_tbl_entry *entry) { switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) { @@ -2759,7 +2749,7 @@ static void rocker_group_tbl_entry_free(enum switchdev_trans trans, } static int rocker_group_tbl_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2771,7 +2761,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, found = rocker_group_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); rocker_group_tbl_entry_free(trans, found); found = match; @@ -2781,7 +2771,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD; } - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_add(rocker->group_tbl, &found->entry, found->group_id); spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags); @@ -2791,7 +2781,7 @@ static int rocker_group_tbl_add(struct rocker_port *rocker_port, } static int rocker_group_tbl_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *match) { struct rocker *rocker = rocker_port->rocker; @@ -2804,7 +2794,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port, found = rocker_group_tbl_find(rocker, match); if (found) { - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL; } @@ -2824,7 +2814,7 @@ static int rocker_group_tbl_del(struct rocker_port *rocker_port, } static int rocker_group_tbl_do(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, struct rocker_group_tbl_entry *entry) { if (flags & ROCKER_OP_FLAG_REMOVE) @@ -2834,7 +2824,7 @@ static int rocker_group_tbl_do(struct rocker_port *rocker_port, } static int rocker_group_l2_interface(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, u32 out_pport, int pop_vlan) { @@ -2851,7 +2841,7 @@ static int rocker_group_l2_interface(struct rocker_port *rocker_port, } static int rocker_group_l2_fan_out(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, u8 group_count, const u32 *group_ids, u32 group_id) { @@ -2876,7 +2866,7 @@ static int rocker_group_l2_fan_out(struct rocker_port *rocker_port, } static int rocker_group_l2_flood(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, u8 group_count, const u32 *group_ids, u32 group_id) { @@ -2886,7 +2876,7 @@ static int rocker_group_l2_flood(struct rocker_port *rocker_port, } static int rocker_group_l3_unicast(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u32 index, const u8 *src_mac, const u8 *dst_mac, __be16 vlan_id, bool ttl_check, u32 pport) { @@ -2922,22 +2912,22 @@ rocker_neigh_tbl_find(const struct rocker *rocker, __be32 ip_addr) } static void _rocker_neigh_add(struct rocker *rocker, - enum switchdev_trans trans, + struct switchdev_trans *trans, struct rocker_neigh_tbl_entry *entry) { - if (trans != SWITCHDEV_TRANS_COMMIT) + if (!switchdev_trans_ph_commit(trans)) entry->index = rocker->neigh_tbl_next_index++; - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; entry->ref_count++; hash_add(rocker->neigh_tbl, &entry->entry, be32_to_cpu(entry->ip_addr)); } -static void _rocker_neigh_del(enum switchdev_trans trans, +static void _rocker_neigh_del(struct switchdev_trans *trans, struct rocker_neigh_tbl_entry *entry) { - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) return; if (--entry->ref_count == 0) { hash_del(&entry->entry); @@ -2946,19 +2936,19 @@ static void _rocker_neigh_del(enum switchdev_trans trans, } static void _rocker_neigh_update(struct rocker_neigh_tbl_entry *entry, - enum switchdev_trans trans, + struct switchdev_trans *trans, const u8 *eth_dst, bool ttl_check) { if (eth_dst) { ether_addr_copy(entry->eth_dst, eth_dst); entry->ttl_check = ttl_check; - } else if (trans != SWITCHDEV_TRANS_PREPARE) { + } else if (!switchdev_trans_ph_prepare(trans)) { entry->ref_count++; } } static int rocker_port_ipv4_neigh(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, __be32 ip_addr, const u8 *eth_dst) { struct rocker *rocker = rocker_port->rocker; @@ -3050,7 +3040,8 @@ err_out: } static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port, - enum switchdev_trans trans, __be32 ip_addr) + struct switchdev_trans *trans, + __be32 ip_addr) { struct net_device *dev = rocker_port->dev; struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr); @@ -3078,7 +3069,7 @@ static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port, } static int rocker_port_ipv4_nh(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be32 ip_addr, u32 *index) { struct rocker *rocker = rocker_port->rocker; @@ -3137,7 +3128,7 @@ static int rocker_port_ipv4_nh(struct rocker_port *rocker_port, } static int rocker_port_vlan_flood_group(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { struct rocker_port *p; @@ -3186,7 +3177,7 @@ no_ports_in_vlan: } static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id, bool pop_vlan) { const struct rocker *rocker = rocker_port->rocker; @@ -3292,7 +3283,7 @@ static struct rocker_ctrl { }; static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { u32 in_pport = rocker_port->pport; @@ -3325,7 +3316,8 @@ static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, + int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { @@ -3350,7 +3342,7 @@ static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; @@ -3374,7 +3366,7 @@ static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl, __be16 vlan_id) { if (ctrl->acl) @@ -3392,7 +3384,7 @@ static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, } static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { int err = 0; @@ -3411,7 +3403,7 @@ static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port, } static int rocker_port_ctrl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const struct rocker_ctrl *ctrl) { u16 vid; @@ -3430,7 +3422,7 @@ static int rocker_port_ctrl(struct rocker_port *rocker_port, } static int rocker_port_vlan(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, u16 vid) + struct switchdev_trans *trans, int flags, u16 vid) { enum rocker_of_dpa_table_id goto_tbl = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; @@ -3487,14 +3479,14 @@ static int rocker_port_vlan(struct rocker_port *rocker_port, "Error (%d) port VLAN table\n", err); err_out: - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap); return err; } static int rocker_port_ig_tbl(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { enum rocker_of_dpa_table_id goto_tbl; u32 in_pport; @@ -3522,7 +3514,7 @@ static int rocker_port_ig_tbl(struct rocker_port *rocker_port, struct rocker_fdb_learn_work { struct work_struct work; struct rocker_port *rocker_port; - enum switchdev_trans trans; + struct switchdev_trans *trans; int flags; u8 addr[ETH_ALEN]; u16 vid; @@ -3550,7 +3542,7 @@ static void rocker_port_fdb_learn_work(struct work_struct *work) } static int rocker_port_fdb_learn(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, const u8 *addr, __be16 vlan_id) { struct rocker_fdb_learn_work *lw; @@ -3592,7 +3584,7 @@ static int rocker_port_fdb_learn(struct rocker_port *rocker_port, ether_addr_copy(lw->addr, addr); lw->vid = rocker_port_vlan_to_vid(rocker_port, vlan_id); - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) rocker_port_kfree(trans, lw); else schedule_work(&lw->work); @@ -3614,7 +3606,7 @@ rocker_fdb_tbl_find(const struct rocker *rocker, } static int rocker_port_fdb(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, const unsigned char *addr, __be16 vlan_id, int flags) { @@ -3629,7 +3621,8 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, return -ENOMEM; fdb->learned = (flags & ROCKER_OP_FLAG_LEARNED); - fdb->key.pport = rocker_port->pport; + fdb->touched = jiffies; + fdb->key.rocker_port = rocker_port; ether_addr_copy(fdb->key.addr, addr); fdb->key.vlan_id = vlan_id; fdb->key_crc32 = crc32(~0, &fdb->key, sizeof(fdb->key)); @@ -3638,13 +3631,17 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, found = rocker_fdb_tbl_find(rocker, fdb); - if (removing && found) { - rocker_port_kfree(trans, fdb); - if (trans != SWITCHDEV_TRANS_PREPARE) - hash_del(&found->entry); - } else if (!removing && !found) { - if (trans != SWITCHDEV_TRANS_PREPARE) - hash_add(rocker->fdb_tbl, &fdb->entry, fdb->key_crc32); + if (found) { + found->touched = jiffies; + if (removing) { + rocker_port_kfree(trans, fdb); + if (!switchdev_trans_ph_prepare(trans)) + hash_del(&found->entry); + } + } else if (!removing) { + if (!switchdev_trans_ph_prepare(trans)) + hash_add(rocker->fdb_tbl, &fdb->entry, + fdb->key_crc32); } spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); @@ -3662,7 +3659,7 @@ static int rocker_port_fdb(struct rocker_port *rocker_port, } static int rocker_port_fdb_flush(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { struct rocker *rocker = rocker_port->rocker; struct rocker_fdb_tbl_entry *found; @@ -3675,12 +3672,12 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port, rocker_port->stp_state == BR_STATE_FORWARDING) return 0; - flags |= ROCKER_OP_FLAG_REMOVE; + flags |= ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE; spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) { - if (found->key.pport != rocker_port->pport) + if (found->key.rocker_port != rocker_port) continue; if (!found->learned) continue; @@ -3689,7 +3686,7 @@ static int rocker_port_fdb_flush(struct rocker_port *rocker_port, found->key.vlan_id); if (err) goto err_out; - if (trans != SWITCHDEV_TRANS_PREPARE) + if (!switchdev_trans_ph_prepare(trans)) hash_del(&found->entry); } @@ -3699,8 +3696,43 @@ err_out: return err; } +static void rocker_fdb_cleanup(unsigned long data) +{ + struct rocker *rocker = (struct rocker *)data; + struct rocker_port *rocker_port; + struct rocker_fdb_tbl_entry *entry; + struct hlist_node *tmp; + unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME; + unsigned long expires; + unsigned long lock_flags; + int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE | + ROCKER_OP_FLAG_LEARNED; + int bkt; + + spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); + + hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, entry, entry) { + if (!entry->learned) + continue; + rocker_port = entry->key.rocker_port; + expires = entry->touched + rocker_port->ageing_time; + if (time_before_eq(expires, jiffies)) { + rocker_port_fdb_learn(rocker_port, NULL, + flags, entry->key.addr, + entry->key.vlan_id); + hash_del(&entry->entry); + } else if (time_before(expires, next_timer)) { + next_timer = expires; + } + } + + spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); + + mod_timer(&rocker->fdb_cleanup_timer, round_jiffies_up(next_timer)); +} + static int rocker_port_router_mac(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, __be16 vlan_id) { u32 in_pport_mask = 0xffffffff; @@ -3733,7 +3765,7 @@ static int rocker_port_router_mac(struct rocker_port *rocker_port, } static int rocker_port_fwding(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { bool pop_vlan; u32 out_pport; @@ -3772,16 +3804,16 @@ static int rocker_port_fwding(struct rocker_port *rocker_port, } static int rocker_port_stp_update(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags, + struct switchdev_trans *trans, int flags, u8 state) { bool want[ROCKER_CTRL_MAX] = { 0, }; bool prev_ctrls[ROCKER_CTRL_MAX]; - u8 prev_state; + u8 uninitialized_var(prev_state); int err; int i; - if (trans == SWITCHDEV_TRANS_PREPARE) { + if (switchdev_trans_ph_prepare(trans)) { memcpy(prev_ctrls, rocker_port->ctrls, sizeof(prev_ctrls)); prev_state = rocker_port->stp_state; } @@ -3833,7 +3865,7 @@ static int rocker_port_stp_update(struct rocker_port *rocker_port, err = rocker_port_fwding(rocker_port, trans, flags); err_out: - if (trans == SWITCHDEV_TRANS_PREPARE) { + if (switchdev_trans_ph_prepare(trans)) { memcpy(rocker_port->ctrls, prev_ctrls, sizeof(prev_ctrls)); rocker_port->stp_state = prev_state; } @@ -3842,7 +3874,7 @@ err_out: } static int rocker_port_fwd_enable(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { if (rocker_port_is_bridged(rocker_port)) /* bridge STP will enable port */ @@ -3854,7 +3886,7 @@ static int rocker_port_fwd_enable(struct rocker_port *rocker_port, } static int rocker_port_fwd_disable(struct rocker_port *rocker_port, - enum switchdev_trans trans, int flags) + struct switchdev_trans *trans, int flags) { if (rocker_port_is_bridged(rocker_port)) /* bridge STP will disable port */ @@ -3952,7 +3984,7 @@ not_found: } static int rocker_port_fib_ipv4(struct rocker_port *rocker_port, - enum switchdev_trans trans, __be32 dst, + struct switchdev_trans *trans, __be32 dst, int dst_len, const struct fib_info *fi, u32 tb_id, int flags) { @@ -4026,7 +4058,7 @@ static int rocker_port_open(struct net_device *dev) goto err_request_rx_irq; } - err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); if (err) goto err_fwd_enable; @@ -4054,7 +4086,7 @@ static int rocker_port_stop(struct net_device *dev) rocker_port_set_enable(rocker_port, false); napi_disable(&rocker_port->napi_rx); napi_disable(&rocker_port->napi_tx); - rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE, + rocker_port_fwd_disable(rocker_port, NULL, ROCKER_OP_FLAG_NOWAIT); free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); @@ -4240,7 +4272,7 @@ static int rocker_port_get_phys_port_name(struct net_device *dev, struct port_name name = { .buf = buf, .len = len }; int err; - err = rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + err = rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_settings_prep, NULL, rocker_cmd_get_port_settings_phys_name_proc, &name); @@ -4265,7 +4297,7 @@ static void rocker_port_neigh_destroy(struct neighbour *n) int flags = ROCKER_OP_FLAG_REMOVE | ROCKER_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *)n->primary_key; - rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE, + rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); } @@ -4297,11 +4329,11 @@ static int rocker_port_attr_get(struct net_device *dev, const struct rocker *rocker = rocker_port->rocker; switch (attr->id) { - case SWITCHDEV_ATTR_PORT_PARENT_ID: + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: attr->u.ppid.id_len = sizeof(rocker->hw.id); memcpy(&attr->u.ppid.id, &rocker->hw.id, attr->u.ppid.id_len); break; - case SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS: + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: attr->u.brport_flags = rocker_port->brport_flags; break; default: @@ -4311,18 +4343,8 @@ static int rocker_port_attr_get(struct net_device *dev, return 0; } -static void rocker_port_trans_abort(const struct rocker_port *rocker_port) -{ - struct list_head *mem, *tmp; - - list_for_each_safe(mem, tmp, &rocker_port->trans_mem) { - list_del(mem); - kfree(mem); - } -} - static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, - enum switchdev_trans trans, + struct switchdev_trans *trans, unsigned long brport_flags) { unsigned long orig_flags; @@ -4333,39 +4355,44 @@ static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, if ((orig_flags ^ rocker_port->brport_flags) & BR_LEARNING) err = rocker_port_set_learning(rocker_port, trans); - if (trans == SWITCHDEV_TRANS_PREPARE) + if (switchdev_trans_ph_prepare(trans)) rocker_port->brport_flags = orig_flags; return err; } +static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port, + struct switchdev_trans *trans, + u32 ageing_time) +{ + if (!switchdev_trans_ph_prepare(trans)) { + rocker_port->ageing_time = clock_t_to_jiffies(ageing_time); + mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies); + } + + return 0; +} + static int rocker_port_attr_set(struct net_device *dev, - struct switchdev_attr *attr) + const struct switchdev_attr *attr, + struct switchdev_trans *trans) { struct rocker_port *rocker_port = netdev_priv(dev); int err = 0; - switch (attr->trans) { - case SWITCHDEV_TRANS_PREPARE: - BUG_ON(!list_empty(&rocker_port->trans_mem)); - break; - case SWITCHDEV_TRANS_ABORT: - rocker_port_trans_abort(rocker_port); - return 0; - default: - break; - } - switch (attr->id) { - case SWITCHDEV_ATTR_PORT_STP_STATE: - err = rocker_port_stp_update(rocker_port, attr->trans, - ROCKER_OP_FLAG_NOWAIT, + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: + err = rocker_port_stp_update(rocker_port, trans, 0, attr->u.stp_state); break; - case SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS: - err = rocker_port_brport_flags_set(rocker_port, attr->trans, + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + err = rocker_port_brport_flags_set(rocker_port, trans, attr->u.brport_flags); break; + case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: + err = rocker_port_bridge_ageing_time(rocker_port, trans, + attr->u.ageing_time); + break; default: err = -EOPNOTSUPP; break; @@ -4375,7 +4402,8 @@ static int rocker_port_attr_set(struct net_device *dev, } static int rocker_port_vlan_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, u16 vid, u16 flags) + struct switchdev_trans *trans, + u16 vid, u16 flags) { int err; @@ -4394,8 +4422,8 @@ static int rocker_port_vlan_add(struct rocker_port *rocker_port, } static int rocker_port_vlans_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, - const struct switchdev_obj_vlan *vlan) + struct switchdev_trans *trans, + const struct switchdev_obj_port_vlan *vlan) { u16 vid; int err; @@ -4411,8 +4439,8 @@ static int rocker_port_vlans_add(struct rocker_port *rocker_port, } static int rocker_port_fdb_add(struct rocker_port *rocker_port, - enum switchdev_trans trans, - const struct switchdev_obj_fdb *fdb) + struct switchdev_trans *trans, + const struct switchdev_obj_port_fdb *fdb) { __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); int flags = 0; @@ -4424,36 +4452,27 @@ static int rocker_port_fdb_add(struct rocker_port *rocker_port, } static int rocker_port_obj_add(struct net_device *dev, - struct switchdev_obj *obj) + const struct switchdev_obj *obj, + struct switchdev_trans *trans) { struct rocker_port *rocker_port = netdev_priv(dev); const struct switchdev_obj_ipv4_fib *fib4; int err = 0; - switch (obj->trans) { - case SWITCHDEV_TRANS_PREPARE: - BUG_ON(!list_empty(&rocker_port->trans_mem)); - break; - case SWITCHDEV_TRANS_ABORT: - rocker_port_trans_abort(rocker_port); - return 0; - default: - break; - } - switch (obj->id) { - case SWITCHDEV_OBJ_PORT_VLAN: - err = rocker_port_vlans_add(rocker_port, obj->trans, - &obj->u.vlan); + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = rocker_port_vlans_add(rocker_port, trans, + SWITCHDEV_OBJ_PORT_VLAN(obj)); break; - case SWITCHDEV_OBJ_IPV4_FIB: - fib4 = &obj->u.ipv4_fib; - err = rocker_port_fib_ipv4(rocker_port, obj->trans, + case SWITCHDEV_OBJ_ID_IPV4_FIB: + fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); + err = rocker_port_fib_ipv4(rocker_port, trans, htonl(fib4->dst), fib4->dst_len, - fib4->fi, fib4->tb_id, 0); + &fib4->fi, fib4->tb_id, 0); break; - case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_add(rocker_port, obj->trans, &obj->u.fdb); + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = rocker_port_fdb_add(rocker_port, trans, + SWITCHDEV_OBJ_PORT_FDB(obj)); break; default: err = -EOPNOTSUPP; @@ -4468,17 +4487,17 @@ static int rocker_port_vlan_del(struct rocker_port *rocker_port, { int err; - err = rocker_port_router_mac(rocker_port, SWITCHDEV_TRANS_NONE, + err = rocker_port_router_mac(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE, htons(vid)); if (err) return err; - return rocker_port_vlan(rocker_port, SWITCHDEV_TRANS_NONE, + return rocker_port_vlan(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE, vid); } static int rocker_port_vlans_del(struct rocker_port *rocker_port, - const struct switchdev_obj_vlan *vlan) + const struct switchdev_obj_port_vlan *vlan) { u16 vid; int err; @@ -4493,11 +4512,11 @@ static int rocker_port_vlans_del(struct rocker_port *rocker_port, } static int rocker_port_fdb_del(struct rocker_port *rocker_port, - enum switchdev_trans trans, - const struct switchdev_obj_fdb *fdb) + struct switchdev_trans *trans, + const struct switchdev_obj_port_fdb *fdb) { __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); - int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE; + int flags = ROCKER_OP_FLAG_REMOVE; if (!rocker_port_is_bridged(rocker_port)) return -EINVAL; @@ -4506,25 +4525,27 @@ static int rocker_port_fdb_del(struct rocker_port *rocker_port, } static int rocker_port_obj_del(struct net_device *dev, - struct switchdev_obj *obj) + const struct switchdev_obj *obj) { struct rocker_port *rocker_port = netdev_priv(dev); const struct switchdev_obj_ipv4_fib *fib4; int err = 0; switch (obj->id) { - case SWITCHDEV_OBJ_PORT_VLAN: - err = rocker_port_vlans_del(rocker_port, &obj->u.vlan); + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = rocker_port_vlans_del(rocker_port, + SWITCHDEV_OBJ_PORT_VLAN(obj)); break; - case SWITCHDEV_OBJ_IPV4_FIB: - fib4 = &obj->u.ipv4_fib; - err = rocker_port_fib_ipv4(rocker_port, SWITCHDEV_TRANS_NONE, + case SWITCHDEV_OBJ_ID_IPV4_FIB: + fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); + err = rocker_port_fib_ipv4(rocker_port, NULL, htonl(fib4->dst), fib4->dst_len, - fib4->fi, fib4->tb_id, + &fib4->fi, fib4->tb_id, ROCKER_OP_FLAG_REMOVE); break; - case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_del(rocker_port, obj->trans, &obj->u.fdb); + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = rocker_port_fdb_del(rocker_port, NULL, + SWITCHDEV_OBJ_PORT_FDB(obj)); break; default: err = -EOPNOTSUPP; @@ -4535,10 +4556,10 @@ static int rocker_port_obj_del(struct net_device *dev, } static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, - struct switchdev_obj *obj) + struct switchdev_obj_port_fdb *fdb, + switchdev_obj_dump_cb_t *cb) { struct rocker *rocker = rocker_port->rocker; - struct switchdev_obj_fdb *fdb = &obj->u.fdb; struct rocker_fdb_tbl_entry *found; struct hlist_node *tmp; unsigned long lock_flags; @@ -4547,13 +4568,13 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) { - if (found->key.pport != rocker_port->pport) + if (found->key.rocker_port != rocker_port) continue; - fdb->addr = found->key.addr; + ether_addr_copy(fdb->addr, found->key.addr); fdb->ndm_state = NUD_REACHABLE; fdb->vid = rocker_port_vlan_to_vid(rocker_port, found->key.vlan_id); - err = obj->cb(rocker_port->dev, obj); + err = cb(&fdb->obj); if (err) break; } @@ -4563,9 +4584,9 @@ static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, } static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, - struct switchdev_obj *obj) + struct switchdev_obj_port_vlan *vlan, + switchdev_obj_dump_cb_t *cb) { - struct switchdev_obj_vlan *vlan = &obj->u.vlan; u16 vid; int err = 0; @@ -4576,7 +4597,7 @@ static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, if (rocker_vlan_id_is_internal(htons(vid))) vlan->flags |= BRIDGE_VLAN_INFO_PVID; vlan->vid_begin = vlan->vid_end = vid; - err = obj->cb(rocker_port->dev, obj); + err = cb(&vlan->obj); if (err) break; } @@ -4585,17 +4606,20 @@ static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, } static int rocker_port_obj_dump(struct net_device *dev, - struct switchdev_obj *obj) + struct switchdev_obj *obj, + switchdev_obj_dump_cb_t *cb) { const struct rocker_port *rocker_port = netdev_priv(dev); int err = 0; switch (obj->id) { - case SWITCHDEV_OBJ_PORT_FDB: - err = rocker_port_fdb_dump(rocker_port, obj); + case SWITCHDEV_OBJ_ID_PORT_FDB: + err = rocker_port_fdb_dump(rocker_port, + SWITCHDEV_OBJ_PORT_FDB(obj), cb); break; - case SWITCHDEV_OBJ_PORT_VLAN: - err = rocker_port_vlan_dump(rocker_port, obj); + case SWITCHDEV_OBJ_ID_PORT_VLAN: + err = rocker_port_vlan_dump(rocker_port, + SWITCHDEV_OBJ_PORT_VLAN(obj), cb); break; default: err = -EOPNOTSUPP; @@ -4738,7 +4762,7 @@ rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port, static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port, void *priv) { - return rocker_cmd_exec(rocker_port, SWITCHDEV_TRANS_NONE, 0, + return rocker_cmd_exec(rocker_port, NULL, 0, rocker_cmd_get_port_stats_prep, NULL, rocker_cmd_get_port_stats_ethtool_proc, priv); @@ -4930,8 +4954,7 @@ static void rocker_remove_ports(const struct rocker *rocker) rocker_port = rocker->ports[i]; if (!rocker_port) continue; - rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, - ROCKER_OP_FLAG_REMOVE); + rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); unregister_netdev(rocker_port->dev); free_netdev(rocker_port->dev); } @@ -4969,7 +4992,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) rocker_port->port_number = port_number; rocker_port->pport = port_number + 1; rocker_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC; - INIT_LIST_HEAD(&rocker_port->trans_mem); + rocker_port->ageing_time = BR_DEFAULT_AGEING_TIME; rocker_port_dev_addr_init(rocker_port); dev->netdev_ops = &rocker_port_netdev_ops; @@ -4992,9 +5015,9 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) switchdev_port_fwd_mark_set(rocker_port->dev, NULL, false); - rocker_port_set_learning(rocker_port, SWITCHDEV_TRANS_NONE); + rocker_port_set_learning(rocker_port, NULL); - err = rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_ig_tbl(rocker_port, NULL, 0); if (err) { netdev_err(rocker_port->dev, "install ig port table failed\n"); goto err_port_ig_tbl; @@ -5003,8 +5026,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) rocker_port->internal_vlan_id = rocker_port_internal_vlan_id_get(rocker_port, dev->ifindex); - err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); if (err) { netdev_err(rocker_port->dev, "install untagged VLAN failed\n"); goto err_untagged_vlan; @@ -5013,8 +5035,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) return 0; err_untagged_vlan: - rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, - ROCKER_OP_FLAG_REMOVE); + rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); err_port_ig_tbl: rocker->ports[port_number] = NULL; unregister_netdev(dev); @@ -5183,6 +5204,10 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_init_tbls; } + setup_timer(&rocker->fdb_cleanup_timer, rocker_fdb_cleanup, + (unsigned long) rocker); + mod_timer(&rocker->fdb_cleanup_timer, jiffies); + err = rocker_probe_ports(rocker); if (err) { dev_err(&pdev->dev, "failed to probe ports\n"); @@ -5195,6 +5220,7 @@ static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_probe_ports: + del_timer_sync(&rocker->fdb_cleanup_timer); rocker_free_tbls(rocker); err_init_tbls: free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); @@ -5222,6 +5248,7 @@ static void rocker_remove(struct pci_dev *pdev) { struct rocker *rocker = pci_get_drvdata(pdev); + del_timer_sync(&rocker->fdb_cleanup_timer); rocker_free_tbls(rocker); rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); rocker_remove_ports(rocker); @@ -5275,8 +5302,7 @@ static int rocker_port_bridge_join(struct rocker_port *rocker_port, rocker_port->bridge_dev = bridge; switchdev_port_fwd_mark_set(rocker_port->dev, bridge, true); - return rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + return rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); } static int rocker_port_bridge_leave(struct rocker_port *rocker_port) @@ -5298,14 +5324,12 @@ static int rocker_port_bridge_leave(struct rocker_port *rocker_port) false); rocker_port->bridge_dev = NULL; - err = rocker_port_vlan_add(rocker_port, SWITCHDEV_TRANS_NONE, - untagged_vid, 0); + err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); if (err) return err; if (rocker_port->dev->flags & IFF_UP) - err = rocker_port_fwd_enable(rocker_port, - SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); return err; } @@ -5318,10 +5342,10 @@ static int rocker_port_ovs_changed(struct rocker_port *rocker_port, rocker_port->bridge_dev = master; - err = rocker_port_fwd_disable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_disable(rocker_port, NULL, 0); if (err) return err; - err = rocker_port_fwd_enable(rocker_port, SWITCHDEV_TRANS_NONE, 0); + err = rocker_port_fwd_enable(rocker_port, NULL, 0); return err; } @@ -5399,8 +5423,7 @@ static int rocker_neigh_update(struct net_device *dev, struct neighbour *n) ROCKER_OP_FLAG_NOWAIT; __be32 ip_addr = *(__be32 *)n->primary_key; - return rocker_port_ipv4_neigh(rocker_port, SWITCHDEV_TRANS_NONE, - flags, ip_addr, n->ha); + return rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); } static int rocker_netevent_event(struct notifier_block *unused, diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index ff649ebef637..78b7b7bcae37 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -1604,6 +1604,22 @@ efx_ef10_mcdi_read_response(struct efx_nic *efx, efx_dword_t *outbuf, memcpy(outbuf, pdu + offset, outlen); } +static void efx_ef10_mcdi_reboot_detected(struct efx_nic *efx) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + + /* All our allocations have been reset */ + efx_ef10_reset_mc_allocations(efx); + + /* The datapath firmware might have been changed */ + nic_data->must_check_datapath_caps = true; + + /* MAC statistics have been cleared on the NIC; clear the local + * statistic that we update with efx_update_diff_stat(). + */ + nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0; +} + static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; @@ -1623,17 +1639,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) return 0; nic_data->warm_boot_count = rc; - - /* All our allocations have been reset */ - efx_ef10_reset_mc_allocations(efx); - - /* The datapath firmware might have been changed */ - nic_data->must_check_datapath_caps = true; - - /* MAC statistics have been cleared on the NIC; clear the local - * statistic that we update with efx_update_diff_stat(). - */ - nic_data->stats[EF10_STAT_port_rx_bad_bytes] = 0; + efx_ef10_mcdi_reboot_detected(efx); return -EIO; } @@ -4670,6 +4676,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = { .mcdi_poll_response = efx_ef10_mcdi_poll_response, .mcdi_read_response = efx_ef10_mcdi_read_response, .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot, + .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected, .irq_enable_master = efx_port_dummy_op_void, .irq_test_generate = efx_ef10_irq_test_generate, .irq_disable_non_ev = efx_port_dummy_op_void, @@ -4774,6 +4781,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .mcdi_poll_response = efx_ef10_mcdi_poll_response, .mcdi_read_response = efx_ef10_mcdi_read_response, .mcdi_poll_reboot = efx_ef10_mcdi_poll_reboot, + .mcdi_reboot_detected = efx_ef10_mcdi_reboot_detected, .irq_enable_master = efx_port_dummy_op_void, .irq_test_generate = efx_ef10_irq_test_generate, .irq_disable_non_ev = efx_port_dummy_op_void, diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 98d172b04f71..d3f307e5c070 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -1028,10 +1028,21 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) /* Consume the status word since efx_mcdi_rpc_finish() won't */ for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) { - if (efx_mcdi_poll_reboot(efx)) + rc = efx_mcdi_poll_reboot(efx); + if (rc) break; udelay(MCDI_STATUS_DELAY_US); } + + /* On EF10, a CODE_MC_REBOOT event can be received without the + * reboot detection in efx_mcdi_poll_reboot() being triggered. + * If zero was returned from the final call to + * efx_mcdi_poll_reboot(), the MC reboot wasn't noticed but the + * MC has definitely rebooted so prepare for the reset. + */ + if (!rc && efx->type->mcdi_reboot_detected) + efx->type->mcdi_reboot_detected(efx); + mcdi->new_epoch = true; /* Nobody was waiting for an MCDI request, so trigger a reset */ diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index c530e1c4cb4f..ad56231743a6 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1277,6 +1277,7 @@ struct efx_nic_type { void (*mcdi_read_response)(struct efx_nic *efx, efx_dword_t *pdu, size_t pdu_offset, size_t pdu_len); int (*mcdi_poll_reboot)(struct efx_nic *efx); + void (*mcdi_reboot_detected)(struct efx_nic *efx); void (*irq_enable_master)(struct efx_nic *efx); void (*irq_test_generate)(struct efx_nic *efx); void (*irq_disable_non_ev)(struct efx_nic *efx); diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 630f0b7800e4..0e2fc1a844ab 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2018,10 +2018,18 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, lp->cfg.flags |= SMC91X_USE_DMA; # endif if (lp->cfg.flags & SMC91X_USE_DMA) { - int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW, - smc_pxa_dma_irq, NULL); - if (dma >= 0) - dev->dma = dma; + dma_cap_mask_t mask; + struct pxad_param param; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + param.prio = PXAD_PRIO_LOWEST; + param.drcmr = -1UL; + + lp->dma_chan = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m, &dev->dev, + "data"); } #endif @@ -2032,8 +2040,8 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, version_string, revision_register & 0x0f, lp->base, dev->irq); - if (dev->dma != (unsigned char)-1) - pr_cont(" DMA %d", dev->dma); + if (lp->dma_chan) + pr_cont(" DMA %p", lp->dma_chan); pr_cont("%s%s\n", lp->cfg.flags & SMC91X_NOWAIT ? " [nowait]" : "", @@ -2058,8 +2066,8 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, err_out: #ifdef CONFIG_ARCH_PXA - if (retval && dev->dma != (unsigned char)-1) - pxa_free_dma(dev->dma); + if (retval && lp->dma_chan) + dma_release_channel(lp->dma_chan); #endif return retval; } @@ -2370,6 +2378,7 @@ static int smc_drv_probe(struct platform_device *pdev) struct smc_local *lp = netdev_priv(ndev); lp->device = &pdev->dev; lp->physaddr = res->start; + } #endif @@ -2406,8 +2415,8 @@ static int smc_drv_remove(struct platform_device *pdev) free_irq(ndev->irq, ndev); #ifdef CONFIG_ARCH_PXA - if (ndev->dma != (unsigned char)-1) - pxa_free_dma(ndev->dma); + if (lp->dma_chan) + dma_release_channel(lp->dma_chan); #endif iounmap(lp->base); diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h index 3a18501d1068..a3c129e1e40a 100644 --- a/drivers/net/ethernet/smsc/smc91x.h +++ b/drivers/net/ethernet/smsc/smc91x.h @@ -33,6 +33,7 @@ #ifndef _SMC91X_H_ #define _SMC91X_H_ +#include <linux/dmaengine.h> #include <linux/smc91x.h> /* @@ -244,6 +245,7 @@ struct smc_local { u_long physaddr; struct device *device; #endif + struct dma_chan *dma_chan; void __iomem *base; void __iomem *datacs; @@ -265,21 +267,47 @@ struct smc_local { * as RX which can overrun memory and lose packets. */ #include <linux/dma-mapping.h> -#include <mach/dma.h> +#include <linux/dma/pxa-dma.h> #ifdef SMC_insl #undef SMC_insl #define SMC_insl(a, r, p, l) \ smc_pxa_dma_insl(a, lp, r, dev->dma, p, l) static inline void +smc_pxa_dma_inpump(struct smc_local *lp, u_char *buf, int len) +{ + dma_addr_t dmabuf; + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + enum dma_status status; + struct dma_tx_state state; + + dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE); + tx = dmaengine_prep_slave_single(lp->dma_chan, dmabuf, len, + DMA_DEV_TO_MEM, 0); + if (tx) { + cookie = dmaengine_submit(tx); + dma_async_issue_pending(lp->dma_chan); + do { + status = dmaengine_tx_status(lp->dma_chan, cookie, + &state); + cpu_relax(); + } while (status != DMA_COMPLETE && status != DMA_ERROR && + state.residue); + dmaengine_terminate_all(lp->dma_chan); + } + dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE); +} + +static inline void smc_pxa_dma_insl(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma, u_char *buf, int len) { - u_long physaddr = lp->physaddr; - dma_addr_t dmabuf; + struct dma_slave_config config; + int ret; /* fallback if no DMA available */ - if (dma == (unsigned char)-1) { + if (!lp->dma_chan) { readsl(ioaddr + reg, buf, len); return; } @@ -291,18 +319,22 @@ smc_pxa_dma_insl(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma, len--; } + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.src_addr = lp->physaddr + reg; + config.dst_addr = lp->physaddr + reg; + config.src_maxburst = 32; + config.dst_maxburst = 32; + ret = dmaengine_slave_config(lp->dma_chan, &config); + if (ret) { + dev_err(lp->device, "dma channel configuration failed: %d\n", + ret); + return; + } + len *= 4; - dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE); - DCSR(dma) = DCSR_NODESC; - DTADR(dma) = dmabuf; - DSADR(dma) = physaddr + reg; - DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 | - DCMD_WIDTH4 | (DCMD_LENGTH & len)); - DCSR(dma) = DCSR_NODESC | DCSR_RUN; - while (!(DCSR(dma) & DCSR_STOPSTATE)) - cpu_relax(); - DCSR(dma) = 0; - dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE); + smc_pxa_dma_inpump(lp, buf, len); } #endif @@ -314,11 +346,11 @@ static inline void smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma, u_char *buf, int len) { - u_long physaddr = lp->physaddr; - dma_addr_t dmabuf; + struct dma_slave_config config; + int ret; /* fallback if no DMA available */ - if (dma == (unsigned char)-1) { + if (!lp->dma_chan) { readsw(ioaddr + reg, buf, len); return; } @@ -330,26 +362,25 @@ smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma, len--; } + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + config.src_addr = lp->physaddr + reg; + config.dst_addr = lp->physaddr + reg; + config.src_maxburst = 32; + config.dst_maxburst = 32; + ret = dmaengine_slave_config(lp->dma_chan, &config); + if (ret) { + dev_err(lp->device, "dma channel configuration failed: %d\n", + ret); + return; + } + len *= 2; - dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE); - DCSR(dma) = DCSR_NODESC; - DTADR(dma) = dmabuf; - DSADR(dma) = physaddr + reg; - DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 | - DCMD_WIDTH2 | (DCMD_LENGTH & len)); - DCSR(dma) = DCSR_NODESC | DCSR_RUN; - while (!(DCSR(dma) & DCSR_STOPSTATE)) - cpu_relax(); - DCSR(dma) = 0; - dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE); + smc_pxa_dma_inpump(lp, buf, len); } #endif -static void -smc_pxa_dma_irq(int dma, void *dummy) -{ - DCSR(dma) = 0; -} #endif /* CONFIG_ARCH_PXA */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 925f2f8659b8..64d8aa4e0cad 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -424,7 +424,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) { struct stmmac_priv *priv = netdev_priv(dev); struct hwtstamp_config config; - struct timespec now; + struct timespec64 now; u64 temp = 0; u32 ptp_v2 = 0; u32 tstamp_all = 0; @@ -621,8 +621,10 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) priv->default_addend); /* initialize system time */ - getnstimeofday(&now); - priv->hw->ptp->init_systime(priv->ioaddr, now.tv_sec, + ktime_get_real_ts64(&now); + + /* lower 32 bits of tv_sec are safe until y2106 */ + priv->hw->ptp->init_systime(priv->ioaddr, (u32)now.tv_sec, now.tv_nsec); } @@ -1945,7 +1947,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); unsigned int txsize = priv->dma_tx_size; - unsigned int entry; + int entry; int i, csum_insertion = 0, is_jumbo = 0; int nfrags = skb_shinfo(skb)->nr_frags; struct dma_desc *desc, *first; diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 6ce973187225..062bce9acde6 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -4529,9 +4529,6 @@ static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); strlcpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); - info->regdump_len = cp->casreg_len < CAS_MAX_REGS ? - cp->casreg_len : CAS_MAX_REGS; - info->n_stats = CAS_NUM_STAT_KEYS; } static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index a9cac8413e49..14c9d1baa85c 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -2182,11 +2182,6 @@ bdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(priv->pdev), sizeof(drvinfo->bus_info)); - - drvinfo->n_stats = ((priv->stats_flag) ? ARRAY_SIZE(bdx_stat_names) : 0); - drvinfo->testinfo_len = 0; - drvinfo->regdump_len = 0; - drvinfo->eedump_len = 0; } /* diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index cba3d9fcb465..77d26fe286c0 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -899,7 +899,6 @@ static void cpmac_get_drvinfo(struct net_device *dev, strlcpy(info->driver, "cpmac", sizeof(info->driver)); strlcpy(info->version, CPMAC_VERSION, sizeof(info->version)); snprintf(info->bus_info, sizeof(info->bus_info), "%s", "cpmac"); - info->regdump_len = 0; } static const struct ethtool_ops cpmac_ethtool_ops = { diff --git a/drivers/net/ethernet/ti/cpsw-common.c b/drivers/net/ethernet/ti/cpsw-common.c index f59509486113..c08be62bceba 100644 --- a/drivers/net/ethernet/ti/cpsw-common.c +++ b/drivers/net/ethernet/ti/cpsw-common.c @@ -19,11 +19,38 @@ #include "cpsw.h" -#define AM33XX_CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id)) -#define AM33XX_CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4) +#define CTRL_MAC_LO_REG(offset, id) ((offset) + 0x8 * (id)) +#define CTRL_MAC_HI_REG(offset, id) ((offset) + 0x8 * (id) + 0x4) -int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, - u8 *mac_addr) +static int davinci_emac_3517_get_macid(struct device *dev, u16 offset, + int slave, u8 *mac_addr) +{ + u32 macid_lsb; + u32 macid_msb; + struct regmap *syscon; + + syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); + if (IS_ERR(syscon)) { + if (PTR_ERR(syscon) == -ENODEV) + return 0; + return PTR_ERR(syscon); + } + + regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lsb); + regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_msb); + + mac_addr[0] = (macid_msb >> 16) & 0xff; + mac_addr[1] = (macid_msb >> 8) & 0xff; + mac_addr[2] = macid_msb & 0xff; + mac_addr[3] = (macid_lsb >> 16) & 0xff; + mac_addr[4] = (macid_lsb >> 8) & 0xff; + mac_addr[5] = macid_lsb & 0xff; + + return 0; +} + +static int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, + u8 *mac_addr) { u32 macid_lo; u32 macid_hi; @@ -36,10 +63,8 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, return PTR_ERR(syscon); } - regmap_read(syscon, AM33XX_CTRL_MAC_LO_REG(offset, slave), - &macid_lo); - regmap_read(syscon, AM33XX_CTRL_MAC_HI_REG(offset, slave), - &macid_hi); + regmap_read(syscon, CTRL_MAC_LO_REG(offset, slave), &macid_lo); + regmap_read(syscon, CTRL_MAC_HI_REG(offset, slave), &macid_hi); mac_addr[5] = (macid_lo >> 8) & 0xff; mac_addr[4] = macid_lo & 0xff; @@ -50,6 +75,27 @@ int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, return 0; } -EXPORT_SYMBOL_GPL(cpsw_am33xx_cm_get_macid); + +int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr) +{ + if (of_machine_is_compatible("ti,am33xx")) + return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); + + if (of_device_is_compatible(dev->of_node, "ti,am3517-emac")) + return davinci_emac_3517_get_macid(dev, 0x110, slave, mac_addr); + + if (of_device_is_compatible(dev->of_node, "ti,dm816-emac")) + return cpsw_am33xx_cm_get_macid(dev, 0x30, slave, mac_addr); + + if (of_machine_is_compatible("ti,am4372")) + return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr); + + if (of_machine_is_compatible("ti,dra7")) + return davinci_emac_3517_get_macid(dev, 0x514, slave, mac_addr); + + dev_err(dev, "incompatible machine/device type for reading mac address\n"); + return -ENOENT; +} +EXPORT_SYMBOL_GPL(ti_cm_get_macid); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/ti/cpsw-phy-sel.c b/drivers/net/ethernet/ti/cpsw-phy-sel.c index 0ea78326cc21..e9cc61e1ec74 100644 --- a/drivers/net/ethernet/ti/cpsw-phy-sel.c +++ b/drivers/net/ethernet/ti/cpsw-phy-sel.c @@ -2,6 +2,8 @@ * * Copyright (C) 2013 Texas Instruments * + * Module Author: Mugunthan V N <mugunthanvnm@ti.com> + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -13,7 +15,7 @@ */ #include <linux/platform_device.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/netdevice.h> #include <linux/phy.h> #include <linux/of.h> @@ -173,7 +175,6 @@ static const struct of_device_id cpsw_phy_sel_id_table[] = { }, {} }; -MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table); static int cpsw_phy_sel_probe(struct platform_device *pdev) { @@ -214,7 +215,4 @@ static struct platform_driver cpsw_phy_sel_driver = { .of_match_table = cpsw_phy_sel_id_table, }, }; - -module_platform_driver(cpsw_phy_sel_driver); -MODULE_AUTHOR("Mugunthan V N <mugunthanvnm@ti.com>"); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver(cpsw_phy_sel_driver); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 8fc90f1c872c..3b75adfb3f37 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -29,6 +29,7 @@ #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/pm_runtime.h> +#include <linux/gpio.h> #include <linux/of.h> #include <linux/of_net.h> #include <linux/of_device.h> @@ -1783,7 +1784,6 @@ static void cpsw_get_drvinfo(struct net_device *ndev, strlcpy(info->driver, "cpsw", sizeof(info->driver)); strlcpy(info->version, "1.0", sizeof(info->version)); strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info)); - info->regdump_len = cpsw_get_regs_len(ndev); } static u32 cpsw_get_msglevel(struct net_device *ndev) @@ -2057,13 +2057,10 @@ no_phy_slave: if (mac_addr) { memcpy(slave_data->mac_addr, mac_addr, ETH_ALEN); } else { - if (of_machine_is_compatible("ti,am33xx")) { - ret = cpsw_am33xx_cm_get_macid(&pdev->dev, - 0x630, i, - slave_data->mac_addr); - if (ret) - return ret; - } + ret = ti_cm_get_macid(&pdev->dev, i, + slave_data->mac_addr); + if (ret) + return ret; } if (data->dual_emac) { if (of_property_read_u32(slave_node, "dual_emac_res_vlan", @@ -2207,6 +2204,7 @@ static int cpsw_probe(struct platform_device *pdev) void __iomem *ss_regs; struct resource *res, *ss_res; const struct of_device_id *of_id; + struct gpio_descs *mode; u32 slave_offset, sliver_offset, slave_size; int ret = 0, i; int irq; @@ -2232,6 +2230,13 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_ndev_ret; } + mode = devm_gpiod_get_array_optional(&pdev->dev, "mode", GPIOD_OUT_LOW); + if (IS_ERR(mode)) { + ret = PTR_ERR(mode); + dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret); + goto clean_ndev_ret; + } + /* * This may be required here for child devices. */ diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index ca90efafd156..442a7038e660 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -41,7 +41,6 @@ struct cpsw_platform_data { }; void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave); -int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, - u8 *mac_addr); +int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr); #endif /* __CPSW_H__ */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index a21c77bc1b27..33bd3b902304 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1861,8 +1861,12 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) pdata->no_bd_ram = of_property_read_bool(np, "ti,davinci-no-bd-ram"); priv->phy_node = of_parse_phandle(np, "phy-handle", 0); - if (!priv->phy_node) - pdata->phy_id = NULL; + if (!priv->phy_node) { + if (!of_phy_is_fixed_link(np)) + pdata->phy_id = NULL; + else if (of_phy_register_fixed_link(np) >= 0) + priv->phy_node = of_node_get(np); + } auxdata = pdev->dev.platform_data; if (auxdata) { @@ -1882,51 +1886,13 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) return pdata; } -static int davinci_emac_3517_get_macid(struct device *dev, u16 offset, - int slave, u8 *mac_addr) -{ - u32 macid_lsb; - u32 macid_msb; - struct regmap *syscon; - - syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); - if (IS_ERR(syscon)) { - if (PTR_ERR(syscon) == -ENODEV) - return 0; - return PTR_ERR(syscon); - } - - regmap_read(syscon, offset, &macid_lsb); - regmap_read(syscon, offset + 4, &macid_msb); - - mac_addr[0] = (macid_msb >> 16) & 0xff; - mac_addr[1] = (macid_msb >> 8) & 0xff; - mac_addr[2] = macid_msb & 0xff; - mac_addr[3] = (macid_lsb >> 16) & 0xff; - mac_addr[4] = (macid_lsb >> 8) & 0xff; - mac_addr[5] = macid_lsb & 0xff; - - return 0; -} - static int davinci_emac_try_get_mac(struct platform_device *pdev, int instance, u8 *mac_addr) { - int error = -EINVAL; - if (!pdev->dev.of_node) - return error; - - if (of_device_is_compatible(pdev->dev.of_node, "ti,am3517-emac")) - error = davinci_emac_3517_get_macid(&pdev->dev, 0x110, - 0, mac_addr); - else if (of_device_is_compatible(pdev->dev.of_node, - "ti,dm816-emac")) - error = cpsw_am33xx_cm_get_macid(&pdev->dev, 0x30, - instance, - mac_addr); - - return error; + return -EINVAL; + + return ti_cm_get_macid(&pdev->dev, instance, mac_addr); } /** diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 691ec936e88d..a274cd49afe9 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -791,7 +791,6 @@ static void tlan_get_drvinfo(struct net_device *dev, sizeof(info->bus_info)); else strlcpy(info->bus_info, "EISA", sizeof(info->bus_info)); - info->eedump_len = TLAN_EEPROM_SIZE; } static int tlan_get_eeprom_len(struct net_device *dev) diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c index 8cf9d4f56bb2..415de1eaf641 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c +++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c @@ -59,16 +59,15 @@ static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) int temac_mdio_setup(struct temac_local *lp, struct device_node *np) { struct mii_bus *bus; - const u32 *bus_hz; + u32 bus_hz; int clk_div; - int rc, size; + int rc; struct resource res; /* Calculate a reasonable divisor for the clock rate */ clk_div = 0x3f; /* worst-case default setting */ - bus_hz = of_get_property(np, "clock-frequency", &size); - if (bus_hz && size >= sizeof(*bus_hz)) { - clk_div = (*bus_hz) / (2500 * 1000 * 2) - 1; + if (of_property_read_u32(np, "clock-frequency", &bus_hz) == 0) { + clk_div = bus_hz / (2500 * 1000 * 2) - 1; if (clk_div < 1) clk_div = 1; if (clk_div > 0x3f) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d95f9aae95e7..4684644703cc 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1135,7 +1135,6 @@ static void axienet_ethtools_get_drvinfo(struct net_device *ndev, { strlcpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); strlcpy(ed->version, DRIVER_VERSION, sizeof(ed->version)); - ed->regdump_len = sizeof(u32) * AXIENET_REGS_N; } /** diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 2a5a16834c01..507bbb0355c2 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -129,7 +129,6 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) { int ret; u32 clk_div, host_clock; - u32 *property_p; struct mii_bus *bus; struct resource res; struct device_node *np1; @@ -168,8 +167,7 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) clk_div = DEFAULT_CLOCK_DIVISOR; goto issue; } - property_p = (u32 *) of_get_property(np1, "clock-frequency", NULL); - if (!property_p) { + if (of_property_read_u32(np1, "clock-frequency", &host_clock)) { netdev_warn(lp->ndev, "clock-frequency property not found.\n"); netdev_warn(lp->ndev, "Setting MDIO clock divisor to default %d\n", @@ -179,7 +177,6 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) goto issue; } - host_clock = be32_to_cpup(property_p); clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; /* If there is any remainder from the division of * fHOST / (MAX_MDIO_FREQ * 2), then we need to add |