diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index b66f1c14..b269840b 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -364,7 +364,8 @@ static irqreturn_t ether_tx_chan_isr(int irq, void *data) if (likely(napi_schedule_prep(&tx_napi->napi))) { __napi_schedule_irqoff(&tx_napi->napi); - } else { + /* NAPI may get scheduled when tx_usecs is enabled */ + } else if (osi_dma->use_tx_usecs == OSI_DISABLE) { pr_err("Tx DMA-%d IRQ when NAPI already scheduled!\n", chan); WARN_ON(true); } @@ -1363,7 +1364,11 @@ static inline void ether_reset_stats(struct ether_priv_data *pdata) static int ether_close(struct net_device *ndev) { struct ether_priv_data *pdata = netdev_priv(ndev); - int ret = 0; + int ret = 0, i; + + /* Cancel hrtimer */ + for (i = 0; i < pdata->osi_dma->num_dma_chans; i++) + hrtimer_cancel(&pdata->tx_napi[i]->tx_usecs_timer); /* Unregister broadcasting MAC timestamp to clients */ tegra_unregister_hwtime_source(); @@ -1766,6 +1771,15 @@ static int ether_start_xmit(struct sk_buff *skb, struct net_device *ndev) netdev_dbg(ndev, "Tx ring[%d] insufficient desc.\n", chan); } + if (osi_dma->use_tx_usecs == OSI_ENABLE && + atomic_read(&pdata->tx_napi[chan]->tx_usecs_timer_armed) == + OSI_DISABLE) { + atomic_set(&pdata->tx_napi[chan]->tx_usecs_timer_armed, + OSI_ENABLE); + hrtimer_start(&pdata->tx_napi[chan]->tx_usecs_timer, + osi_dma->tx_usecs * NSEC_PER_USEC, + HRTIMER_MODE_REL); + } return NETDEV_TX_OK; } @@ -2355,6 +2369,17 @@ static int ether_napi_poll_tx(struct napi_struct *napi, int budget) int processed; processed = osi_process_tx_completions(osi_dma, chan); + + /* re-arm the timer if tx ring is not empty */ + if (!osi_txring_empty(osi_dma, chan) && + osi_dma->use_tx_usecs == OSI_ENABLE && + atomic_read(&tx_napi->tx_usecs_timer_armed) == OSI_DISABLE) { + atomic_set(&tx_napi->tx_usecs_timer_armed, OSI_ENABLE); + hrtimer_start(&tx_napi->tx_usecs_timer, + osi_dma->tx_usecs * NSEC_PER_USEC, + HRTIMER_MODE_REL); + } + if (processed == 0) { napi_complete(napi); spin_lock_irqsave(&pdata->rlock, flags); @@ -2366,6 +2391,26 @@ static int ether_napi_poll_tx(struct napi_struct *napi, int budget) return budget; } +static enum hrtimer_restart ether_tx_usecs_hrtimer(struct hrtimer *data) +{ + struct ether_tx_napi *tx_napi = container_of(data, struct ether_tx_napi, + tx_usecs_timer); + struct ether_priv_data *pdata = tx_napi->pdata; + struct osi_core_priv_data *osi_core = pdata->osi_core; + unsigned long val; + + val = osi_core->xstats.tx_usecs_swtimer_n[tx_napi->chan]; + osi_core->xstats.tx_usecs_swtimer_n[tx_napi->chan] = + osi_update_stats_counter(val, 1U); + + atomic_set(&pdata->tx_napi[tx_napi->chan]->tx_usecs_timer_armed, + OSI_DISABLE); + if (likely(napi_schedule_prep(&tx_napi->napi))) + __napi_schedule_irqoff(&tx_napi->napi); + + return HRTIMER_NORESTART; +} + /** * @brief Allocate NAPI resources. * @@ -3236,6 +3281,44 @@ static int ether_parse_dt(struct ether_priv_data *pdata) } } + /* tx_usecs value to be set */ + ret = of_property_read_u32(np, "nvidia,tx_usecs", &osi_dma->tx_usecs); + if (ret < 0) { + osi_dma->use_tx_usecs = OSI_DISABLE; + } else { + if (osi_dma->tx_usecs > OSI_MAX_TX_COALESCE_USEC || + osi_dma->tx_usecs < OSI_MIN_TX_COALESCE_USEC) { + dev_err(dev, + "invalid tx_riwt, must be inrange %d to %d\n", + OSI_MIN_TX_COALESCE_USEC, + OSI_MAX_TX_COALESCE_USEC); + return -EINVAL; + } + osi_dma->use_tx_usecs = OSI_ENABLE; + } + /* tx_frames value to be set */ + ret = of_property_read_u32(np, "nvidia,tx_frames", + &osi_dma->tx_frames); + if (ret < 0) { + osi_dma->use_tx_frames = OSI_DISABLE; + } else { + if (osi_dma->tx_frames > ETHER_TX_MAX_FRAME || + osi_dma->tx_frames < OSI_MIN_TX_COALESCE_FRAMES) { + dev_err(dev, + "invalid tx-frames, must be inrange %d to %ld", + OSI_MIN_TX_COALESCE_FRAMES, ETHER_TX_MAX_FRAME); + return -EINVAL; + } + osi_dma->use_tx_frames = OSI_ENABLE; + } + + if (osi_dma->use_tx_usecs == OSI_DISABLE && + osi_dma->use_tx_frames == OSI_ENABLE) { + dev_err(dev, "invalid settings : tx_frames must be enabled" + " along with tx_usecs in DT\n"); + return -EINVAL; + } + /* RIWT value to be set */ ret = of_property_read_u32(np, "nvidia,rx_riwt", &osi_dma->rx_riwt); if (ret < 0) { @@ -3499,7 +3582,7 @@ static int ether_probe(struct platform_device *pdev) struct osi_core_priv_data *osi_core; struct osi_dma_priv_data *osi_dma; struct net_device *ndev; - int ret = 0; + int ret = 0, i; ether_get_num_dma_chan_mtl_q(pdev, &num_dma_chans, &mac, &num_mtl_queues); @@ -3609,6 +3692,16 @@ static int ether_probe(struct platform_device *pdev) goto err_napi; } + /* Setup the tx_usecs timer */ + for (i = 0; i < osi_dma->num_dma_chans; i++) { + atomic_set(&pdata->tx_napi[i]->tx_usecs_timer_armed, + OSI_DISABLE); + hrtimer_init(&pdata->tx_napi[i]->tx_usecs_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pdata->tx_napi[i]->tx_usecs_timer.function = + ether_tx_usecs_hrtimer; + } + /* Register sysfs entry */ ret = ether_sysfs_register(pdata->dev); if (ret < 0) { diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 998f4b19..97f281d6 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -137,6 +138,7 @@ */ #define ETHER_TX_DESC_THRESHOLD (MAX_SKB_FRAGS + ETHER_TX_MAX_SPLIT + 2) +#define ETHER_TX_MAX_FRAME (TX_DESC_CNT / ETHER_TX_DESC_THRESHOLD) /** *@brief Returns count of available transmit descriptors * @@ -178,6 +180,10 @@ struct ether_tx_napi { struct ether_priv_data *pdata; /** NAPI instance associated with transmit channel */ struct napi_struct napi; + /** SW timer associated with transmit channel */ + struct hrtimer tx_usecs_timer; + /** SW timer flag associated with transmit channel */ + atomic_t tx_usecs_timer_armed; }; /** diff --git a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c index 09667fbd..ebbf2682 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c @@ -116,6 +116,10 @@ static const struct eqos_stats eqos_gstrings_stats[] = { EQOS_EXTRA_STAT(tx_normal_irq_n[1]), EQOS_EXTRA_STAT(tx_normal_irq_n[2]), EQOS_EXTRA_STAT(tx_normal_irq_n[3]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[0]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[1]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[2]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[3]), EQOS_EXTRA_STAT(rx_normal_irq_n[0]), EQOS_EXTRA_STAT(rx_normal_irq_n[1]), EQOS_EXTRA_STAT(rx_normal_irq_n[2]), @@ -579,11 +583,42 @@ static int ether_set_coalesce(struct net_device *dev, (ec->rx_max_coalesced_frames_high) || (ec->tx_max_coalesced_frames_irq) || (ec->stats_block_coalesce_usecs) || - (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval) || - (ec->tx_coalesce_usecs) || (ec->tx_max_coalesced_frames)) { + (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval)) { return -EOPNOTSUPP; } + if (ec->tx_max_coalesced_frames == OSI_DISABLE) { + osi_dma->use_tx_frames = OSI_DISABLE; + } else if ((ec->tx_max_coalesced_frames > ETHER_TX_MAX_FRAME) || + (ec->tx_max_coalesced_frames < OSI_MIN_TX_COALESCE_FRAMES)) { + netdev_err(dev, + "invalid tx-frames, must be in the range of" + " %d to %ld frames\n", OSI_MIN_TX_COALESCE_FRAMES, + ETHER_TX_MAX_FRAME); + return -EINVAL; + } else { + osi_dma->use_tx_frames = OSI_ENABLE; + } + + if (ec->tx_coalesce_usecs == OSI_DISABLE) { + osi_dma->use_tx_usecs = OSI_DISABLE; + } else if ((ec->tx_coalesce_usecs > OSI_MAX_TX_COALESCE_USEC) || + (ec->tx_coalesce_usecs < OSI_MIN_TX_COALESCE_USEC)) { + netdev_err(dev, + "invalid tx_usecs, must be in a range of" + " %d to %d usec\n", OSI_MIN_TX_COALESCE_USEC, + OSI_MAX_TX_COALESCE_USEC); + return -EINVAL; + } else { + osi_dma->use_tx_usecs = OSI_ENABLE; + } + + netdev_err(dev, "TX COALESCING USECS is %s\n", osi_dma->use_tx_usecs ? + "ENABLED" : "DISABLED"); + + netdev_err(dev, "TX COALESCING FRAMES is %s\n", osi_dma->use_tx_frames ? + "ENABLED" : "DISABLED"); + if (ec->rx_max_coalesced_frames == OSI_DISABLE) { osi_dma->use_rx_frames = OSI_DISABLE; } else if ((ec->rx_max_coalesced_frames > RX_DESC_CNT) || @@ -610,6 +645,12 @@ static int ether_set_coalesce(struct net_device *dev, osi_dma->use_riwt = OSI_ENABLE; } + if (osi_dma->use_tx_usecs == OSI_DISABLE && + osi_dma->use_tx_frames == OSI_ENABLE) { + netdev_err(dev, "invalid settings : tx-frames must be enabled" + " along with tx-usecs\n"); + return -EINVAL; + } if (osi_dma->use_riwt == OSI_DISABLE && osi_dma->use_rx_frames == OSI_ENABLE) { netdev_err(dev, "invalid settings : rx-frames must be enabled" @@ -624,6 +665,8 @@ static int ether_set_coalesce(struct net_device *dev, osi_dma->rx_riwt = ec->rx_coalesce_usecs; osi_dma->rx_frames = ec->rx_max_coalesced_frames; + osi_dma->tx_usecs = ec->tx_coalesce_usecs; + osi_dma->tx_frames = ec->tx_max_coalesced_frames; return 0; } @@ -651,6 +694,8 @@ static int ether_get_coalesce(struct net_device *dev, memset(ec, 0, sizeof(struct ethtool_coalesce)); ec->rx_coalesce_usecs = osi_dma->rx_riwt; ec->rx_max_coalesced_frames = osi_dma->rx_frames; + ec->tx_coalesce_usecs = osi_dma->tx_usecs; + ec->tx_max_coalesced_frames = osi_dma->tx_frames; return 0; }