diff options
Diffstat (limited to 'drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c')
-rw-r--r-- | drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 244 |
1 files changed, 197 insertions, 47 deletions
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index d8e5241097a9..c51f1d5b550b 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -56,9 +56,9 @@ static struct { /* The prototype is added here to be used in start_dev when using ACPI. This * will be removed once phylink is used for all modes (dt+ACPI). */ -static void mvpp2_mac_config(struct net_device *dev, unsigned int mode, +static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state); -static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode, +static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, phy_interface_t interface, struct phy_device *phy); /* Queue modes */ @@ -1258,6 +1258,17 @@ static u64 mvpp2_read_count(struct mvpp2_port *port, return val; } +/* Some counters are accessed indirectly by first writing an index to + * MVPP2_CTRS_IDX. The index can represent various resources depending on the + * register we access, it can be a hit counter for some classification tables, + * a counter specific to a rxq, a txq or a buffer pool. + */ +static u32 mvpp2_read_index(struct mvpp2 *priv, u32 index, u32 reg) +{ + mvpp2_write(priv, MVPP2_CTRS_IDX, index); + return mvpp2_read(priv, reg); +} + /* Due to the fact that software statistics and hardware statistics are, by * design, incremented at different moments in the chain of packet processing, * it is very likely that incoming packets could have been dropped after being @@ -1267,7 +1278,7 @@ static u64 mvpp2_read_count(struct mvpp2_port *port, * Hence, statistics gathered from userspace with ifconfig (software) and * ethtool (hardware) cannot be compared. */ -static const struct mvpp2_ethtool_counter mvpp2_ethtool_regs[] = { +static const struct mvpp2_ethtool_counter mvpp2_ethtool_mib_regs[] = { { MVPP2_MIB_GOOD_OCTETS_RCVD, "good_octets_received", true }, { MVPP2_MIB_BAD_OCTETS_RCVD, "bad_octets_received" }, { MVPP2_MIB_CRC_ERRORS_SENT, "crc_errors_sent" }, @@ -1297,31 +1308,114 @@ static const struct mvpp2_ethtool_counter mvpp2_ethtool_regs[] = { { MVPP2_MIB_LATE_COLLISION, "late_collision" }, }; +static const struct mvpp2_ethtool_counter mvpp2_ethtool_port_regs[] = { + { MVPP2_OVERRUN_ETH_DROP, "rx_fifo_or_parser_overrun_drops" }, + { MVPP2_CLS_ETH_DROP, "rx_classifier_drops" }, +}; + +static const struct mvpp2_ethtool_counter mvpp2_ethtool_txq_regs[] = { + { MVPP2_TX_DESC_ENQ_CTR, "txq_%d_desc_enqueue" }, + { MVPP2_TX_DESC_ENQ_TO_DDR_CTR, "txq_%d_desc_enqueue_to_ddr" }, + { MVPP2_TX_BUFF_ENQ_TO_DDR_CTR, "txq_%d_buff_euqueue_to_ddr" }, + { MVPP2_TX_DESC_ENQ_HW_FWD_CTR, "txq_%d_desc_hardware_forwarded" }, + { MVPP2_TX_PKTS_DEQ_CTR, "txq_%d_packets_dequeued" }, + { MVPP2_TX_PKTS_FULL_QUEUE_DROP_CTR, "txq_%d_queue_full_drops" }, + { MVPP2_TX_PKTS_EARLY_DROP_CTR, "txq_%d_packets_early_drops" }, + { MVPP2_TX_PKTS_BM_DROP_CTR, "txq_%d_packets_bm_drops" }, + { MVPP2_TX_PKTS_BM_MC_DROP_CTR, "txq_%d_packets_rep_bm_drops" }, +}; + +static const struct mvpp2_ethtool_counter mvpp2_ethtool_rxq_regs[] = { + { MVPP2_RX_DESC_ENQ_CTR, "rxq_%d_desc_enqueue" }, + { MVPP2_RX_PKTS_FULL_QUEUE_DROP_CTR, "rxq_%d_queue_full_drops" }, + { MVPP2_RX_PKTS_EARLY_DROP_CTR, "rxq_%d_packets_early_drops" }, + { MVPP2_RX_PKTS_BM_DROP_CTR, "rxq_%d_packets_bm_drops" }, +}; + +#define MVPP2_N_ETHTOOL_STATS(ntxqs, nrxqs) (ARRAY_SIZE(mvpp2_ethtool_mib_regs) + \ + ARRAY_SIZE(mvpp2_ethtool_port_regs) + \ + (ARRAY_SIZE(mvpp2_ethtool_txq_regs) * (ntxqs)) + \ + (ARRAY_SIZE(mvpp2_ethtool_rxq_regs) * (nrxqs))) + static void mvpp2_ethtool_get_strings(struct net_device *netdev, u32 sset, u8 *data) { - if (sset == ETH_SS_STATS) { - int i; + struct mvpp2_port *port = netdev_priv(netdev); + int i, q; - for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_regs); i++) - strscpy(data + i * ETH_GSTRING_LEN, - mvpp2_ethtool_regs[i].string, ETH_GSTRING_LEN); + if (sset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++) { + strscpy(data, mvpp2_ethtool_mib_regs[i].string, + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_port_regs); i++) { + strscpy(data, mvpp2_ethtool_port_regs[i].string, + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + for (q = 0; q < port->ntxqs; q++) { + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++) { + snprintf(data, ETH_GSTRING_LEN, + mvpp2_ethtool_txq_regs[i].string, q); + data += ETH_GSTRING_LEN; + } + } + + for (q = 0; q < port->nrxqs; q++) { + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++) { + snprintf(data, ETH_GSTRING_LEN, + mvpp2_ethtool_rxq_regs[i].string, + q); + data += ETH_GSTRING_LEN; + } } } +static void mvpp2_read_stats(struct mvpp2_port *port) +{ + u64 *pstats; + int i, q; + + pstats = port->ethtool_stats; + + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_mib_regs); i++) + *pstats++ += mvpp2_read_count(port, &mvpp2_ethtool_mib_regs[i]); + + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_port_regs); i++) + *pstats++ += mvpp2_read(port->priv, + mvpp2_ethtool_port_regs[i].offset + + 4 * port->id); + + for (q = 0; q < port->ntxqs; q++) + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_txq_regs); i++) + *pstats++ += mvpp2_read_index(port->priv, + MVPP22_CTRS_TX_CTR(port->id, i), + mvpp2_ethtool_txq_regs[i].offset); + + /* Rxqs are numbered from 0 from the user standpoint, but not from the + * driver's. We need to add the port->first_rxq offset. + */ + for (q = 0; q < port->nrxqs; q++) + for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_rxq_regs); i++) + *pstats++ += mvpp2_read_index(port->priv, + port->first_rxq + i, + mvpp2_ethtool_rxq_regs[i].offset); +} + static void mvpp2_gather_hw_statistics(struct work_struct *work) { struct delayed_work *del_work = to_delayed_work(work); struct mvpp2_port *port = container_of(del_work, struct mvpp2_port, stats_work); - u64 *pstats; - int i; mutex_lock(&port->gather_stats_lock); - pstats = port->ethtool_stats; - for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_regs); i++) - *pstats++ += mvpp2_read_count(port, &mvpp2_ethtool_regs[i]); + mvpp2_read_stats(port); /* No need to read again the counters right after this function if it * was called asynchronously by the user (ie. use of ethtool). @@ -1345,27 +1439,24 @@ static void mvpp2_ethtool_get_stats(struct net_device *dev, mutex_lock(&port->gather_stats_lock); memcpy(data, port->ethtool_stats, - sizeof(u64) * ARRAY_SIZE(mvpp2_ethtool_regs)); + sizeof(u64) * MVPP2_N_ETHTOOL_STATS(port->ntxqs, port->nrxqs)); mutex_unlock(&port->gather_stats_lock); } static int mvpp2_ethtool_get_sset_count(struct net_device *dev, int sset) { + struct mvpp2_port *port = netdev_priv(dev); + if (sset == ETH_SS_STATS) - return ARRAY_SIZE(mvpp2_ethtool_regs); + return MVPP2_N_ETHTOOL_STATS(port->ntxqs, port->nrxqs); return -EOPNOTSUPP; } static void mvpp2_mac_reset_assert(struct mvpp2_port *port) { - unsigned int i; u32 val; - /* Read the GOP statistics to reset the hardware counters */ - for (i = 0; i < ARRAY_SIZE(mvpp2_ethtool_regs); i++) - mvpp2_read_count(port, &mvpp2_ethtool_regs[i]); - val = readl(port->base + MVPP2_GMAC_CTRL_2_REG) | MVPP2_GMAC_PORT_RESET_MASK; writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); @@ -3237,9 +3328,9 @@ static void mvpp2_start_dev(struct mvpp2_port *port) struct phylink_link_state state = { .interface = port->phy_interface, }; - mvpp2_mac_config(port->dev, MLO_AN_INBAND, &state); - mvpp2_mac_link_up(port->dev, MLO_AN_INBAND, port->phy_interface, - NULL); + mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state); + mvpp2_mac_link_up(&port->phylink_config, MLO_AN_INBAND, + port->phy_interface, NULL); } netif_tx_start_all_queues(port->dev); @@ -3954,7 +4045,7 @@ static int mvpp2_ethtool_get_rxnfc(struct net_device *dev, ret = mvpp2_ethtool_cls_rule_get(port, info); break; case ETHTOOL_GRXCLSRLALL: - for (i = 0; i < MVPP2_N_RFS_RULES; i++) { + for (i = 0; i < MVPP2_N_RFS_ENTRIES_PER_FLOW; i++) { if (port->rfs_rules[i]) rules[loc++] = i; } @@ -4000,24 +4091,25 @@ static int mvpp2_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, u8 *hfunc) { struct mvpp2_port *port = netdev_priv(dev); + int ret = 0; if (!mvpp22_rss_is_supported()) return -EOPNOTSUPP; if (indir) - memcpy(indir, port->indir, - ARRAY_SIZE(port->indir) * sizeof(port->indir[0])); + ret = mvpp22_port_rss_ctx_indir_get(port, 0, indir); if (hfunc) *hfunc = ETH_RSS_HASH_CRC32; - return 0; + return ret; } static int mvpp2_ethtool_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key, const u8 hfunc) { struct mvpp2_port *port = netdev_priv(dev); + int ret = 0; if (!mvpp22_rss_is_supported()) return -EOPNOTSUPP; @@ -4028,15 +4120,58 @@ static int mvpp2_ethtool_set_rxfh(struct net_device *dev, const u32 *indir, if (key) return -EOPNOTSUPP; - if (indir) { - memcpy(port->indir, indir, - ARRAY_SIZE(port->indir) * sizeof(port->indir[0])); - mvpp22_rss_fill_table(port, port->id); - } + if (indir) + ret = mvpp22_port_rss_ctx_indir_set(port, 0, indir); - return 0; + return ret; +} + +static int mvpp2_ethtool_get_rxfh_context(struct net_device *dev, u32 *indir, + u8 *key, u8 *hfunc, u32 rss_context) +{ + struct mvpp2_port *port = netdev_priv(dev); + int ret = 0; + + if (!mvpp22_rss_is_supported()) + return -EOPNOTSUPP; + + if (hfunc) + *hfunc = ETH_RSS_HASH_CRC32; + + if (indir) + ret = mvpp22_port_rss_ctx_indir_get(port, rss_context, indir); + + return ret; } +static int mvpp2_ethtool_set_rxfh_context(struct net_device *dev, + const u32 *indir, const u8 *key, + const u8 hfunc, u32 *rss_context, + bool delete) +{ + struct mvpp2_port *port = netdev_priv(dev); + int ret; + + if (!mvpp22_rss_is_supported()) + return -EOPNOTSUPP; + + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_CRC32) + return -EOPNOTSUPP; + + if (key) + return -EOPNOTSUPP; + + if (delete) + return mvpp22_port_rss_ctx_delete(port, *rss_context); + + if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) { + ret = mvpp22_port_rss_ctx_create(port, rss_context); + if (ret) + return ret; + } + + return mvpp22_port_rss_ctx_indir_set(port, *rss_context, indir); +} /* Device ops */ static const struct net_device_ops mvpp2_netdev_ops = { @@ -4073,7 +4208,8 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = { .get_rxfh_indir_size = mvpp2_ethtool_get_rxfh_indir_size, .get_rxfh = mvpp2_ethtool_get_rxfh, .set_rxfh = mvpp2_ethtool_set_rxfh, - + .get_rxfh_context = mvpp2_ethtool_get_rxfh_context, + .set_rxfh_context = mvpp2_ethtool_set_rxfh_context, }; /* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that @@ -4327,6 +4463,11 @@ static int mvpp2_port_init(struct mvpp2_port *port) if (err) goto err_free_percpu; + /* Clear all port stats */ + mvpp2_read_stats(port); + memset(port->ethtool_stats, 0, + MVPP2_N_ETHTOOL_STATS(port->ntxqs, port->nrxqs) * sizeof(u64)); + return 0; err_free_percpu: @@ -4416,11 +4557,12 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, eth_hw_addr_random(dev); } -static void mvpp2_phylink_validate(struct net_device *dev, +static void mvpp2_phylink_validate(struct phylink_config *config, unsigned long *supported, struct phylink_link_state *state) { - struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port *port = container_of(config, struct mvpp2_port, + phylink_config); __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; /* Invalid combinations */ @@ -4544,10 +4686,11 @@ static void mvpp2_gmac_link_state(struct mvpp2_port *port, state->pause |= MLO_PAUSE_TX; } -static int mvpp2_phylink_mac_link_state(struct net_device *dev, +static int mvpp2_phylink_mac_link_state(struct phylink_config *config, struct phylink_link_state *state) { - struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port *port = container_of(config, struct mvpp2_port, + phylink_config); if (port->priv->hw_version == MVPP22 && port->gop_id == 0) { u32 mode = readl(port->base + MVPP22_XLG_CTRL3_REG); @@ -4563,9 +4706,10 @@ static int mvpp2_phylink_mac_link_state(struct net_device *dev, return 1; } -static void mvpp2_mac_an_restart(struct net_device *dev) +static void mvpp2_mac_an_restart(struct phylink_config *config) { - struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port *port = container_of(config, struct mvpp2_port, + phylink_config); u32 val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); writel(val | MVPP2_GMAC_IN_BAND_RESTART_AN, @@ -4750,9 +4894,10 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, } } -static void mvpp2_mac_config(struct net_device *dev, unsigned int mode, +static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { + struct net_device *dev = to_net_dev(config->dev); struct mvpp2_port *port = netdev_priv(dev); bool change_interface = port->phy_interface != state->interface; @@ -4792,9 +4937,10 @@ static void mvpp2_mac_config(struct net_device *dev, unsigned int mode, mvpp2_port_enable(port); } -static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode, +static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, phy_interface_t interface, struct phy_device *phy) { + struct net_device *dev = to_net_dev(config->dev); struct mvpp2_port *port = netdev_priv(dev); u32 val; @@ -4819,9 +4965,10 @@ static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode, netif_tx_wake_all_queues(dev); } -static void mvpp2_mac_link_down(struct net_device *dev, unsigned int mode, - phy_interface_t interface) +static void mvpp2_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) { + struct net_device *dev = to_net_dev(config->dev); struct mvpp2_port *port = netdev_priv(dev); u32 val; @@ -5002,7 +5149,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, } port->ethtool_stats = devm_kcalloc(&pdev->dev, - ARRAY_SIZE(mvpp2_ethtool_regs), + MVPP2_N_ETHTOOL_STATS(ntxqs, nrxqs), sizeof(u64), GFP_KERNEL); if (!port->ethtool_stats) { err = -ENOMEM; @@ -5078,8 +5225,11 @@ static int mvpp2_port_probe(struct platform_device *pdev, /* Phylink isn't used w/ ACPI as of now */ if (port_node) { - phylink = phylink_create(dev, port_fwnode, phy_mode, - &mvpp2_phylink_ops); + port->phylink_config.dev = &dev->dev; + port->phylink_config.type = PHYLINK_NETDEV; + + phylink = phylink_create(&port->phylink_config, port_fwnode, + phy_mode, &mvpp2_phylink_ops); if (IS_ERR(phylink)) { err = PTR_ERR(phylink); goto err_free_port_pcpu; |