kernel: bump 4.9 to 4.9.125
[openwrt/openwrt.git] / target / linux / layerscape / patches-4.9 / 705-dpaa2-support-layerscape.patch
index 51abc032545595a43cc42f87601525f441ff2c7a..5363f0a3562fb32f31a45b340787fc7c44638cb4 100644 (file)
@@ -1,15 +1,16 @@
-From 3a302437605308079db398b67000a77a4fe92da8 Mon Sep 17 00:00:00 2001
+From e729e648e4259940473e256dd4f9c8df99e774b0 Mon Sep 17 00:00:00 2001
 From: Yangbo Lu <yangbo.lu@nxp.com>
-Date: Mon, 25 Sep 2017 12:07:58 +0800
+Date: Wed, 17 Jan 2018 15:12:58 +0800
 Subject: [PATCH] dpaa2: support layerscape
 
-This is a integrated patch for layerscape dpaa2 support.
+This is an integrated patch for layerscape dpaa2 support.
 
 Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com>
 Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
 Signed-off-by: Razvan Stefanescu <razvan.stefanescu@nxp.com>
 Signed-off-by: costi <constantin.tudor@freescale.com>
 Signed-off-by: Catalin Horghidan <catalin.horghidan@nxp.com>
+Signed-off-by: Mathew McBride <matt@traverse.com.au>
 Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 ---
  drivers/soc/fsl/ls2-console/Kconfig                |    4 +
@@ -17,41 +18,41 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
  drivers/soc/fsl/ls2-console/ls2-console.c          |  284 ++
  drivers/staging/fsl-dpaa2/ethernet/Makefile        |   11 +
  drivers/staging/fsl-dpaa2/ethernet/README          |  186 ++
- .../staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c |  350 +++
+ .../staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c |  35++
  .../staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h |   60 +
- .../staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h   |  184 ++
- drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c     | 3155 ++++++++++++++++++++
- drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h     |  460 +++
- drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c |  856 ++++++
- drivers/staging/fsl-dpaa2/ethernet/dpkg.h          |  176 ++
- drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h      |  600 ++++
- drivers/staging/fsl-dpaa2/ethernet/dpni.c          | 1770 +++++++++++
- drivers/staging/fsl-dpaa2/ethernet/dpni.h          |  989 ++++++
+ .../staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h   |  184 +
+ drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c     | 3516 ++++++++++++++++++++
+ drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h     |  499 +++
+ drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c |  864 +++++
+ drivers/staging/fsl-dpaa2/ethernet/dpkg.h          |  176 +
+ drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h      |  658 ++++
+ drivers/staging/fsl-dpaa2/ethernet/dpni.c          | 1903 +++++++++++
+ drivers/staging/fsl-dpaa2/ethernet/dpni.h          | 1053 ++++++
  drivers/staging/fsl-dpaa2/ethernet/net.h           |  480 +++
  drivers/staging/fsl-dpaa2/ethsw/Kconfig            |    6 +
  drivers/staging/fsl-dpaa2/ethsw/Makefile           |   10 +
- drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h         |  851 ++++++
- drivers/staging/fsl-dpaa2/ethsw/dpsw.c             | 2762 +++++++++++++++++
- drivers/staging/fsl-dpaa2/ethsw/dpsw.h             | 1269 ++++++++
- drivers/staging/fsl-dpaa2/ethsw/switch.c           | 1857 ++++++++++++
+ drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h         |  851 +++++
+ drivers/staging/fsl-dpaa2/ethsw/dpsw.c             | 2762 +++++++++++++++
+ drivers/staging/fsl-dpaa2/ethsw/dpsw.h             | 1269 +++++++
+ drivers/staging/fsl-dpaa2/ethsw/switch.c           | 1857 +++++++++++
  drivers/staging/fsl-dpaa2/evb/Kconfig              |    7 +
  drivers/staging/fsl-dpaa2/evb/Makefile             |   10 +
  drivers/staging/fsl-dpaa2/evb/dpdmux-cmd.h         |  279 ++
  drivers/staging/fsl-dpaa2/evb/dpdmux.c             | 1112 +++++++
  drivers/staging/fsl-dpaa2/evb/dpdmux.h             |  453 +++
- drivers/staging/fsl-dpaa2/evb/evb.c                | 1350 +++++++++
+ drivers/staging/fsl-dpaa2/evb/evb.c                | 1350 ++++++++
  drivers/staging/fsl-dpaa2/mac/Kconfig              |   23 +
  drivers/staging/fsl-dpaa2/mac/Makefile             |   10 +
- drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h          |  172 ++
+ drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h          |  172 +
  drivers/staging/fsl-dpaa2/mac/dpmac.c              |  620 ++++
- drivers/staging/fsl-dpaa2/mac/dpmac.h              |  342 +++
- drivers/staging/fsl-dpaa2/mac/mac.c                |  666 +++++
+ drivers/staging/fsl-dpaa2/mac/dpmac.h              |  342 ++
+ drivers/staging/fsl-dpaa2/mac/mac.c                |  670 ++++
  drivers/staging/fsl-dpaa2/rtc/Makefile             |   10 +
  drivers/staging/fsl-dpaa2/rtc/dprtc-cmd.h          |  160 +
  drivers/staging/fsl-dpaa2/rtc/dprtc.c              |  746 +++++
- drivers/staging/fsl-dpaa2/rtc/dprtc.h              |  172 ++
+ drivers/staging/fsl-dpaa2/rtc/dprtc.h              |  172 +
  drivers/staging/fsl-dpaa2/rtc/rtc.c                |  243 ++
- 39 files changed, 22696 insertions(+)
+ 39 files changed, 23365 insertions(+)
  create mode 100644 drivers/soc/fsl/ls2-console/Kconfig
  create mode 100644 drivers/soc/fsl/ls2-console/Makefile
  create mode 100644 drivers/soc/fsl/ls2-console/ls2-console.c
@@ -595,7 +596,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +non-standard driver stats can be consulted through ethtool -S option.
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c
-@@ -0,0 +1,350 @@
+@@ -0,0 +1,352 @@
 +
 +/* Copyright 2015 Freescale Semiconductor Inc.
 + *
@@ -708,9 +709,9 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      int i, err;
 +
 +      seq_printf(file, "FQ stats for %s:\n", priv->net_dev->name);
-+      seq_printf(file, "%s%16s%16s%16s%16s%16s\n",
-+                 "VFQID", "CPU", "Type", "Frames", "Pending frames",
-+                 "Congestion");
++      seq_printf(file, "%s%16s%16s%16s%16s%16s%16s\n",
++                 "VFQID", "CPU", "Traffic Class", "Type", "Frames",
++                 "Pending frames", "Congestion");
 +
 +      for (i = 0; i <  priv->num_fqs; i++) {
 +              fq = &priv->fq[i];
@@ -718,9 +719,10 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              if (err)
 +                      fcnt = 0;
 +
-+              seq_printf(file, "%5d%16d%16s%16llu%16u%16llu\n",
++              seq_printf(file, "%5d%16d%16d%16s%16llu%16u%16llu\n",
 +                         fq->fqid,
 +                         fq->target_cpu,
++                         fq->tc,
 +                         fq_type_to_str(fq),
 +                         fq->stats.frames,
 +                         fcnt,
@@ -756,19 +758,20 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      int i;
 +
 +      seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name);
-+      seq_printf(file, "%s%16s%16s%16s%16s%16s\n",
++      seq_printf(file, "%s%16s%16s%16s%16s%16s%16s\n",
 +                 "CHID", "CPU", "Deq busy", "Frames", "CDANs",
-+                 "Avg frm/CDAN");
++                 "Avg frm/CDAN", "Buf count");
 +
 +      for (i = 0; i < priv->num_channels; i++) {
 +              ch = priv->channel[i];
-+              seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu\n",
++              seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu%16d\n",
 +                         ch->ch_id,
 +                         ch->nctx.desired_cpu,
 +                         ch->stats.dequeue_portal_busy,
 +                         ch->stats.frames,
 +                         ch->stats.cdan,
-+                         ch->stats.frames / ch->stats.cdan);
++                         ch->stats.frames / ch->stats.cdan,
++                         ch->buf_count);
 +      }
 +
 +      return 0;
@@ -1198,7 +1201,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#include <trace/define_trace.h>
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c
-@@ -0,0 +1,3155 @@
+@@ -0,0 +1,3516 @@
 +/* Copyright 2014-2015 Freescale Semiconductor Inc.
 + *
 + * Redistribution and use in source and binary forms, with or without
@@ -1338,6 +1341,8 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      u16 fd_offset = dpaa2_fd_get_offset(fd);
 +      u32 fd_length = dpaa2_fd_get_len(fd);
 +
++      ch->buf_count--;
++
 +      skb = build_skb(fd_vaddr, DPAA2_ETH_SKB_SIZE);
 +      if (unlikely(!skb))
 +              return NULL;
@@ -1345,8 +1350,6 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      skb_reserve(skb, fd_offset);
 +      skb_put(skb, fd_length);
 +
-+      ch->buf_count--;
-+
 +      return skb;
 +}
 +
@@ -1384,7 +1387,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +                      /* We build the skb around the first data buffer */
 +                      skb = build_skb(sg_vaddr, DPAA2_ETH_SKB_SIZE);
 +                      if (unlikely(!skb))
-+                              return NULL;
++                              goto err_build;
 +
 +                      sg_offset = dpaa2_sg_get_offset(sge);
 +                      skb_reserve(skb, sg_offset);
@@ -1415,6 +1418,32 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      ch->buf_count -= i + 2;
 +
 +      return skb;
++
++err_build:
++      /* We still need to subtract the buffers used by this FD from our
++       * software counter
++       */
++      for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++)
++              if (dpaa2_sg_is_final(&sgt[i]))
++                      break;
++      ch->buf_count -= i + 2;
++
++      return NULL;
++}
++
++static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count)
++{
++      struct device *dev = priv->net_dev->dev.parent;
++      void *vaddr;
++      int i;
++
++      for (i = 0; i < count; i++) {
++              /* Same logic as on regular Rx path */
++              vaddr = dpaa2_eth_iova_to_virt(priv->iommu_domain, buf_array[i]);
++              dma_unmap_single(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE,
++                               DMA_FROM_DEVICE);
++              put_page(virt_to_head_page(vaddr));
++      }
 +}
 +
 +/* Main Rx frame processing routine */
@@ -1722,7 +1751,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      dpaa2_fd_set_addr(fd, addr);
 +      dpaa2_fd_set_len(fd, skb->len);
 +
-+      fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | FD_CTRL_PTA | FD_CTRL_PTV1;
++      fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | FD_CTRL_PTA;
 +
 +      if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
 +              enable_tx_tstamp(fd, sgt_buf);
@@ -1779,7 +1808,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      dpaa2_fd_set_len(fd, skb->len);
 +      dpaa2_fd_set_format(fd, dpaa2_fd_single);
 +
-+      fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | FD_CTRL_PTA | FD_CTRL_PTV1;
++      fd->simple.ctrl = DPAA2_FD_CTRL_ASAL | FD_CTRL_PTA;
 +
 +      if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
 +              enable_tx_tstamp(fd, buffer_start);
@@ -1798,7 +1827,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 + */
 +static void free_tx_fd(const struct dpaa2_eth_priv *priv,
 +                     const struct dpaa2_fd *fd,
-+                     u32 *status)
++                     u32 *status, bool in_napi)
 +{
 +      struct device *dev = priv->net_dev->dev.parent;
 +      dma_addr_t fd_addr;
@@ -1877,7 +1906,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              kfree(skbh);
 +
 +      /* Move on with skb release */
-+      dev_kfree_skb(skb);
++      napi_consume_skb(skb, in_napi);
 +}
 +
 +static int dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
@@ -1961,7 +1990,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      if (unlikely(err < 0)) {
 +              percpu_stats->tx_errors++;
 +              /* Clean up everything, including freeing the skb */
-+              free_tx_fd(priv, &fd, NULL);
++              free_tx_fd(priv, &fd, NULL, false);
 +      } else {
 +              percpu_stats->tx_packets++;
 +              percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd);
@@ -2014,7 +2043,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +                                 fd->simple.ctrl & DPAA2_FD_TX_ERR_MASK);
 +      }
 +
-+      free_tx_fd(priv, fd, check_fas_errors ? &status : NULL);
++      free_tx_fd(priv, fd, check_fas_errors ? &status : NULL, true);
 +
 +      /* if there are no errors, we're done */
 +      if (likely(!errors))
@@ -2084,7 +2113,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
 +      void *buf;
 +      dma_addr_t addr;
-+      int i;
++      int i, err;
 +
 +      for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) {
 +              /* Allocate buffer visible to WRIOP + skb shared info +
@@ -2111,22 +2140,25 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      }
 +
 +release_bufs:
-+      /* In case the portal is busy, retry until successful.
-+       * The buffer release function would only fail if the QBMan portal
-+       * was busy, which implies portal contention (i.e. more CPUs than
-+       * portals, i.e. GPPs w/o affine DPIOs). For all practical purposes,
-+       * there is little we can realistically do, short of giving up -
-+       * in which case we'd risk depleting the buffer pool and never again
-+       * receiving the Rx interrupt which would kick-start the refill logic.
-+       * So just keep retrying, at the risk of being moved to ksoftirqd.
-+       */
-+      while (dpaa2_io_service_release(NULL, bpid, buf_array, i))
++      /* In case the portal is busy, retry until successful */
++      while ((err = dpaa2_io_service_release(NULL, bpid,
++                                             buf_array, i)) == -EBUSY)
 +              cpu_relax();
++
++      /* If release command failed, clean up and bail out; not much
++       * else we can do about it
++       */
++      if (unlikely(err)) {
++              free_bufs(priv, buf_array, i);
++              return 0;
++      }
++
 +      return i;
 +
 +err_map:
 +      put_page(virt_to_head_page(buf));
 +err_alloc:
++      /* If we managed to allocate at least some buffers, release them */
 +      if (i)
 +              goto release_bufs;
 +
@@ -2169,10 +2201,8 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 + */
 +static void drain_bufs(struct dpaa2_eth_priv *priv, int count)
 +{
-+      struct device *dev = priv->net_dev->dev.parent;
 +      u64 buf_array[DPAA2_ETH_BUFS_PER_CMD];
-+      void *vaddr;
-+      int ret, i;
++      int ret;
 +
 +      do {
 +              ret = dpaa2_io_service_acquire(NULL, priv->bpid,
@@ -2181,15 +2211,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +                      netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n");
 +                      return;
 +              }
-+              for (i = 0; i < ret; i++) {
-+                      /* Same logic as on regular Rx path */
-+                      vaddr = dpaa2_eth_iova_to_virt(priv->iommu_domain,
-+                                                     buf_array[i]);
-+                      dma_unmap_single(dev, buf_array[i],
-+                                       DPAA2_ETH_RX_BUF_SIZE,
-+                                       DMA_FROM_DEVICE);
-+                      put_page(virt_to_head_page(vaddr));
-+              }
++              free_bufs(priv, buf_array, ret);
 +      } while (ret);
 +}
 +
@@ -2497,7 +2519,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +/** Fill in counters maintained by the GPP driver. These may be different from
 + * the hardware counters obtained by ethtool.
 + */
-+static void dpaa2_eth_get_stats(struct net_device *net_dev,
++static struct rtnl_link_stats64 *dpaa2_eth_get_stats(struct net_device *net_dev,
 +                              struct rtnl_link_stats64 *stats)
 +{
 +      struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
@@ -2513,6 +2535,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              for (j = 0; j < num; j++)
 +                      netstats[j] += cpustats[j];
 +      }
++      return stats;
 +}
 +
 +static int dpaa2_eth_change_mtu(struct net_device *net_dev, int mtu)
@@ -3039,7 +3062,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +
 +static void setup_fqs(struct dpaa2_eth_priv *priv)
 +{
-+      int i;
++      int i, j;
 +
 +      /* We have one TxConf FQ per Tx flow. Tx queues MUST be at the
 +       * beginning of the queue array.
@@ -3052,11 +3075,13 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              priv->fq[priv->num_fqs++].flowid = (u16)i;
 +      }
 +
-+      for (i = 0; i < dpaa2_eth_queue_count(priv); i++) {
-+              priv->fq[priv->num_fqs].type = DPAA2_RX_FQ;
-+              priv->fq[priv->num_fqs].consume = dpaa2_eth_rx;
-+              priv->fq[priv->num_fqs++].flowid = (u16)i;
-+      }
++      for (i = 0; i < dpaa2_eth_tc_count(priv); i++)
++              for (j = 0; j < dpaa2_eth_queue_count(priv); j++) {
++                      priv->fq[priv->num_fqs].type = DPAA2_RX_FQ;
++                      priv->fq[priv->num_fqs].consume = dpaa2_eth_rx;
++                      priv->fq[priv->num_fqs].tc = (u8)i;
++                      priv->fq[priv->num_fqs++].flowid = (u16)j;
++              }
 +
 +#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE
 +      /* We have exactly one Rx error queue per DPNI */
@@ -3299,9 +3324,6 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              dev_warn(dev, "Tx data offset (%d) not a multiple of 64B",
 +                       priv->tx_data_offset);
 +
-+      /* Accommodate software annotation space (SWA) */
-+      priv->tx_data_offset += DPAA2_ETH_SWA_SIZE;
-+
 +      /* Enable congestion notifications for Tx queues */
 +      err = setup_tx_congestion(priv);
 +      if (err)
@@ -3357,39 +3379,111 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      kfree(priv->cscn_unaligned);
 +}
 +
-+int setup_fqs_taildrop(struct dpaa2_eth_priv *priv,
-+                     bool enable)
++static int set_queue_taildrop(struct dpaa2_eth_priv *priv,
++                            struct dpni_taildrop *td)
 +{
 +      struct device *dev = priv->net_dev->dev.parent;
-+      struct dpni_taildrop td;
-+      int err = 0, i;
++      int err, i;
 +
-+      td.enable = enable;
-+      td.threshold = DPAA2_ETH_TAILDROP_THRESH;
-+
-+      if (enable) {
-+              priv->num_bufs = DPAA2_ETH_NUM_BUFS_TD;
-+              priv->refill_thresh = DPAA2_ETH_REFILL_THRESH_TD;
-+      } else {
-+              priv->num_bufs = DPAA2_ETH_NUM_BUFS_FC /
-+                      priv->num_channels;
-+              priv->refill_thresh = priv->num_bufs - DPAA2_ETH_BUFS_PER_CMD;
-+      }
 +
 +      for (i = 0; i < priv->num_fqs; i++) {
 +              if (priv->fq[i].type != DPAA2_RX_FQ)
 +                      continue;
 +
 +              err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token,
-+                                      DPNI_CP_QUEUE, DPNI_QUEUE_RX, 0,
-+                                      priv->fq[i].flowid, &td);
++                                      DPNI_CP_QUEUE, DPNI_QUEUE_RX,
++                                      priv->fq[i].tc, priv->fq[i].flowid,
++                                      td);
 +              if (err) {
 +                      dev_err(dev, "dpni_set_taildrop() failed (%d)\n", err);
-+                      break;
++                      return err;
 +              }
 +      }
 +
-+      return err;
++      return 0;
++}
++
++static int set_group_taildrop(struct dpaa2_eth_priv *priv,
++                            struct dpni_taildrop *td)
++{
++      struct device *dev = priv->net_dev->dev.parent;
++      struct dpni_taildrop disable_td, *tc_td;
++      int i, err;
++
++      memset(&disable_td, 0, sizeof(struct dpni_taildrop));
++      for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
++              if (td->enable && dpaa2_eth_is_pfc_enabled(priv, i))
++                      /* Do not set taildrop thresholds for PFC-enabled
++                       * traffic classes. We will enable congestion
++                       * notifications for them.
++                       */
++                      tc_td = &disable_td;
++              else
++                      tc_td = td;
++
++              err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token,
++                                      DPNI_CP_GROUP, DPNI_QUEUE_RX,
++                                      i, 0, tc_td);
++              if (err) {
++                      dev_err(dev, "dpni_set_taildrop() failed (%d)\n", err);
++                      return err;
++              }
++      }
++      return 0;
++}
++
++/* Enable/disable Rx FQ taildrop
++ *
++ * Rx FQ taildrop is mutually exclusive with flow control and it only gets
++ * disabled when FC is active. Depending on FC status, we need to compute
++ * the maximum number of buffers in the pool differently, so use the
++ * opportunity to update max number of buffers as well.
++ */
++int set_rx_taildrop(struct dpaa2_eth_priv *priv)
++{
++      enum dpaa2_eth_td_cfg cfg = dpaa2_eth_get_td_type(priv);
++      struct dpni_taildrop td_queue, td_group;
++      int err = 0;
++
++      switch (cfg) {
++      case DPAA2_ETH_TD_NONE:
++              memset(&td_queue, 0, sizeof(struct dpni_taildrop));
++              memset(&td_group, 0, sizeof(struct dpni_taildrop));
++              priv->num_bufs = DPAA2_ETH_NUM_BUFS_FC /
++                                      priv->num_channels;
++              break;
++      case DPAA2_ETH_TD_QUEUE:
++              memset(&td_group, 0, sizeof(struct dpni_taildrop));
++              td_queue.enable = 1;
++              td_queue.units = DPNI_CONGESTION_UNIT_BYTES;
++              td_queue.threshold = DPAA2_ETH_TAILDROP_THRESH /
++                                   dpaa2_eth_tc_count(priv);
++              priv->num_bufs = DPAA2_ETH_NUM_BUFS_TD;
++              break;
++      case DPAA2_ETH_TD_GROUP:
++              memset(&td_queue, 0, sizeof(struct dpni_taildrop));
++              td_group.enable = 1;
++              td_group.units = DPNI_CONGESTION_UNIT_FRAMES;
++              td_group.threshold = NAPI_POLL_WEIGHT *
++                                   dpaa2_eth_queue_count(priv);
++              priv->num_bufs = NAPI_POLL_WEIGHT *
++                                      dpaa2_eth_tc_count(priv);
++              break;
++      default:
++              break;
++      }
++
++      err = set_queue_taildrop(priv, &td_queue);
++      if (err)
++              return err;
++
++      err = set_group_taildrop(priv, &td_group);
++      if (err)
++              return err;
++
++      priv->refill_thresh = priv->num_bufs - DPAA2_ETH_BUFS_PER_CMD;
++
++      return 0;
 +}
 +
 +static int setup_rx_flow(struct dpaa2_eth_priv *priv,
@@ -3402,7 +3496,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      int err;
 +
 +      err = dpni_get_queue(priv->mc_io, 0, priv->mc_token,
-+                           DPNI_QUEUE_RX, 0, fq->flowid, &q, &qid);
++                           DPNI_QUEUE_RX, fq->tc, fq->flowid, &q, &qid);
 +      if (err) {
 +              dev_err(dev, "dpni_get_queue() failed (%d)\n", err);
 +              return err;
@@ -3415,7 +3509,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      q.destination.priority = 1;
 +      q.user_context = (u64)fq;
 +      err = dpni_set_queue(priv->mc_io, 0, priv->mc_token,
-+                           DPNI_QUEUE_RX, 0, fq->flowid, q_opt, &q);
++                           DPNI_QUEUE_RX, fq->tc, fq->flowid, q_opt, &q);
 +      if (err) {
 +              dev_err(dev, "dpni_set_queue() failed (%d)\n", err);
 +              return err;
@@ -3612,7 +3706,13 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              dist_cfg.dist_mode = DPNI_DIST_MODE_HASH;
 +      }
 +
-+      err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg);
++      for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
++              err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, i,
++                                        &dist_cfg);
++              if (err)
++                      break;
++      }
++
 +      dma_unmap_single(dev, dist_cfg.key_cfg_iova,
 +                       DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE);
 +      if (err)
@@ -3639,6 +3739,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      pools_params.num_dpbp = 1;
 +      pools_params.pools[0].dpbp_id = priv->dpbp_dev->obj_desc.id;
 +      pools_params.pools[0].backup_pool = 0;
++      pools_params.pools[0].priority_mask = 0xff;
 +      pools_params.pools[0].buffer_size = DPAA2_ETH_RX_BUF_SIZE;
 +      err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params);
 +      if (err) {
@@ -4124,6 +4225,264 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              device_remove_file(dev, &dpaa2_eth_attrs[i]);
 +}
 +
++#ifdef CONFIG_FSL_DPAA2_ETH_DCB
++static int dpaa2_eth_dcbnl_ieee_getpfc(struct net_device *net_dev,
++                                     struct ieee_pfc *pfc)
++{
++      struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
++      struct dpni_congestion_notification_cfg notification_cfg;
++      struct dpni_link_state state;
++      int err, i;
++
++      pfc->pfc_cap = dpaa2_eth_tc_count(priv);
++
++      err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state);
++      if (err) {
++              netdev_err(net_dev, "ERROR %d getting link state", err);
++              return err;
++      }
++
++      if (!(state.options & DPNI_LINK_OPT_PFC_PAUSE))
++              return 0;
++
++      priv->pfc.pfc_en = 0;
++      for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
++              err = dpni_get_congestion_notification(priv->mc_io, 0,
++                                                     priv->mc_token,
++                                                     DPNI_QUEUE_RX,
++                                                     i, &notification_cfg);
++              if (err) {
++                      netdev_err(net_dev, "Error %d getting congestion notif",
++                                 err);
++                      return err;
++              }
++
++              if (notification_cfg.threshold_entry)
++                      priv->pfc.pfc_en |= 1 << i;
++      }
++
++      pfc->pfc_en = priv->pfc.pfc_en;
++      pfc->mbc = priv->pfc.mbc;
++      pfc->delay = priv->pfc.delay;
++
++      return 0;
++}
++
++/* Configure ingress classification based on VLAN PCP */
++static int set_vlan_qos(struct dpaa2_eth_priv *priv)
++{
++      struct device *dev = priv->net_dev->dev.parent;
++      struct dpkg_profile_cfg kg_cfg = {0};
++      struct dpni_qos_tbl_cfg qos_cfg = {0};
++      struct dpni_rule_cfg key_params;
++      u8 *params_iova;
++      __be16 key, mask = cpu_to_be16(VLAN_PRIO_MASK);
++      int err = 0, i, j = 0;
++
++      if (priv->vlan_clsf_set)
++              return 0;
++
++      params_iova = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL);
++      if (!params_iova)
++              return -ENOMEM;
++
++      kg_cfg.num_extracts = 1;
++      kg_cfg.extracts[0].type = DPKG_EXTRACT_FROM_HDR;
++      kg_cfg.extracts[0].extract.from_hdr.prot = NET_PROT_VLAN;
++      kg_cfg.extracts[0].extract.from_hdr.type = DPKG_FULL_FIELD;
++      kg_cfg.extracts[0].extract.from_hdr.field = NH_FLD_VLAN_TCI;
++
++      err = dpni_prepare_key_cfg(&kg_cfg, params_iova);
++      if (err) {
++              dev_err(dev, "dpkg_prepare_key_cfg failed: %d\n", err);
++              goto out_free;
++      }
++
++      /* Set QoS table */
++      qos_cfg.default_tc = 0;
++      qos_cfg.discard_on_miss = 0;
++      qos_cfg.key_cfg_iova = dma_map_single(dev, params_iova,
++                                            DPAA2_CLASSIFIER_DMA_SIZE,
++                                            DMA_TO_DEVICE);
++      if (dma_mapping_error(dev, qos_cfg.key_cfg_iova)) {
++              dev_err(dev, "%s: DMA mapping failed\n", __func__);
++              err = -ENOMEM;
++              goto out_free;
++      }
++      err = dpni_set_qos_table(priv->mc_io, 0, priv->mc_token, &qos_cfg);
++      dma_unmap_single(dev, qos_cfg.key_cfg_iova,
++                       DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE);
++
++      if (err) {
++              dev_err(dev, "dpni_set_qos_table failed: %d\n", err);
++              goto out_free;
++      }
++
++      key_params.key_size = sizeof(key);
++
++      if (dpaa2_eth_fs_mask_enabled(priv)) {
++              key_params.mask_iova = dma_map_single(dev, &mask, sizeof(mask),
++                                                    DMA_TO_DEVICE);
++              if (dma_mapping_error(dev, key_params.mask_iova)) {
++                      dev_err(dev, "DMA mapping failed %s\n", __func__);
++                      err = -ENOMEM;
++                      goto out_free;
++              }
++      } else {
++              key_params.mask_iova = 0;
++      }
++
++      key_params.key_iova = dma_map_single(dev, &key, sizeof(key),
++                                           DMA_TO_DEVICE);
++      if (dma_mapping_error(dev, key_params.key_iova)) {
++              dev_err(dev, "%s: DMA mapping failed\n", __func__);
++              err = -ENOMEM;
++              goto out_unmap_mask;
++      }
++
++      for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
++              key = cpu_to_be16(i << VLAN_PRIO_SHIFT);
++              dma_sync_single_for_device(dev, key_params.key_iova,
++                                         sizeof(key), DMA_TO_DEVICE);
++
++              err = dpni_add_qos_entry(priv->mc_io, 0, priv->mc_token,
++                                       &key_params, i, j++);
++              if (err) {
++                      dev_err(dev, "dpni_add_qos_entry failed: %d\n", err);
++                      goto out_unmap;
++              }
++      }
++
++      priv->vlan_clsf_set = true;
++
++out_unmap:
++      dma_unmap_single(dev, key_params.key_iova, sizeof(key), DMA_TO_DEVICE);
++out_unmap_mask:
++      if (key_params.mask_iova)
++              dma_unmap_single(dev, key_params.mask_iova, sizeof(mask),
++                               DMA_TO_DEVICE);
++out_free:
++      kfree(params_iova);
++      return err;
++}
++
++static int dpaa2_eth_dcbnl_ieee_setpfc(struct net_device *net_dev,
++                                     struct ieee_pfc *pfc)
++{
++      struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
++      struct dpni_congestion_notification_cfg notification_cfg = {0};
++      struct dpni_link_state state = {0};
++      struct dpni_link_cfg cfg = {0};
++      int err = 0, i;
++
++      if (priv->pfc.pfc_en == pfc->pfc_en)
++              /* Same enabled mask, nothing to be done */
++              return 0;
++
++      err = set_vlan_qos(priv);
++      if (err)
++              return err;
++
++      err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state);
++      if (err) {
++              netdev_err(net_dev, "ERROR %d getting link state", err);
++              return err;
++      }
++
++      cfg.rate = state.rate;
++      cfg.options = state.options;
++      if (pfc->pfc_en)
++              cfg.options |= DPNI_LINK_OPT_PFC_PAUSE;
++      else
++              cfg.options &= ~DPNI_LINK_OPT_PFC_PAUSE;
++
++      err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg);
++      if (err) {
++              netdev_err(net_dev, "ERROR %d setting link cfg", err);
++              return err;
++      }
++
++      memcpy(&priv->pfc, pfc, sizeof(priv->pfc));
++
++      err = set_rx_taildrop(priv);
++      if (err)
++              return err;
++
++      /* configure congestion notifications */
++      notification_cfg.notification_mode = DPNI_CONG_OPT_FLOW_CONTROL;
++      notification_cfg.units = DPNI_CONGESTION_UNIT_FRAMES;
++      notification_cfg.message_iova = 0ULL;
++      notification_cfg.message_ctx = 0ULL;
++
++      for (i = 0; i < dpaa2_eth_tc_count(priv); i++) {
++              if (dpaa2_eth_is_pfc_enabled(priv, i)) {
++                      notification_cfg.threshold_entry = NAPI_POLL_WEIGHT;
++                      notification_cfg.threshold_exit = NAPI_POLL_WEIGHT / 2;
++              } else {
++                      notification_cfg.threshold_entry = 0;
++                      notification_cfg.threshold_exit = 0;
++              }
++
++              err = dpni_set_congestion_notification(priv->mc_io, 0,
++                                                     priv->mc_token,
++                                                     DPNI_QUEUE_RX,
++                                                     i, &notification_cfg);
++              if (err) {
++                      netdev_err(net_dev, "Error %d setting congestion notif",
++                                 err);
++                      return err;
++              }
++      }
++
++      return 0;
++}
++
++static u8 dpaa2_eth_dcbnl_getdcbx(struct net_device *net_dev)
++{
++      struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
++
++      return priv->dcbx_mode;
++}
++
++static u8 dpaa2_eth_dcbnl_setdcbx(struct net_device *net_dev, u8 mode)
++{
++      struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
++
++      priv->dcbx_mode = mode;
++      return 0;
++}
++
++static u8 dpaa2_eth_dcbnl_getcap(struct net_device *net_dev, int capid, u8 *cap)
++{
++      struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
++
++      switch (capid) {
++      case DCB_CAP_ATTR_PFC:
++              *cap = true;
++              break;
++      case DCB_CAP_ATTR_PFC_TCS:
++              *cap = 1 << dpaa2_eth_tc_count(priv);
++              break;
++      case DCB_CAP_ATTR_DCBX:
++              *cap = priv->dcbx_mode;
++              break;
++      default:
++              *cap = false;
++              break;
++      }
++
++      return 0;
++}
++
++const struct dcbnl_rtnl_ops dpaa2_eth_dcbnl_ops = {
++      .ieee_getpfc    = dpaa2_eth_dcbnl_ieee_getpfc,
++      .ieee_setpfc    = dpaa2_eth_dcbnl_ieee_setpfc,
++      .getdcbx        = dpaa2_eth_dcbnl_getdcbx,
++      .setdcbx        = dpaa2_eth_dcbnl_setdcbx,
++      .getcap         = dpaa2_eth_dcbnl_getcap,
++};
++#endif
++
 +static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
 +{
 +      struct device *dev;
@@ -4152,7 +4511,8 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
 +                                   &priv->mc_io);
 +      if (err) {
-+              dev_err(dev, "MC portal allocation failed\n");
++              dev_dbg(dev, "MC portal allocation failed\n");
++              err = -EPROBE_DEFER;
 +              goto err_portal_alloc;
 +      }
 +
@@ -4178,10 +4538,6 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      if (err)
 +              goto err_bind;
 +
-+      /* Add a NAPI context for each channel */
-+      add_ch_napi(priv);
-+      enable_ch_napi(priv);
-+
 +      /* Percpu statistics */
 +      priv->percpu_stats = alloc_percpu(*priv->percpu_stats);
 +      if (!priv->percpu_stats) {
@@ -4224,6 +4580,14 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              goto err_alloc_rings;
 +
 +      net_dev->ethtool_ops = &dpaa2_ethtool_ops;
++#ifdef CONFIG_FSL_DPAA2_ETH_DCB
++      net_dev->dcbnl_ops = &dpaa2_eth_dcbnl_ops;
++      priv->dcbx_mode = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
++#endif
++
++      /* Add a NAPI context for each channel */
++      add_ch_napi(priv);
++      enable_ch_napi(priv);
 +
 +      err = setup_irqs(dpni_dev);
 +      if (err) {
@@ -4287,6 +4651,9 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#endif
 +      dpaa2_eth_sysfs_remove(&net_dev->dev);
 +
++      disable_ch_napi(priv);
++      del_ch_napi(priv);
++
 +      unregister_netdev(net_dev);
 +      dev_info(net_dev->dev.parent, "Removed interface %s\n", net_dev->name);
 +
@@ -4298,9 +4665,6 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      free_rings(priv);
 +      free_percpu(priv->percpu_stats);
 +      free_percpu(priv->percpu_extras);
-+
-+      disable_ch_napi(priv);
-+      del_ch_napi(priv);
 +      free_dpbp(priv);
 +      free_dpio(priv);
 +      free_dpni(priv);
@@ -4356,7 +4720,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +module_exit(dpaa2_eth_driver_exit);
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h
-@@ -0,0 +1,460 @@
+@@ -0,0 +1,499 @@
 +/* Copyright 2014-2015 Freescale Semiconductor Inc.
 + *
 + * Redistribution and use in source and binary forms, with or without
@@ -4392,6 +4756,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#define __DPAA2_ETH_H
 +
 +#include <linux/atomic.h>
++#include <linux/dcbnl.h>
 +#include <linux/netdevice.h>
 +#include <linux/if_vlan.h>
 +#include "../../fsl-mc/include/dpaa2-io.h"
@@ -4455,7 +4820,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#define DPAA2_ETH_RX_BUF_ALIGN                64
 +#define DPAA2_ETH_RX_BUF_ALIGN_V1     256
 +#define DPAA2_ETH_NEEDED_HEADROOM(p_priv) \
-+      ((p_priv)->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN)
++      ((p_priv)->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN - HH_DATA_MOD)
 +
 +/* rx_extra_head prevents reallocations in L3 processing. */
 +#define DPAA2_ETH_SKB_SIZE \
@@ -4473,17 +4838,19 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +/* PTP nominal frequency 1GHz */
 +#define DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS 1
 +
-+/* Leave enough extra space in the headroom to make sure the skb is
-+ * not realloc'd in forwarding scenarios.
-+ */
-+#define DPAA2_ETH_RX_HEAD_ROOM                192
-+
 +/* We are accommodating a skb backpointer and some S/G info
 + * in the frame's software annotation. The hardware
 + * options are either 0 or 64, so we choose the latter.
 + */
 +#define DPAA2_ETH_SWA_SIZE            64
 +
++/* Extra headroom space requested to hardware, in order to make sure there's
++ * no realloc'ing in forwarding scenarios
++ */
++#define DPAA2_ETH_RX_HEAD_ROOM \
++      (DPAA2_ETH_TX_HWA_SIZE - DPAA2_ETH_RX_HWA_SIZE + \
++       DPAA2_ETH_TX_BUF_ALIGN)
++
 +/* Must keep this struct smaller than DPAA2_ETH_SWA_SIZE */
 +struct dpaa2_eth_swa {
 +      struct sk_buff *skb;
@@ -4660,16 +5027,17 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      __u64 pull_err;
 +};
 +
++#define DPAA2_ETH_MAX_DPCONS          NR_CPUS
++#define DPAA2_ETH_MAX_TCS             8
++
 +/* Maximum number of queues associated with a DPNI */
-+#define DPAA2_ETH_MAX_RX_QUEUES               16
-+#define DPAA2_ETH_MAX_TX_QUEUES               NR_CPUS
++#define DPAA2_ETH_MAX_RX_QUEUES               (DPNI_MAX_DIST_SIZE * DPAA2_ETH_MAX_TCS)
++#define DPAA2_ETH_MAX_TX_QUEUES               DPNI_MAX_SENDERS
 +#define DPAA2_ETH_MAX_RX_ERR_QUEUES   1
 +#define DPAA2_ETH_MAX_QUEUES          (DPAA2_ETH_MAX_RX_QUEUES + \
 +                                      DPAA2_ETH_MAX_TX_QUEUES + \
 +                                      DPAA2_ETH_MAX_RX_ERR_QUEUES)
 +
-+#define DPAA2_ETH_MAX_DPCONS          NR_CPUS
-+
 +enum dpaa2_eth_fq_type {
 +      DPAA2_RX_FQ = 0,
 +      DPAA2_TX_CONF_FQ,
@@ -4682,6 +5050,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      u32 fqid;
 +      u32 tx_qdbin;
 +      u16 flowid;
++      u8 tc;
 +      int target_cpu;
 +      struct dpaa2_eth_channel *channel;
 +      enum dpaa2_eth_fq_type type;
@@ -4788,6 +5157,10 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      struct dpaa2_eth_cls_rule *cls_rule;
 +
 +      struct dpni_tx_shaping_cfg shaping_cfg;
++
++      u8 dcbx_mode;
++      struct ieee_pfc pfc;
++      bool vlan_clsf_set;
 +};
 +
 +#define dpaa2_eth_hash_enabled(priv)  \
@@ -4813,13 +5186,43 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      return priv->dpni_attrs.num_queues;
 +}
 +
++static inline int dpaa2_eth_tc_count(struct dpaa2_eth_priv *priv)
++{
++      return priv->dpni_attrs.num_tcs;
++}
++
++static inline bool dpaa2_eth_is_pfc_enabled(struct dpaa2_eth_priv *priv,
++                                          int traffic_class)
++{
++      return priv->pfc.pfc_en & (1 << traffic_class);
++}
++
++enum dpaa2_eth_td_cfg {
++      DPAA2_ETH_TD_NONE,
++      DPAA2_ETH_TD_QUEUE,
++      DPAA2_ETH_TD_GROUP
++};
++
++static inline enum dpaa2_eth_td_cfg
++dpaa2_eth_get_td_type(struct dpaa2_eth_priv *priv)
++{
++      bool pfc_enabled = !!(priv->pfc.pfc_en);
++
++      if (pfc_enabled)
++              return DPAA2_ETH_TD_GROUP;
++      else if (priv->tx_pause_frames)
++              return DPAA2_ETH_TD_NONE;
++      else
++              return DPAA2_ETH_TD_QUEUE;
++}
++
 +void check_cls_support(struct dpaa2_eth_priv *priv);
 +
-+int setup_fqs_taildrop(struct dpaa2_eth_priv *priv, bool enable);
++int set_rx_taildrop(struct dpaa2_eth_priv *priv);
 +#endif        /* __DPAA2_H */
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c
-@@ -0,0 +1,856 @@
+@@ -0,0 +1,864 @@
 +/* Copyright 2014-2015 Freescale Semiconductor Inc.
 + *
 + * Redistribution and use in source and binary forms, with or without
@@ -5052,7 +5455,8 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      if (current_tx_pause == pause->tx_pause)
 +              goto out;
 +
-+      err = setup_fqs_taildrop(priv, !pause->tx_pause);
++      priv->tx_pause_frames = pause->tx_pause;
++      err = set_rx_taildrop(priv);
 +      if (err)
 +              netdev_dbg(net_dev, "ERROR %d configuring taildrop", err);
 +
@@ -5498,7 +5902,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      struct dpni_rule_cfg rule_cfg;
 +      struct dpni_fs_action_cfg fs_act = { 0 };
 +      void *dma_mem;
-+      int err = 0;
++      int err = 0, tc;
 +
 +      if (!dpaa2_eth_fs_enabled(priv)) {
 +              netdev_err(net_dev, "dev does not support steering!\n");
@@ -5541,12 +5945,19 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      else
 +              fs_act.flow_id = fs->ring_cookie;
 +
-+      if (add)
-+              err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token,
-+                                      0, fs->location, &rule_cfg, &fs_act);
-+      else
-+              err = dpni_remove_fs_entry(priv->mc_io, 0, priv->mc_token,
-+                                         0, &rule_cfg);
++      for (tc = 0; tc < dpaa2_eth_tc_count(priv); tc++) {
++              if (add)
++                      err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token,
++                                              tc, fs->location, &rule_cfg,
++                                              &fs_act);
++              else
++                      err = dpni_remove_fs_entry(priv->mc_io, 0,
++                                                 priv->mc_token, tc,
++                                                 &rule_cfg);
++
++              if (err)
++                      break;
++      }
 +
 +      dma_unmap_single(dev, rule_cfg.key_iova,
 +                       rule_cfg.key_size * 2, DMA_TO_DEVICE);
@@ -5857,7 +6268,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#endif /* __FSL_DPKG_H_ */
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h
-@@ -0,0 +1,600 @@
+@@ -0,0 +1,658 @@
 +/* Copyright 2013-2016 Freescale Semiconductor Inc.
 + * Copyright 2016 NXP
 + *
@@ -5897,9 +6308,11 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#define DPNI_VER_MAJOR                                7
 +#define DPNI_VER_MINOR                                0
 +#define DPNI_CMD_BASE_VERSION                 1
++#define DPNI_CMD_2ND_VERSION                  2
 +#define DPNI_CMD_ID_OFFSET                    4
 +
 +#define DPNI_CMD(id)  (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_BASE_VERSION)
++#define DPNI_CMD_V2(id)       (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_2ND_VERSION)
 +
 +#define DPNI_CMDID_OPEN                                       DPNI_CMD(0x801)
 +#define DPNI_CMDID_CLOSE                              DPNI_CMD(0x800)
@@ -5922,7 +6335,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#define DPNI_CMDID_GET_IRQ_STATUS                     DPNI_CMD(0x016)
 +#define DPNI_CMDID_CLEAR_IRQ_STATUS                   DPNI_CMD(0x017)
 +
-+#define DPNI_CMDID_SET_POOLS                          DPNI_CMD(0x200)
++#define DPNI_CMDID_SET_POOLS                          DPNI_CMD_V2(0x200)
 +#define DPNI_CMDID_SET_ERRORS_BEHAVIOR                        DPNI_CMD(0x20B)
 +
 +#define DPNI_CMDID_GET_QDID                           DPNI_CMD(0x210)
@@ -5945,6 +6358,8 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +
 +#define DPNI_CMDID_SET_RX_TC_DIST                     DPNI_CMD(0x235)
 +
++#define DPNI_CMDID_SET_QOS_TBL                                DPNI_CMD(0x240)
++#define DPNI_CMDID_ADD_QOS_ENT                                DPNI_CMD(0x241)
 +#define DPNI_CMDID_ADD_FS_ENT                         DPNI_CMD(0x244)
 +#define DPNI_CMDID_REMOVE_FS_ENT                      DPNI_CMD(0x245)
 +#define DPNI_CMDID_CLR_FS_ENT                         DPNI_CMD(0x246)
@@ -5985,13 +6400,14 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +
 +#define DPNI_BACKUP_POOL(val, order)  (((val) & 0x1) << (order))
 +struct dpni_cmd_set_pools {
-+      /* cmd word 0 */
 +      u8 num_dpbp;
 +      u8 backup_pool_mask;
 +      __le16 pad;
-+      /* cmd word 0..4 */
-+      __le32 dpbp_id[DPNI_MAX_DPBP];
-+      /* cmd word 4..6 */
++      struct {
++              __le16 dpbp_id;
++              u8 priority_mask;
++              u8 pad;
++      } pool[DPNI_MAX_DPBP];
 +      __le16 buffer_size[DPNI_MAX_DPBP];
 +};
 +
@@ -6370,6 +6786,36 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      __le64 user_context;
 +};
 +
++#define DPNI_DISCARD_ON_MISS_SHIFT    0
++#define DPNI_DISCARD_ON_MISS_SIZE     1
++
++struct dpni_cmd_set_qos_table {
++      u32 pad;
++      u8 default_tc;
++      /* only the LSB */
++      u8 discard_on_miss;
++      u16 pad1[21];
++      u64 key_cfg_iova;
++};
++
++struct dpni_cmd_add_qos_entry {
++      u16 pad;
++      u8 tc_id;
++      u8 key_size;
++      u16 index;
++      u16 pad2;
++      u64 key_iova;
++      u64 mask_iova;
++};
++
++struct dpni_cmd_remove_qos_entry {
++      u8 pad1[3];
++      u8 key_size;
++      u32 pad2;
++      u64 key_iova;
++      u64 mask_iova;
++};
++
 +struct dpni_cmd_add_fs_entry {
 +      /* cmd word 0 */
 +      u16 options;
@@ -6457,10 +6903,33 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      u32 threshold_exit;
 +};
 +
++struct dpni_cmd_get_congestion_notification {
++      /* cmd word 0 */
++      u8 qtype;
++      u8 tc;
++};
++
++struct dpni_rsp_get_congestion_notification {
++      /* cmd word 0 */
++      u64 pad;
++      /* cmd word 1 */
++      u32 dest_id;
++      u16 notification_mode;
++      u8 dest_priority;
++      /* from LSB: dest_type: 4 units:2 */
++      u8 type_units;
++      /* cmd word 2 */
++      u64 message_iova;
++      /* cmd word 3 */
++      u64 message_ctx;
++      /* cmd word 4 */
++      u32 threshold_entry;
++      u32 threshold_exit;
++};
 +#endif /* _FSL_DPNI_CMD_H */
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c
-@@ -0,0 +1,1770 @@
+@@ -0,0 +1,1903 @@
 +/* Copyright 2013-2016 Freescale Semiconductor Inc.
 + * Copyright 2016 NXP
 + *
@@ -6661,7 +7130,10 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      cmd_params = (struct dpni_cmd_set_pools *)cmd.params;
 +      cmd_params->num_dpbp = cfg->num_dpbp;
 +      for (i = 0; i < DPNI_MAX_DPBP; i++) {
-+              cmd_params->dpbp_id[i] = cpu_to_le32(cfg->pools[i].dpbp_id);
++              cmd_params->pool[i].dpbp_id =
++                      cpu_to_le16(cfg->pools[i].dpbp_id);
++              cmd_params->pool[i].priority_mask =
++                      cfg->pools[i].priority_mask;
 +              cmd_params->buffer_size[i] =
 +                      cpu_to_le16(cfg->pools[i].buffer_size);
 +              cmd_params->backup_pool_mask |=
@@ -7837,6 +8309,82 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      return mc_send_command(mc_io, &cmd);
 +}
 +
++/*
++ * dpni_set_qos_table() - Set QoS mapping table
++ * @mc_io:    Pointer to MC portal's I/O object
++ * @cmd_flags:        Command flags; one or more of 'MC_CMD_FLAG_'
++ * @token:    Token of DPNI object
++ * @cfg:      QoS table configuration
++ *
++ * This function and all QoS-related functions require that
++ *'max_tcs > 1' was set at DPNI creation.
++ *
++ * warning: Before calling this function, call dpkg_prepare_key_cfg() to
++ *                    prepare the key_cfg_iova parameter
++ *
++ * Return:    '0' on Success; Error code otherwise.
++ */
++int dpni_set_qos_table(struct fsl_mc_io *mc_io,
++                     u32 cmd_flags,
++                     u16 token,
++                     const struct dpni_qos_tbl_cfg *cfg)
++{
++      struct dpni_cmd_set_qos_table *cmd_params;
++      struct mc_command cmd = { 0 };
++
++      /* prepare command */
++      cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_QOS_TBL,
++                                        cmd_flags,
++                                        token);
++      cmd_params = (struct dpni_cmd_set_qos_table *)cmd.params;
++      cmd_params->default_tc = cfg->default_tc;
++      cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova);
++      dpni_set_field(cmd_params->discard_on_miss,
++                     ENABLE,
++                     cfg->discard_on_miss);
++
++      /* send command to mc*/
++      return mc_send_command(mc_io, &cmd);
++}
++
++/**
++ * dpni_add_qos_entry() - Add QoS mapping entry (to select a traffic class)
++ * @mc_io:    Pointer to MC portal's I/O object
++ * @cmd_flags:        Command flags; one or more of 'MC_CMD_FLAG_'
++ * @token:    Token of DPNI object
++ * @cfg:      QoS rule to add
++ * @tc_id:    Traffic class selection (0-7)
++ * @index:    Location in the QoS table where to insert the entry.
++ *            Only relevant if MASKING is enabled for QoS classification on
++ *            this DPNI, it is ignored for exact match.
++ *
++ * Return:    '0' on Success; Error code otherwise.
++ */
++int dpni_add_qos_entry(struct fsl_mc_io *mc_io,
++                     u32 cmd_flags,
++                     u16 token,
++                     const struct dpni_rule_cfg *cfg,
++                     u8 tc_id,
++                     u16 index)
++{
++      struct dpni_cmd_add_qos_entry *cmd_params;
++      struct mc_command cmd = { 0 };
++
++      /* prepare command */
++      cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_QOS_ENT,
++                                        cmd_flags,
++                                        token);
++      cmd_params = (struct dpni_cmd_add_qos_entry *)cmd.params;
++      cmd_params->tc_id = tc_id;
++      cmd_params->key_size = cfg->key_size;
++      cmd_params->index = cpu_to_le16(index);
++      cmd_params->key_iova = cpu_to_le64(cfg->key_iova);
++      cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova);
++
++      /* send command to mc*/
++      return mc_send_command(mc_io, &cmd);
++}
++
 +/**
 + * dpni_add_fs_entry() - Add Flow Steering entry for a specific traffic class
 + *                    (to select a flow ID)
@@ -7961,6 +8509,60 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +}
 +
 +/**
++ * dpni_get_congestion_notification() - Get traffic class congestion
++ *    notification configuration
++ * @mc_io:    Pointer to MC portal's I/O object
++ * @cmd_flags:        Command flags; one or more of 'MC_CMD_FLAG_'
++ * @token:    Token of DPNI object
++ * @qtype:    Type of queue - Rx, Tx and Tx confirm types are supported
++ * @tc_id:    Traffic class selection (0-7)
++ * @cfg:      congestion notification configuration
++ *
++ * Return:    '0' on Success; error code otherwise.
++ */
++int dpni_get_congestion_notification(
++                      struct fsl_mc_io *mc_io,
++                      u32 cmd_flags,
++                      u16 token,
++                      enum dpni_queue_type qtype,
++                      u8 tc_id,
++                      struct dpni_congestion_notification_cfg *cfg)
++{
++      struct dpni_rsp_get_congestion_notification *rsp_params;
++      struct dpni_cmd_get_congestion_notification *cmd_params;
++      struct mc_command cmd = { 0 };
++      int err;
++
++      /* prepare command */
++      cmd.header = mc_encode_cmd_header(
++                              DPNI_CMDID_GET_CONGESTION_NOTIFICATION,
++                              cmd_flags,
++                              token);
++      cmd_params = (struct dpni_cmd_get_congestion_notification *)cmd.params;
++      cmd_params->qtype = qtype;
++      cmd_params->tc = tc_id;
++
++      /* send command to mc*/
++      err = mc_send_command(mc_io, &cmd);
++      if (err)
++              return err;
++
++      rsp_params = (struct dpni_rsp_get_congestion_notification *)cmd.params;
++      cfg->units = dpni_get_field(rsp_params->type_units, CONG_UNITS);
++      cfg->threshold_entry = le32_to_cpu(rsp_params->threshold_entry);
++      cfg->threshold_exit = le32_to_cpu(rsp_params->threshold_exit);
++      cfg->message_ctx = le64_to_cpu(rsp_params->message_ctx);
++      cfg->message_iova = le64_to_cpu(rsp_params->message_iova);
++      cfg->notification_mode = le16_to_cpu(rsp_params->notification_mode);
++      cfg->dest_cfg.dest_id = le32_to_cpu(rsp_params->dest_id);
++      cfg->dest_cfg.priority = rsp_params->dest_priority;
++      cfg->dest_cfg.dest_type = dpni_get_field(rsp_params->type_units,
++                                               DEST_TYPE);
++
++      return 0;
++}
++
++/**
 + * dpni_set_queue() - Set queue parameters
 + * @mc_io:    Pointer to MC portal's I/O object
 + * @cmd_flags:        Command flags; one or more of 'MC_CMD_FLAG_'
@@ -8233,7 +8835,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +}
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.h
-@@ -0,0 +1,989 @@
+@@ -0,0 +1,1053 @@
 +/* Copyright 2013-2016 Freescale Semiconductor Inc.
 + * Copyright 2016 NXP
 + *
@@ -8288,6 +8890,14 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 + * Maximum number of buffer pools per DPNI
 + */
 +#define DPNI_MAX_DPBP                         8
++/**
++ * Maximum number of senders
++ */
++#define DPNI_MAX_SENDERS                      8
++/**
++ * Maximum distribution size
++ */
++#define DPNI_MAX_DIST_SIZE                    8
 +
 +/**
 + * All traffic classes considered; see dpni_set_queue()
@@ -8359,13 +8969,15 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      /**
 +       * struct pools - Buffer pools parameters
 +       * @dpbp_id: DPBP object ID
++       * @priority_mask: priorities served by DPBP
 +       * @buffer_size: Buffer size
 +       * @backup_pool: Backup pool
 +       */
 +      struct {
-+              int     dpbp_id;
++              u16     dpbp_id;
++              u8      priority_mask;
 +              u16     buffer_size;
-+              int     backup_pool;
++              u8      backup_pool;
 +      } pools[DPNI_MAX_DPBP];
 +};
 +
@@ -8745,6 +9357,10 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 + * Enable a-symmetric pause frames
 + */
 +#define DPNI_LINK_OPT_ASYM_PAUSE      0x0000000000000008ULL
++/**
++ * Enable priority flow control pause frames
++ */
++#define DPNI_LINK_OPT_PFC_PAUSE               0x0000000000000010ULL
 +
 +/**
 + * struct - Structure representing DPNI link configuration
@@ -8894,6 +9510,26 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +                       u8 *key_cfg_buf);
 +
 +/**
++ * struct dpni_qos_tbl_cfg - Structure representing QOS table configuration
++ * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with
++ *            key extractions to be used as the QoS criteria by calling
++ *            dpkg_prepare_key_cfg()
++ * @discard_on_miss: Set to '1' to discard frames in case of no match (miss);
++ *            '0' to use the 'default_tc' in such cases
++ * @default_tc: Used in case of no-match and 'discard_on_miss'= 0
++ */
++struct dpni_qos_tbl_cfg {
++      u64 key_cfg_iova;
++      int discard_on_miss;
++      u8 default_tc;
++};
++
++int dpni_set_qos_table(struct fsl_mc_io *mc_io,
++                     u32 cmd_flags,
++                     u16 token,
++                     const struct dpni_qos_tbl_cfg *cfg);
++
++/**
 + * struct dpni_rx_tc_dist_cfg - Rx traffic class distribution configuration
 + * @dist_size: Set the distribution size;
 + *    supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96,
@@ -9086,6 +9722,12 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 + * sw-portal's DQRR, the DQRI interrupt is asserted immediately (if enabled)
 + */
 +#define DPNI_CONG_OPT_INTR_COALESCING_DISABLED        0x00000020
++/**
++ * This congestion will trigger flow control or priority flow control.
++ * This will have effect only if flow control is enabled with
++ * dpni_set_link_cfg().
++ */
++#define DPNI_CONG_OPT_FLOW_CONTROL    0x00000040
 +
 +/**
 + * struct dpni_congestion_notification_cfg - congestion notification
@@ -9119,6 +9761,14 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +                      u8 tc_id,
 +                      const struct dpni_congestion_notification_cfg *cfg);
 +
++int dpni_get_congestion_notification(
++                      struct fsl_mc_io *mc_io,
++                      u32 cmd_flags,
++                      u16 token,
++                      enum dpni_queue_type qtype,
++                      u8 tc_id,
++                      struct dpni_congestion_notification_cfg *cfg);
++
 +/**
 + * struct dpni_taildrop - Structure representing the taildrop
 + * @enable:   Indicates whether the taildrop is active or not.
@@ -9165,6 +9815,22 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      u8      key_size;
 +};
 +
++int dpni_add_qos_entry(struct fsl_mc_io *mc_io,
++                     u32 cmd_flags,
++                     u16 token,
++                     const struct dpni_rule_cfg *cfg,
++                     u8 tc_id,
++                     u16 index);
++
++int dpni_remove_qos_entry(struct fsl_mc_io *mc_io,
++                        u32 cmd_flags,
++                        u16 token,
++                        const struct dpni_rule_cfg *cfg);
++
++int dpni_clear_qos_table(struct fsl_mc_io *mc_io,
++                       u32 cmd_flags,
++                       u16 token);
++
 +/**
 + * Discard matching traffic.  If set, this takes precedence over any other
 + * configuration and matching traffic is always discarded.
@@ -15718,7 +16384,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      return 0;
 +}
 +
-+void ethsw_port_get_stats(struct net_device *netdev,
++struct rtnl_link_stats64 *ethsw_port_get_stats(struct net_device *netdev,
 +                        struct rtnl_link_stats64 *storage)
 +{
 +      struct ethsw_port_priv  *port_priv = netdev_priv(netdev);
@@ -15778,7 +16444,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      if (err)
 +              goto error;
 +
-+      return;
++      return storage;
 +
 +error:
 +      netdev_err(netdev, "dpsw_if_get_counter err %d\n", err);
@@ -19125,7 +19791,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      return 0;
 +}
 +
-+void evb_port_get_stats(struct net_device *netdev,
++struct rtnl_link_stats64 *evb_port_get_stats(struct net_device *netdev,
 +                      struct rtnl_link_stats64 *storage)
 +{
 +      struct evb_port_priv    *port_priv = netdev_priv(netdev);
@@ -19202,7 +19868,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      if (unlikely(err))
 +              goto error;
 +
-+      return;
++      return storage;
 +
 +error:
 +      netdev_err(netdev, "dpdmux_if_get_counter err %d\n", err);
@@ -20892,7 +21558,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +#endif /* __FSL_DPMAC_H */
 --- /dev/null
 +++ b/drivers/staging/fsl-dpaa2/mac/mac.c
-@@ -0,0 +1,666 @@
+@@ -0,0 +1,670 @@
 +/* Copyright 2015 Freescale Semiconductor Inc.
 + *
 + * Redistribution and use in source and binary forms, with or without
@@ -21014,15 +21680,6 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              dev_err(&priv->mc_dev->dev, "dpmac_set_link_state: %d\n", err);
 +}
 +
-+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
-+static netdev_tx_t dpaa2_mac_drop_frame(struct sk_buff *skb,
-+                                      struct net_device *dev)
-+{
-+      /* we don't support I/O for now, drop the frame */
-+      dev_kfree_skb_any(skb);
-+      return NETDEV_TX_OK;
-+}
-+
 +static int dpaa2_mac_open(struct net_device *netdev)
 +{
 +      /* start PHY state machine */
@@ -21047,6 +21704,15 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      return 0;
 +}
 +
++#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
++static netdev_tx_t dpaa2_mac_drop_frame(struct sk_buff *skb,
++                                      struct net_device *dev)
++{
++      /* we don't support I/O for now, drop the frame */
++      dev_kfree_skb_any(skb);
++      return NETDEV_TX_OK;
++}
++
 +static int dpaa2_mac_get_settings(struct net_device *netdev,
 +                                struct ethtool_cmd *cmd)
 +{
@@ -21059,7 +21725,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      return phy_ethtool_sset(netdev->phydev, cmd);
 +}
 +
-+static void dpaa2_mac_get_stats(struct net_device *netdev,
++static struct rtnl_link_stats64 *dpaa2_mac_get_stats(struct net_device *netdev,
 +                              struct rtnl_link_stats64 *storage)
 +{
 +      struct dpaa2_mac_priv   *priv = netdev_priv(netdev);
@@ -21122,9 +21788,10 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      if (err)
 +              goto error;
 +
-+      return;
++      return storage;
 +error:
 +      netdev_err(netdev, "dpmac_get_counter err %d\n", err);
++      return storage;
 +}
 +
 +static struct {
@@ -21207,9 +21874,9 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +}
 +
 +static const struct net_device_ops dpaa2_mac_ndo_ops = {
-+      .ndo_start_xmit         = &dpaa2_mac_drop_frame,
 +      .ndo_open               = &dpaa2_mac_open,
 +      .ndo_stop               = &dpaa2_mac_stop,
++      .ndo_start_xmit         = &dpaa2_mac_drop_frame,
 +      .ndo_get_stats64        = &dpaa2_mac_get_stats,
 +};
 +
@@ -21437,10 +22104,9 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      }
 +#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */
 +
-+      /* probe the PHY as a fixed-link if the link type declared in DPC
-+       * explicitly mandates this
++      /* probe the PHY as a fixed-link if there's a phy-handle defined
++       * in the device tree
 +       */
-+
 +      phy_node = of_parse_phandle(dpmac_node, "phy-handle", 0);
 +      if (!phy_node) {
 +              goto probe_fixed_link;
@@ -21492,12 +22158,8 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +              dev_info(dev, "Registered fixed PHY.\n");
 +      }
 +
-+      /* start PHY state machine */
-+#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
 +      dpaa2_mac_open(netdev);
-+#else /* CONFIG_FSL_DPAA2_MAC_NETDEVS */
-+      phy_start(netdev->phydev);
-+#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */
++
 +      return 0;
 +
 +err_defer:
@@ -21521,6 +22183,15 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +{
 +      struct device           *dev = &mc_dev->dev;
 +      struct dpaa2_mac_priv   *priv = dev_get_drvdata(dev);
++      struct net_device       *netdev = priv->netdev;
++
++      dpaa2_mac_stop(netdev);
++
++      if (phy_is_pseudo_fixed_link(netdev->phydev))
++              fixed_phy_unregister(netdev->phydev);
++      else
++              phy_disconnect(netdev->phydev);
++      netdev->phydev = NULL;
 +
 +#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
 +      unregister_netdev(priv->netdev);
@@ -21531,7 +22202,6 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
 +      free_netdev(priv->netdev);
 +
 +      dev_set_drvdata(dev, NULL);
-+      kfree(priv);
 +
 +      return 0;
 +}