diff mbox series

[v4,09/10] net: eth: altera: add msgdma prefetcher

Message ID 20200708072401.169150-10-joyce.ooi@intel.com
State Changes Requested
Delegated to: David Miller
Headers show
Series net: eth: altera: tse: Add PTP and mSGDMA prefetcher | expand

Commit Message

Joyce Ooi July 8, 2020, 7:24 a.m. UTC
From: Dalon Westergreen <dalon.westergreen@intel.com>

Add support for the mSGDMA prefetcher.  The prefetcher adds support
for a linked list of descriptors in system memory.  The prefetcher
feeds these to the mSGDMA dispatcher.

The prefetcher is configured to poll for the next descriptor in the
list to be owned by hardware, then pass the descriptor to the
dispatcher.  It will then poll the next descriptor until it is
owned by hardware.

The dispatcher responses are written back to the appropriate
descriptor, and the owned by hardware bit is cleared.

The driver sets up a linked list twice the tx and rx ring sizes,
with the last descriptor pointing back to the first.  This ensures
that the ring of descriptors will always have inactive descriptors
preventing the prefetcher from looping over and reusing descriptors
inappropriately.  The prefetcher will continuously loop over these
descriptors.  The driver modifies descriptors as required to update
the skb address and length as well as the owned by hardware bit.

In addition to the above, the mSGDMA prefetcher can be used to
handle rx and tx timestamps coming from the ethernet ip.  These
can be included in the prefetcher response in the descriptor.

Signed-off-by: Dalon Westergreen <dalon.westergreen@intel.com>
Signed-off-by: Joyce Ooi <joyce.ooi@intel.com>
---
v2: minor fixes and suggested edits
v3: queue is stopped before returning NETDEV_TX_BUSY
v4: fix build warnings
---
 drivers/net/ethernet/altera/Makefile               |   2 +-
 .../net/ethernet/altera/altera_msgdma_prefetcher.c | 431 +++++++++++++++++++++
 .../net/ethernet/altera/altera_msgdma_prefetcher.h |  30 ++
 .../ethernet/altera/altera_msgdmahw_prefetcher.h   |  87 +++++
 drivers/net/ethernet/altera/altera_tse.h           |  14 +
 drivers/net/ethernet/altera/altera_tse_main.c      |  51 +++
 6 files changed, 614 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/altera/altera_msgdma_prefetcher.c
 create mode 100644 drivers/net/ethernet/altera/altera_msgdma_prefetcher.h
 create mode 100644 drivers/net/ethernet/altera/altera_msgdmahw_prefetcher.h

Comments

David Miller July 8, 2020, 5:33 p.m. UTC | #1
From: "Ooi, Joyce" <joyce.ooi@intel.com>
Date: Wed,  8 Jul 2020 15:24:00 +0800

> +int msgdma_pref_initialize(struct altera_tse_private *priv)
> +{
> +	int i;
> +	struct msgdma_pref_extended_desc *rx_descs;
> +	struct msgdma_pref_extended_desc *tx_descs;
> +	dma_addr_t rx_descsphys;
> +	dma_addr_t tx_descsphys;

Reverse christmas tree please.

> +netdev_tx_t msgdma_pref_tx_buffer(struct altera_tse_private *priv,
> +				  struct tse_buffer *buffer)
> +{
> +	u32 desc_entry = priv->tx_prod % (priv->tx_ring_size * 2);
> +	struct msgdma_pref_extended_desc *tx_descs = priv->pref_txdesc;

Likewise.

> +u32 msgdma_pref_tx_completions(struct altera_tse_private *priv)
> +{
> +	u32 control;
> +	u32 ready = 0;
> +	u32 cons = priv->tx_cons;
> +	u32 desc_ringsize = priv->tx_ring_size * 2;
> +	u32 ringsize = priv->tx_ring_size;
> +	u64 ns = 0;
> +	struct msgdma_pref_extended_desc *cur;
> +	struct tse_buffer *tx_buff;
> +	struct skb_shared_hwtstamps shhwtstamp;
> +	int i;

Likewise.

> +u32 msgdma_pref_rx_status(struct altera_tse_private *priv)
> +{
> +	u32 rxstatus = 0;
> +	u32 pktlength;
> +	u32 pktstatus;
> +	u64 ns = 0;
> +	u32 entry = priv->rx_cons % priv->rx_ring_size;
> +	u32 desc_entry = priv->rx_prod % (priv->rx_ring_size * 2);
> +	struct msgdma_pref_extended_desc *rx_descs = priv->pref_rxdesc;
> +	struct skb_shared_hwtstamps *shhwtstamp = NULL;
> +	struct tse_buffer *rx_buff = priv->rx_ring;

Likewise.

> +	} else if (priv->dmaops &&
> +			   priv->dmaops->altera_dtype ==
> +			   ALTERA_DTYPE_MSGDMA_PREF) {

This is not properly formatted.

On a multi-line conditional, every subsequent line after the first should
align with the first column after the openning parenthesis of the first
line.
Jakub Kicinski July 8, 2020, 9:49 p.m. UTC | #2
On Wed,  8 Jul 2020 15:24:00 +0800 Ooi, Joyce wrote:
> +		/* get prefetcher rx poll frequency from device tree */
> +		if (of_property_read_u32(pdev->dev.of_node, "rx-poll-freq",
> +					 &priv->rx_poll_freq)) {
> +			dev_info(&pdev->dev, "Defaulting RX Poll Frequency to 128\n");
> +			priv->rx_poll_freq = 128;
> +		}
> +
> +		/* get prefetcher rx poll frequency from device tree */
> +		if (of_property_read_u32(pdev->dev.of_node, "tx-poll-freq",
> +					 &priv->tx_poll_freq)) {
> +			dev_info(&pdev->dev, "Defaulting TX Poll Frequency to 128\n");
> +			priv->tx_poll_freq = 128;
> +		}

I'm no device tree expert but these look like config options rather than
HW description. They also don't appear to be documented in the next
patch.
Joyce Ooi July 14, 2020, 2:35 p.m. UTC | #3
> -----Original Message-----
> From: Jakub Kicinski <kuba@kernel.org>
> Sent: Thursday, July 9, 2020 5:49 AM
> To: Ooi, Joyce <joyce.ooi@intel.com>
> Cc: Thor Thayer <thor.thayer@linux.intel.com>; David S . Miller
> <davem@davemloft.net>; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; Dalon Westergreen
> <dalon.westergreen@linux.intel.com>; Tan, Ley Foon
> <ley.foon.tan@intel.com>; See, Chin Liang <chin.liang.see@intel.com>;
> Nguyen, Dinh <dinh.nguyen@intel.com>; Westergreen, Dalon
> <dalon.westergreen@intel.com>
> Subject: Re: [PATCH v4 09/10] net: eth: altera: add msgdma prefetcher
> 
> On Wed,  8 Jul 2020 15:24:00 +0800 Ooi, Joyce wrote:
> > +		/* get prefetcher rx poll frequency from device tree */
> > +		if (of_property_read_u32(pdev->dev.of_node, "rx-poll-
> freq",
> > +					 &priv->rx_poll_freq)) {
> > +			dev_info(&pdev->dev, "Defaulting RX Poll Frequency
> to 128\n");
> > +			priv->rx_poll_freq = 128;
> > +		}
> > +
> > +		/* get prefetcher rx poll frequency from device tree */
> > +		if (of_property_read_u32(pdev->dev.of_node, "tx-poll-
> freq",
> > +					 &priv->tx_poll_freq)) {
> > +			dev_info(&pdev->dev, "Defaulting TX Poll Frequency
> to 128\n");
> > +			priv->tx_poll_freq = 128;
> > +		}
> 
> I'm no device tree expert but these look like config options rather than HW
> description. They also don't appear to be documented in the next patch.

The poll_freq are part of the msgdma prefetcher IP, whereby it specifies the frequency of descriptor polling operation.
I can add the poll_freq description in the next patch.
Joyce Ooi July 14, 2020, 2:36 p.m. UTC | #4
> -----Original Message-----
> From: David Miller <davem@davemloft.net>
> Sent: Thursday, July 9, 2020 1:34 AM
> To: Ooi, Joyce <joyce.ooi@intel.com>
> Cc: thor.thayer@linux.intel.com; kuba@kernel.org; netdev@vger.kernel.org;
> linux-kernel@vger.kernel.org; dalon.westergreen@linux.intel.com; Tan, Ley
> Foon <ley.foon.tan@intel.com>; See, Chin Liang <chin.liang.see@intel.com>;
> Nguyen, Dinh <dinh.nguyen@intel.com>; Westergreen, Dalon
> <dalon.westergreen@intel.com>
> Subject: Re: [PATCH v4 09/10] net: eth: altera: add msgdma prefetcher
> 
> From: "Ooi, Joyce" <joyce.ooi@intel.com>
> Date: Wed,  8 Jul 2020 15:24:00 +0800
> 
> > +int msgdma_pref_initialize(struct altera_tse_private *priv) {
> > +	int i;
> > +	struct msgdma_pref_extended_desc *rx_descs;
> > +	struct msgdma_pref_extended_desc *tx_descs;
> > +	dma_addr_t rx_descsphys;
> > +	dma_addr_t tx_descsphys;
> 
> Reverse christmas tree please.
> 
> > +netdev_tx_t msgdma_pref_tx_buffer(struct altera_tse_private *priv,
> > +				  struct tse_buffer *buffer)
> > +{
> > +	u32 desc_entry = priv->tx_prod % (priv->tx_ring_size * 2);
> > +	struct msgdma_pref_extended_desc *tx_descs = priv->pref_txdesc;
> 
> Likewise.
> 
> > +u32 msgdma_pref_tx_completions(struct altera_tse_private *priv) {
> > +	u32 control;
> > +	u32 ready = 0;
> > +	u32 cons = priv->tx_cons;
> > +	u32 desc_ringsize = priv->tx_ring_size * 2;
> > +	u32 ringsize = priv->tx_ring_size;
> > +	u64 ns = 0;
> > +	struct msgdma_pref_extended_desc *cur;
> > +	struct tse_buffer *tx_buff;
> > +	struct skb_shared_hwtstamps shhwtstamp;
> > +	int i;
> 
> Likewise.
> 
> > +u32 msgdma_pref_rx_status(struct altera_tse_private *priv) {
> > +	u32 rxstatus = 0;
> > +	u32 pktlength;
> > +	u32 pktstatus;
> > +	u64 ns = 0;
> > +	u32 entry = priv->rx_cons % priv->rx_ring_size;
> > +	u32 desc_entry = priv->rx_prod % (priv->rx_ring_size * 2);
> > +	struct msgdma_pref_extended_desc *rx_descs = priv->pref_rxdesc;
> > +	struct skb_shared_hwtstamps *shhwtstamp = NULL;
> > +	struct tse_buffer *rx_buff = priv->rx_ring;
> 
> Likewise.
> 
> > +	} else if (priv->dmaops &&
> > +			   priv->dmaops->altera_dtype ==
> > +			   ALTERA_DTYPE_MSGDMA_PREF) {
> 
> This is not properly formatted.
> 
> On a multi-line conditional, every subsequent line after the first should align
> with the first column after the openning parenthesis of the first line.

Noted, will make the changes.
Jakub Kicinski July 14, 2020, 3:55 p.m. UTC | #5
On Tue, 14 Jul 2020 14:35:16 +0000 Ooi, Joyce wrote:
> > I'm no device tree expert but these look like config options rather than HW
> > description. They also don't appear to be documented in the next patch.  
> 
> The poll_freq are part of the msgdma prefetcher IP, whereby it
> specifies the frequency of descriptor polling operation. I can add
> the poll_freq description in the next patch.

Is the value decided at the time of synthesis or can the driver choose 
the value it wants?
Westergreen, Dalon July 14, 2020, 3:58 p.m. UTC | #6
On Tue, 2020-07-14 at 08:55 -0700, Jakub Kicinski wrote:
> On Tue, 14 Jul 2020 14:35:16 +0000 Ooi, Joyce wrote:
> > > I'm no device tree expert but these look like config options rather than
> > > HW
> > > description. They also don't appear to be documented in the next patch.  
> > 
> > The poll_freq are part of the msgdma prefetcher IP, whereby it
> > specifies the frequency of descriptor polling operation. I can add
> > the poll_freq description in the next patch.
> 
> Is the value decided at the time of synthesis or can the driver choose 
> the value it wants?

It is not controlled at synthesis, this parameter should likely not be a
devicetree parameter, perhaps just make it a module parameter with a default
value.

--dalon
Jakub Kicinski July 14, 2020, 4:29 p.m. UTC | #7
On Tue, 14 Jul 2020 15:58:53 +0000 Westergreen, Dalon wrote:
> On Tue, 2020-07-14 at 08:55 -0700, Jakub Kicinski wrote:
> > On Tue, 14 Jul 2020 14:35:16 +0000 Ooi, Joyce wrote:  
> > > > I'm no device tree expert but these look like config options rather than
> > > > HW
> > > > description. They also don't appear to be documented in the next patch.    
> > > 
> > > The poll_freq are part of the msgdma prefetcher IP, whereby it
> > > specifies the frequency of descriptor polling operation. I can add
> > > the poll_freq description in the next patch.  
> > 
> > Is the value decided at the time of synthesis or can the driver choose 
> > the value it wants?  
> 
> It is not controlled at synthesis, this parameter should likely not be a
> devicetree parameter, perhaps just make it a module parameter with a default
> value.

Let's see if I understand the feature - instead of using a doorbell the
HW periodically checks the contents of the next-to-use descriptor to
see if it contains a valid tx frame or rx buffer?

I've seen vendors abuse fields of ethtool --coalesce to configure
similar settings. tx-usecs-irq and rx-usecs-irq, I think. Since this
part of ethtool API has been ported to netlink, could we perhaps add 
a new field to ethtool --coalesce?
Westergreen, Dalon July 14, 2020, 6:51 p.m. UTC | #8
On Tue, 2020-07-14 at 09:29 -0700, Jakub Kicinski wrote:
> On Tue, 14 Jul 2020 15:58:53 +0000 Westergreen, Dalon wrote:
> > On Tue, 2020-07-14 at 08:55 -0700, Jakub Kicinski wrote:
> > > On Tue, 14 Jul 2020 14:35:16 +0000 Ooi, Joyce wrote:  
> > > > > I'm no device tree expert but these look like config options rather
> > > > > than
> > > > > HW
> > > > > description. They also don't appear to be documented in the next
> > > > > patch.    
> > > > 
> > > > The poll_freq are part of the msgdma prefetcher IP, whereby it
> > > > specifies the frequency of descriptor polling operation. I can add
> > > > the poll_freq description in the next patch.  
> > > 
> > > Is the value decided at the time of synthesis or can the driver choose 
> > > the value it wants?  
> > 
> > It is not controlled at synthesis, this parameter should likely not be a
> > devicetree parameter, perhaps just make it a module parameter with a default
> > value.
> 
> Let's see if I understand the feature - instead of using a doorbell the
> HW periodically checks the contents of the next-to-use descriptor to
> see if it contains a valid tx frame or rx buffer?
> 

Yes, it checks the next-to-use descriptor to see if the descriptor is valid.

the value specifies the number of cycles to wait before checking again

> I've seen vendors abuse fields of ethtool --coalesce to configure
> similar settings. tx-usecs-irq and rx-usecs-irq, I think. Since this
> part of ethtool API has been ported to netlink, could we perhaps add 
> a new field to ethtool --coalesce?

I don't think this is necessary, i think just having a module parameter
meets our needs.  I don't see a need for the value to change on a per
interface basis.  This was primarily used during testing / bringup.

-dalon
Jakub Kicinski July 14, 2020, 7:13 p.m. UTC | #9
On Tue, 14 Jul 2020 18:51:15 +0000 Westergreen, Dalon wrote:
> > I've seen vendors abuse fields of ethtool --coalesce to configure
> > similar settings. tx-usecs-irq and rx-usecs-irq, I think. Since this
> > part of ethtool API has been ported to netlink, could we perhaps add 
> > a new field to ethtool --coalesce?  
> 
> I don't think this is necessary, i think just having a module parameter
> meets our needs.  I don't see a need for the value to change on a per
> interface basis.  This was primarily used during testing / bringup.

It's hard to tell which knobs users will find worth turning therefore
the testing / bringup justification unfortunately has little bearing on
our upstream interface choices.

We try hard to avoid module parameters in networking drivers, so if you
want this tunable upstream a proper user interface will be required.
David Miller July 14, 2020, 8:23 p.m. UTC | #10
From: "Westergreen, Dalon" <dalon.westergreen@intel.com>
Date: Tue, 14 Jul 2020 18:51:15 +0000

> I don't think this is necessary, i think just having a module parameter
> meets our needs.  I don't see a need for the value to change on a per
> interface basis.  This was primarily used during testing / bringup.

Please no module parameters...
Westergreen, Dalon July 14, 2020, 11:25 p.m. UTC | #11
On Tue, 2020-07-14 at 13:23 -0700, David Miller wrote:
> From: "Westergreen, Dalon" <dalon.westergreen@intel.com>
> Date: Tue, 14 Jul 2020 18:51:15 +0000
> 
> > I don't think this is necessary, i think just having a module parameter
> > meets our needs.  I don't see a need for the value to change on a per
> > interface basis.  This was primarily used during testing / bringup.
> 
> Please no module parameters...

I think we are fine just hard coding the value.  that seems to be the best path
forward.

--dalon
diff mbox series

Patch

diff --git a/drivers/net/ethernet/altera/Makefile b/drivers/net/ethernet/altera/Makefile
index fc2e460926b3..4834e972e906 100644
--- a/drivers/net/ethernet/altera/Makefile
+++ b/drivers/net/ethernet/altera/Makefile
@@ -6,4 +6,4 @@ 
 obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
 altera_tse-objs := altera_tse_main.o altera_tse_ethtool.o \
 		   altera_msgdma.o altera_sgdma.o altera_utils.o \
-		   intel_fpga_tod.o
+		   intel_fpga_tod.o altera_msgdma_prefetcher.o
diff --git a/drivers/net/ethernet/altera/altera_msgdma_prefetcher.c b/drivers/net/ethernet/altera/altera_msgdma_prefetcher.c
new file mode 100644
index 000000000000..c5deb5b319cd
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdma_prefetcher.c
@@ -0,0 +1,431 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* MSGDMA Prefetcher driver for Altera ethernet devices
+ *
+ * Copyright (C) 2020 Intel Corporation. All rights reserved.
+ * Author(s):
+ *   Dalon Westergreen <dalon.westergreen@intel.com>
+ */
+
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
+#include "altera_tse.h"
+#include "altera_msgdma.h"
+#include "altera_msgdmahw.h"
+#include "altera_msgdma_prefetcher.h"
+#include "altera_msgdmahw_prefetcher.h"
+#include "altera_utils.h"
+
+int msgdma_pref_initialize(struct altera_tse_private *priv)
+{
+	int i;
+	struct msgdma_pref_extended_desc *rx_descs;
+	struct msgdma_pref_extended_desc *tx_descs;
+	dma_addr_t rx_descsphys;
+	dma_addr_t tx_descsphys;
+
+	priv->pref_rxdescphys = (dma_addr_t)0;
+	priv->pref_txdescphys = (dma_addr_t)0;
+
+	/* we need to allocate more pref descriptors than ringsize to
+	 * prevent all of the descriptors being owned by hw.  To do this
+	 * we just allocate twice ring_size descriptors.
+	 * rx_ring_size = priv->rx_ring_size * 2
+	 * tx_ring_size = priv->tx_ring_size * 2
+	 */
+
+	/* The prefetcher requires the descriptors to be aligned to the
+	 * descriptor read/write master's data width which worst case is
+	 * 512 bits.  Currently we DO NOT CHECK THIS and only support 32-bit
+	 * prefetcher masters.
+	 */
+
+	/* allocate memory for rx descriptors */
+	priv->pref_rxdesc =
+		dma_alloc_coherent(priv->device,
+				   sizeof(struct msgdma_pref_extended_desc)
+				   * priv->rx_ring_size * 2,
+				   &priv->pref_rxdescphys, GFP_KERNEL);
+
+	if (!priv->pref_rxdesc)
+		goto err_rx;
+
+	/* allocate memory for tx descriptors */
+	priv->pref_txdesc =
+		dma_alloc_coherent(priv->device,
+				   sizeof(struct msgdma_pref_extended_desc)
+				   * priv->tx_ring_size * 2,
+				   &priv->pref_txdescphys, GFP_KERNEL);
+
+	if (!priv->pref_txdesc)
+		goto err_tx;
+
+	/* setup base descriptor ring for tx & rx */
+	rx_descs = (struct msgdma_pref_extended_desc *)priv->pref_rxdesc;
+	tx_descs = (struct msgdma_pref_extended_desc *)priv->pref_txdesc;
+	tx_descsphys = priv->pref_txdescphys;
+	rx_descsphys = priv->pref_rxdescphys;
+
+	/* setup RX descriptors */
+	priv->pref_rx_prod = 0;
+	for (i = 0; i < priv->rx_ring_size * 2; i++) {
+		rx_descsphys = priv->pref_rxdescphys +
+			(((i + 1) % (priv->rx_ring_size * 2)) *
+			sizeof(struct msgdma_pref_extended_desc));
+		rx_descs[i].next_desc_lo = lower_32_bits(rx_descsphys);
+		rx_descs[i].next_desc_hi = upper_32_bits(rx_descsphys);
+		rx_descs[i].stride = MSGDMA_DESC_RX_STRIDE;
+		/* burst set to 0 so it defaults to max configured */
+		/* set seq number to desc number */
+		rx_descs[i].burst_seq_num = i;
+	}
+
+	/* setup TX descriptors */
+	for (i = 0; i < priv->tx_ring_size * 2; i++) {
+		tx_descsphys = priv->pref_txdescphys +
+			(((i + 1) % (priv->tx_ring_size * 2)) *
+			sizeof(struct msgdma_pref_extended_desc));
+		tx_descs[i].next_desc_lo = lower_32_bits(tx_descsphys);
+		tx_descs[i].next_desc_hi = upper_32_bits(tx_descsphys);
+		tx_descs[i].stride = MSGDMA_DESC_TX_STRIDE;
+		/* burst set to 0 so it defaults to max configured */
+		/* set seq number to desc number */
+		tx_descs[i].burst_seq_num = i;
+	}
+
+	if (netif_msg_ifup(priv))
+		netdev_info(priv->dev, "%s: RX Desc mem at 0x%llx\n", __func__,
+			    priv->pref_rxdescphys);
+
+	if (netif_msg_ifup(priv))
+		netdev_info(priv->dev, "%s: TX Desc mem at 0x%llx\n", __func__,
+			    priv->pref_txdescphys);
+
+	return 0;
+
+err_tx:
+	dma_free_coherent(priv->device,
+			  sizeof(struct msgdma_pref_extended_desc)
+			  * priv->rx_ring_size * 2,
+			  priv->pref_rxdesc, priv->pref_rxdescphys);
+err_rx:
+	return -ENOMEM;
+}
+
+void msgdma_pref_uninitialize(struct altera_tse_private *priv)
+{
+	if (priv->pref_rxdesc)
+		dma_free_coherent(priv->device,
+				  sizeof(struct msgdma_pref_extended_desc)
+				  * priv->rx_ring_size * 2,
+				  priv->pref_rxdesc, priv->pref_rxdescphys);
+
+	if (priv->pref_txdesc)
+		dma_free_coherent(priv->device,
+				  sizeof(struct msgdma_pref_extended_desc)
+				  * priv->tx_ring_size * 2,
+				  priv->pref_txdesc, priv->pref_txdescphys);
+}
+
+void msgdma_pref_enable_txirq(struct altera_tse_private *priv)
+{
+	tse_set_bit(priv->tx_pref_csr, msgdma_pref_csroffs(control),
+		    MSGDMA_PREF_CTL_GLOBAL_INTR);
+}
+
+void msgdma_pref_disable_txirq(struct altera_tse_private *priv)
+{
+	tse_clear_bit(priv->tx_pref_csr, msgdma_pref_csroffs(control),
+		      MSGDMA_PREF_CTL_GLOBAL_INTR);
+}
+
+void msgdma_pref_clear_txirq(struct altera_tse_private *priv)
+{
+	csrwr32(MSGDMA_PREF_STAT_IRQ, priv->tx_pref_csr,
+		msgdma_pref_csroffs(status));
+}
+
+void msgdma_pref_enable_rxirq(struct altera_tse_private *priv)
+{
+	tse_set_bit(priv->rx_pref_csr, msgdma_pref_csroffs(control),
+		    MSGDMA_PREF_CTL_GLOBAL_INTR);
+}
+
+void msgdma_pref_disable_rxirq(struct altera_tse_private *priv)
+{
+	tse_clear_bit(priv->rx_pref_csr, msgdma_pref_csroffs(control),
+		      MSGDMA_PREF_CTL_GLOBAL_INTR);
+}
+
+void msgdma_pref_clear_rxirq(struct altera_tse_private *priv)
+{
+	csrwr32(MSGDMA_PREF_STAT_IRQ, priv->rx_pref_csr,
+		msgdma_pref_csroffs(status));
+}
+
+static u64 timestamp_to_ns(struct msgdma_pref_extended_desc *desc)
+{
+	u64 ns = 0;
+	u64 second;
+	u32 tmp;
+
+	tmp = desc->timestamp_96b[0] >> 16;
+	tmp |= (desc->timestamp_96b[1] << 16);
+
+	second = desc->timestamp_96b[2];
+	second <<= 16;
+	second |= ((desc->timestamp_96b[1] & 0xffff0000) >> 16);
+
+	ns = second * NSEC_PER_SEC + tmp;
+
+	return ns;
+}
+
+/* Setup TX descriptor
+ *   -> this should never be called when a descriptor isn't available
+ */
+
+netdev_tx_t msgdma_pref_tx_buffer(struct altera_tse_private *priv,
+				  struct tse_buffer *buffer)
+{
+	u32 desc_entry = priv->tx_prod % (priv->tx_ring_size * 2);
+	struct msgdma_pref_extended_desc *tx_descs = priv->pref_txdesc;
+
+	/* if for some reason the descriptor is still owned by hardware */
+	if (unlikely(tx_descs[desc_entry].desc_control
+		     & MSGDMA_PREF_DESC_CTL_OWNED_BY_HW)) {
+		if (!netif_queue_stopped(priv->dev))
+			netif_stop_queue(priv->dev);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* write descriptor entries */
+	tx_descs[desc_entry].len = buffer->len;
+	tx_descs[desc_entry].read_addr_lo = lower_32_bits(buffer->dma_addr);
+	tx_descs[desc_entry].read_addr_hi = upper_32_bits(buffer->dma_addr);
+
+	/* set the control bits and set owned by hw */
+	tx_descs[desc_entry].desc_control = (MSGDMA_DESC_CTL_TX_SINGLE
+			| MSGDMA_PREF_DESC_CTL_OWNED_BY_HW);
+
+	if (netif_msg_tx_queued(priv))
+		netdev_info(priv->dev, "%s: cons: %d prod: %d",
+			    __func__, priv->tx_cons, priv->tx_prod);
+
+	return NETDEV_TX_OK;
+}
+
+u32 msgdma_pref_tx_completions(struct altera_tse_private *priv)
+{
+	u32 control;
+	u32 ready = 0;
+	u32 cons = priv->tx_cons;
+	u32 desc_ringsize = priv->tx_ring_size * 2;
+	u32 ringsize = priv->tx_ring_size;
+	u64 ns = 0;
+	struct msgdma_pref_extended_desc *cur;
+	struct tse_buffer *tx_buff;
+	struct skb_shared_hwtstamps shhwtstamp;
+	int i;
+
+	if (netif_msg_tx_done(priv))
+		for (i = 0; i < desc_ringsize; i++)
+			netdev_info(priv->dev, "%s: desc: %d control 0x%x\n",
+				    __func__, i,
+				    priv->pref_txdesc[i].desc_control);
+
+	cur = &priv->pref_txdesc[cons % desc_ringsize];
+	control = cur->desc_control;
+	tx_buff = &priv->tx_ring[cons % ringsize];
+
+	while (!(control & MSGDMA_PREF_DESC_CTL_OWNED_BY_HW) &&
+	       (priv->tx_prod != (cons + ready)) && control) {
+		if (skb_shinfo(tx_buff->skb)->tx_flags & SKBTX_IN_PROGRESS) {
+			/* Timestamping is enabled, pass timestamp back */
+			ns = timestamp_to_ns(cur);
+			memset(&shhwtstamp, 0,
+			       sizeof(struct skb_shared_hwtstamps));
+			shhwtstamp.hwtstamp = ns_to_ktime(ns);
+			skb_tstamp_tx(tx_buff->skb, &shhwtstamp);
+		}
+
+		if (netif_msg_tx_done(priv))
+			netdev_info(priv->dev, "%s: cur: %d ts: %lld ns\n",
+				    __func__,
+				    ((cons + ready) % desc_ringsize), ns);
+
+		/* clear data */
+		cur->desc_control = 0;
+		cur->timestamp_96b[0] = 0;
+		cur->timestamp_96b[1] = 0;
+		cur->timestamp_96b[2] = 0;
+
+		ready++;
+		cur = &priv->pref_txdesc[(cons + ready) % desc_ringsize];
+		tx_buff = &priv->tx_ring[(cons + ready) % ringsize];
+		control = cur->desc_control;
+	}
+
+	return ready;
+}
+
+void msgdma_pref_reset(struct altera_tse_private *priv)
+{
+	int counter;
+
+	/* turn off polling */
+	tse_clear_bit(priv->rx_pref_csr, msgdma_pref_csroffs(control),
+		      MSGDMA_PREF_CTL_DESC_POLL_EN);
+	tse_clear_bit(priv->tx_pref_csr, msgdma_pref_csroffs(control),
+		      MSGDMA_PREF_CTL_DESC_POLL_EN);
+
+	/* Reset the RX Prefetcher */
+	csrwr32(MSGDMA_PREF_STAT_IRQ, priv->rx_pref_csr,
+		msgdma_pref_csroffs(status));
+	csrwr32(MSGDMA_PREF_CTL_RESET, priv->rx_pref_csr,
+		msgdma_pref_csroffs(control));
+
+	counter = 0;
+	while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+		if (tse_bit_is_clear(priv->rx_pref_csr,
+				     msgdma_pref_csroffs(control),
+				     MSGDMA_PREF_CTL_RESET))
+			break;
+		udelay(1);
+	}
+
+	if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
+		netif_warn(priv, drv, priv->dev,
+			   "TSE Rx Prefetcher reset bit never cleared!\n");
+
+	/* Reset the TX Prefetcher */
+	csrwr32(MSGDMA_PREF_STAT_IRQ, priv->tx_pref_csr,
+		msgdma_pref_csroffs(status));
+	csrwr32(MSGDMA_PREF_CTL_RESET, priv->tx_pref_csr,
+		msgdma_pref_csroffs(control));
+
+	counter = 0;
+	while (counter++ < ALTERA_TSE_SW_RESET_WATCHDOG_CNTR) {
+		if (tse_bit_is_clear(priv->tx_pref_csr,
+				     msgdma_pref_csroffs(control),
+				     MSGDMA_PREF_CTL_RESET))
+			break;
+		udelay(1);
+	}
+
+	if (counter >= ALTERA_TSE_SW_RESET_WATCHDOG_CNTR)
+		netif_warn(priv, drv, priv->dev,
+			   "TSE Tx Prefetcher reset bit never cleared!\n");
+
+	/* clear all status bits */
+	csrwr32(MSGDMA_PREF_STAT_IRQ, priv->tx_pref_csr,
+		msgdma_pref_csroffs(status));
+
+	/* Reset mSGDMA dispatchers*/
+	msgdma_reset(priv);
+}
+
+/* Setup the RX and TX prefetchers to poll the descriptor chain */
+void msgdma_pref_start_rxdma(struct altera_tse_private *priv)
+{
+	csrwr32(priv->rx_poll_freq, priv->rx_pref_csr,
+		msgdma_pref_csroffs(desc_poll_freq));
+	csrwr32(lower_32_bits(priv->pref_rxdescphys), priv->rx_pref_csr,
+		msgdma_pref_csroffs(next_desc_lo));
+	csrwr32(upper_32_bits(priv->pref_rxdescphys), priv->rx_pref_csr,
+		msgdma_pref_csroffs(next_desc_hi));
+	tse_set_bit(priv->rx_pref_csr, msgdma_pref_csroffs(control),
+		    MSGDMA_PREF_CTL_DESC_POLL_EN | MSGDMA_PREF_CTL_RUN);
+}
+
+void msgdma_pref_start_txdma(struct altera_tse_private *priv)
+{
+	csrwr32(priv->tx_poll_freq, priv->tx_pref_csr,
+		msgdma_pref_csroffs(desc_poll_freq));
+	csrwr32(lower_32_bits(priv->pref_txdescphys), priv->tx_pref_csr,
+		msgdma_pref_csroffs(next_desc_lo));
+	csrwr32(upper_32_bits(priv->pref_txdescphys), priv->tx_pref_csr,
+		msgdma_pref_csroffs(next_desc_hi));
+	tse_set_bit(priv->tx_pref_csr, msgdma_pref_csroffs(control),
+		    MSGDMA_PREF_CTL_DESC_POLL_EN | MSGDMA_PREF_CTL_RUN);
+}
+
+/* Add MSGDMA Prefetcher Descriptor to descriptor list
+ *   -> This should never be called when a descriptor isn't available
+ */
+void msgdma_pref_add_rx_desc(struct altera_tse_private *priv,
+			     struct tse_buffer *rxbuffer)
+{
+	struct msgdma_pref_extended_desc *rx_descs = priv->pref_rxdesc;
+	u32 desc_entry = priv->pref_rx_prod % (priv->rx_ring_size * 2);
+
+	/* write descriptor entries */
+	rx_descs[desc_entry].len = priv->rx_dma_buf_sz;
+	rx_descs[desc_entry].write_addr_lo = lower_32_bits(rxbuffer->dma_addr);
+	rx_descs[desc_entry].write_addr_hi = upper_32_bits(rxbuffer->dma_addr);
+
+	/* set the control bits and set owned by hw */
+	rx_descs[desc_entry].desc_control = (MSGDMA_DESC_CTL_END_ON_EOP
+			| MSGDMA_DESC_CTL_END_ON_LEN
+			| MSGDMA_DESC_CTL_TR_COMP_IRQ
+			| MSGDMA_DESC_CTL_EARLY_IRQ
+			| MSGDMA_DESC_CTL_TR_ERR_IRQ
+			| MSGDMA_DESC_CTL_GO
+			| MSGDMA_PREF_DESC_CTL_OWNED_BY_HW);
+
+	/* we need to keep a separate one for rx as RX_DESCRIPTORS are
+	 * pre-configured at startup
+	 */
+	priv->pref_rx_prod++;
+
+	if (netif_msg_rx_status(priv)) {
+		netdev_info(priv->dev, "%s: desc: %d buf: %d control 0x%x\n",
+			    __func__, desc_entry,
+			    priv->rx_prod % priv->rx_ring_size,
+			    priv->pref_rxdesc[desc_entry].desc_control);
+	}
+}
+
+u32 msgdma_pref_rx_status(struct altera_tse_private *priv)
+{
+	u32 rxstatus = 0;
+	u32 pktlength;
+	u32 pktstatus;
+	u64 ns = 0;
+	u32 entry = priv->rx_cons % priv->rx_ring_size;
+	u32 desc_entry = priv->rx_prod % (priv->rx_ring_size * 2);
+	struct msgdma_pref_extended_desc *rx_descs = priv->pref_rxdesc;
+	struct skb_shared_hwtstamps *shhwtstamp = NULL;
+	struct tse_buffer *rx_buff = priv->rx_ring;
+
+	/* if the current entry is not owned by hardware, process it */
+	if (!(rx_descs[desc_entry].desc_control
+	      & MSGDMA_PREF_DESC_CTL_OWNED_BY_HW) &&
+	      rx_descs[desc_entry].desc_control) {
+		pktlength = rx_descs[desc_entry].bytes_transferred;
+		pktstatus = rx_descs[desc_entry].desc_status;
+		rxstatus = pktstatus;
+		rxstatus = rxstatus << 16;
+		rxstatus |= (pktlength & 0xffff);
+
+		/* get the timestamp */
+		if (priv->hwts_rx_en) {
+			ns = timestamp_to_ns(&rx_descs[desc_entry]);
+			shhwtstamp = skb_hwtstamps(rx_buff[entry].skb);
+			memset(shhwtstamp, 0,
+			       sizeof(struct skb_shared_hwtstamps));
+			shhwtstamp->hwtstamp = ns_to_ktime(ns);
+		}
+
+		/* clear data */
+		rx_descs[desc_entry].desc_control = 0;
+		rx_descs[desc_entry].timestamp_96b[0] = 0;
+		rx_descs[desc_entry].timestamp_96b[1] = 0;
+		rx_descs[desc_entry].timestamp_96b[2] = 0;
+
+		if (netif_msg_rx_status(priv))
+			netdev_info(priv->dev, "%s: desc: %d buf: %d ts: %lld ns",
+				    __func__, desc_entry, entry, ns);
+	}
+	return rxstatus;
+}
diff --git a/drivers/net/ethernet/altera/altera_msgdma_prefetcher.h b/drivers/net/ethernet/altera/altera_msgdma_prefetcher.h
new file mode 100644
index 000000000000..6507c2805a05
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdma_prefetcher.h
@@ -0,0 +1,30 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* MSGDMA Prefetcher driver for Altera ethernet devices
+ *
+ * Copyright (C) 2020 Intel Corporation. All rights reserved.
+ * Author(s):
+ *   Dalon Westergreen <dalon.westergreen@intel.com>
+ */
+
+#ifndef __ALTERA_PREF_MSGDMA_H__
+#define __ALTERA_PREF_MSGDMA_H__
+
+void msgdma_pref_reset(struct altera_tse_private *priv);
+void msgdma_pref_enable_txirq(struct altera_tse_private *priv);
+void msgdma_pref_enable_rxirq(struct altera_tse_private *priv);
+void msgdma_pref_disable_rxirq(struct altera_tse_private *priv);
+void msgdma_pref_disable_txirq(struct altera_tse_private *priv);
+void msgdma_pref_clear_rxirq(struct altera_tse_private *priv);
+void msgdma_pref_clear_txirq(struct altera_tse_private *priv);
+u32 msgdma_pref_tx_completions(struct altera_tse_private *priv);
+void msgdma_pref_add_rx_desc(struct altera_tse_private *priv,
+			     struct tse_buffer *buffer);
+netdev_tx_t msgdma_pref_tx_buffer(struct altera_tse_private *priv,
+				  struct tse_buffer *buffer);
+u32 msgdma_pref_rx_status(struct altera_tse_private *priv);
+int msgdma_pref_initialize(struct altera_tse_private *priv);
+void msgdma_pref_uninitialize(struct altera_tse_private *priv);
+void msgdma_pref_start_rxdma(struct altera_tse_private *priv);
+void msgdma_pref_start_txdma(struct altera_tse_private *priv);
+
+#endif /*  __ALTERA_PREF_MSGDMA_H__ */
diff --git a/drivers/net/ethernet/altera/altera_msgdmahw_prefetcher.h b/drivers/net/ethernet/altera/altera_msgdmahw_prefetcher.h
new file mode 100644
index 000000000000..efda31e491ca
--- /dev/null
+++ b/drivers/net/ethernet/altera/altera_msgdmahw_prefetcher.h
@@ -0,0 +1,87 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* MSGDMA Prefetcher driver for Altera ethernet devices
+ *
+ * Copyright (C) 2020 Intel Corporation.
+ * Contributors:
+ *   Dalon Westergreen
+ *   Thomas Chou
+ *   Ian Abbott
+ *   Yuriy Kozlov
+ *   Tobias Klauser
+ *   Andriy Smolskyy
+ *   Roman Bulgakov
+ *   Dmytro Mytarchuk
+ *   Matthew Gerlach
+ */
+
+#ifndef __ALTERA_MSGDMAHW_PREFETCHER_H__
+#define __ALTERA_MSGDMAHW_PREFETCHER_H__
+
+/* mSGDMA prefetcher extended prefectcher descriptor format
+ */
+struct msgdma_pref_extended_desc {
+	/* data buffer source address low bits */
+	u32 read_addr_lo;
+	/* data buffer destination address low bits */
+	u32 write_addr_lo;
+	/* the number of bytes to transfer */
+	u32 len;
+	/* next descriptor low address */
+	u32 next_desc_lo;
+	/* number of bytes transferred */
+	u32 bytes_transferred;
+	u32 desc_status;
+	u32 reserved_18;
+	/* bit 31:24 write burst */
+	/* bit 23:16 read burst */
+	/* bit 15:0  sequence number */
+	u32 burst_seq_num;
+	/* bit 31:16 write stride */
+	/* bit 15:0  read stride */
+	u32 stride;
+	/* data buffer source address high bits */
+	u32 read_addr_hi;
+	/* data buffer destination address high bits */
+	u32 write_addr_hi;
+	/* next descriptor high address */
+	u32 next_desc_hi;
+	/* prefetcher mod now writes these reserved bits*/
+	/* Response bits [191:160] */
+	u32 timestamp_96b[3];
+	/* desc_control */
+	u32 desc_control;
+};
+
+/* mSGDMA Prefetcher Descriptor Status bits */
+#define MSGDMA_PREF_DESC_STAT_STOPPED_ON_EARLY		BIT(8)
+#define MSGDMA_PREF_DESC_STAT_MASK			0xFF
+
+/* mSGDMA Prefetcher Descriptor Control bits */
+/* bit 31 and bits 29-0 are the same as the normal dispatcher ctl flags */
+#define MSGDMA_PREF_DESC_CTL_OWNED_BY_HW		BIT(30)
+
+/* mSGDMA Prefetcher CSR */
+struct msgdma_prefetcher_csr {
+	u32 control;
+	u32 next_desc_lo;
+	u32 next_desc_hi;
+	u32 desc_poll_freq;
+	u32 status;
+};
+
+/* mSGDMA Prefetcher Control */
+#define MSGDMA_PREF_CTL_PARK				BIT(4)
+#define MSGDMA_PREF_CTL_GLOBAL_INTR			BIT(3)
+#define MSGDMA_PREF_CTL_RESET				BIT(2)
+#define MSGDMA_PREF_CTL_DESC_POLL_EN			BIT(1)
+#define MSGDMA_PREF_CTL_RUN				BIT(0)
+
+#define MSGDMA_PREF_POLL_FREQ_MASK			0xFFFF
+
+/* mSGDMA Prefetcher Status */
+#define MSGDMA_PREF_STAT_IRQ				BIT(0)
+
+#define msgdma_pref_csroffs(a) (offsetof(struct msgdma_prefetcher_csr, a))
+#define msgdma_pref_descroffs(a) (offsetof(struct msgdma_pref_extended_desc, a))
+
+#endif /* __ALTERA_MSGDMAHW_PREFETCHER_H__*/
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index b7c176a808ac..35f99bfce4ef 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -382,6 +382,7 @@  struct altera_tse_private;
 
 #define ALTERA_DTYPE_SGDMA 1
 #define ALTERA_DTYPE_MSGDMA 2
+#define ALTERA_DTYPE_MSGDMA_PREF 3
 
 /* standard DMA interface for SGDMA and MSGDMA */
 struct altera_dmaops {
@@ -434,6 +435,19 @@  struct altera_tse_private {
 	void __iomem *tx_dma_csr;
 	void __iomem *tx_dma_desc;
 
+	/* mSGDMA Rx Prefecher address space */
+	void __iomem *rx_pref_csr;
+	struct msgdma_pref_extended_desc *pref_rxdesc;
+	dma_addr_t pref_rxdescphys;
+	u32 pref_rx_prod;
+
+	/* mSGDMA Tx Prefecher address space */
+	void __iomem *tx_pref_csr;
+	struct msgdma_pref_extended_desc *pref_txdesc;
+	dma_addr_t pref_txdescphys;
+	u32 rx_poll_freq;
+	u32 tx_poll_freq;
+
 	/* Rx buffers queue */
 	struct tse_buffer *rx_ring;
 	u32 rx_cons;
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index bdc2fb1c41c7..a879537997a4 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -44,6 +44,7 @@ 
 #include "altera_sgdma.h"
 #include "altera_msgdma.h"
 #include "intel_fpga_tod.h"
+#include "altera_msgdma_prefetcher.h"
 
 static atomic_t instance_count = ATOMIC_INIT(~0);
 /* Module parameters */
@@ -1500,6 +1501,34 @@  static int altera_tse_probe(struct platform_device *pdev)
 		priv->rxdescmem = resource_size(dma_res);
 		priv->rxdescmem_busaddr = dma_res->start;
 
+	} else if (priv->dmaops &&
+			   priv->dmaops->altera_dtype ==
+			   ALTERA_DTYPE_MSGDMA_PREF) {
+		/* mSGDMA Rx Prefetcher address space */
+		ret = request_and_map(pdev, "rx_pref", &dma_res,
+				      &priv->rx_pref_csr);
+		if (ret)
+			goto err_free_netdev;
+
+		/* mSGDMA Tx Prefetcher address space */
+		ret = request_and_map(pdev, "tx_pref", &dma_res,
+				      &priv->tx_pref_csr);
+		if (ret)
+			goto err_free_netdev;
+
+		/* get prefetcher rx poll frequency from device tree */
+		if (of_property_read_u32(pdev->dev.of_node, "rx-poll-freq",
+					 &priv->rx_poll_freq)) {
+			dev_info(&pdev->dev, "Defaulting RX Poll Frequency to 128\n");
+			priv->rx_poll_freq = 128;
+		}
+
+		/* get prefetcher rx poll frequency from device tree */
+		if (of_property_read_u32(pdev->dev.of_node, "tx-poll-freq",
+					 &priv->tx_poll_freq)) {
+			dev_info(&pdev->dev, "Defaulting TX Poll Frequency to 128\n");
+			priv->tx_poll_freq = 128;
+		}
 	} else {
 		goto err_free_netdev;
 	}
@@ -1758,7 +1787,29 @@  static const struct altera_dmaops altera_dtype_msgdma = {
 	.start_txdma = NULL,
 };
 
+static const struct altera_dmaops altera_dtype_prefetcher = {
+	.altera_dtype = ALTERA_DTYPE_MSGDMA_PREF,
+	.dmamask = 64,
+	.reset_dma = msgdma_pref_reset,
+	.enable_txirq = msgdma_pref_enable_txirq,
+	.enable_rxirq = msgdma_pref_enable_rxirq,
+	.disable_txirq = msgdma_pref_disable_txirq,
+	.disable_rxirq = msgdma_pref_disable_rxirq,
+	.clear_txirq = msgdma_pref_clear_txirq,
+	.clear_rxirq = msgdma_pref_clear_rxirq,
+	.tx_buffer = msgdma_pref_tx_buffer,
+	.tx_completions = msgdma_pref_tx_completions,
+	.add_rx_desc = msgdma_pref_add_rx_desc,
+	.get_rx_status = msgdma_pref_rx_status,
+	.init_dma = msgdma_pref_initialize,
+	.uninit_dma = msgdma_pref_uninitialize,
+	.start_rxdma = msgdma_pref_start_rxdma,
+	.start_txdma = msgdma_pref_start_txdma,
+};
+
 static const struct of_device_id altera_tse_ids[] = {
+	{ .compatible = "altr,tse-msgdma-2.0",
+		.data = &altera_dtype_prefetcher, },
 	{ .compatible = "altr,tse-msgdma-1.0", .data = &altera_dtype_msgdma, },
 	{ .compatible = "altr,tse-1.0", .data = &altera_dtype_sgdma, },
 	{ .compatible = "ALTR,tse-1.0", .data = &altera_dtype_sgdma, },