layerscape: add patches-5.4
[openwrt/staging/wigyori.git] / target / linux / layerscape / patches-5.4 / 701-net-0157-staging-fsl-dpaa2-mac-Add-Freescale-DPAA2-mac-driver.patch
diff --git a/target/linux/layerscape/patches-5.4/701-net-0157-staging-fsl-dpaa2-mac-Add-Freescale-DPAA2-mac-driver.patch b/target/linux/layerscape/patches-5.4/701-net-0157-staging-fsl-dpaa2-mac-Add-Freescale-DPAA2-mac-driver.patch
new file mode 100644 (file)
index 0000000..750c6ce
--- /dev/null
@@ -0,0 +1,765 @@
+From 50ba3c63dc3f4fbeac32fc5675a00756723d786d Mon Sep 17 00:00:00 2001
+From: Bogdan Purcareata <bogdan.purcareata@nxp.com>
+Date: Wed, 12 Apr 2017 12:42:07 +0000
+Subject: [PATCH] staging: fsl-dpaa2/mac: Add Freescale DPAA2 mac driver
+
+Introduce the DPAA2 mac driver, which manages Datapath
+Media Access Control (DPMAC) objects discovered on the
+MC bus.
+
+This driver works as a proxy between phylib including phy
+drivers and the Management Complex firmware. It receives
+updates on link state changes from PHY lib and forwards
+them to the Management Complex and receives interrupts
+from the Management Complex whenever a request is made to
+change the link state.
+
+This is a squashed commit containing contributions of the
+following owners:
+Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com>
+Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com>
+Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com>
+Signed-off-by: Razvan Stefanescu <razvan.stefanescu@nxp.com>
+Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com>
+Signed-off-by: J. German Rivera <German.Rivera@freescale.com>
+Signed-off-by: Itai Katz <itai.katz@freescale.com>
+
+Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com>
+---
+ drivers/staging/fsl-dpaa2/Kconfig      |   2 +
+ drivers/staging/fsl-dpaa2/Makefile     |   1 +
+ drivers/staging/fsl-dpaa2/mac/Kconfig  |  23 ++
+ drivers/staging/fsl-dpaa2/mac/Makefile |  10 +
+ drivers/staging/fsl-dpaa2/mac/mac.c    | 670 +++++++++++++++++++++++++++++++++
+ 5 files changed, 706 insertions(+)
+ create mode 100644 drivers/staging/fsl-dpaa2/mac/Kconfig
+ create mode 100644 drivers/staging/fsl-dpaa2/mac/Makefile
+ create mode 100644 drivers/staging/fsl-dpaa2/mac/mac.c
+
+--- a/drivers/staging/fsl-dpaa2/Kconfig
++++ b/drivers/staging/fsl-dpaa2/Kconfig
+@@ -17,3 +17,5 @@ config FSL_DPAA2_ETHSW
+       help
+         Driver for Freescale DPAA2 Ethernet Switch. Select
+         BRIDGE to have support for bridge tools.
++
++source "drivers/staging/fsl-dpaa2/mac/Kconfig"
+--- a/drivers/staging/fsl-dpaa2/Makefile
++++ b/drivers/staging/fsl-dpaa2/Makefile
+@@ -4,3 +4,4 @@
+ #
+ obj-$(CONFIG_FSL_DPAA2_ETHSW)         += ethsw/
++obj-$(CONFIG_FSL_DPAA2_MAC)           += mac/
+--- /dev/null
++++ b/drivers/staging/fsl-dpaa2/mac/Kconfig
+@@ -0,0 +1,23 @@
++config FSL_DPAA2_MAC
++      tristate "DPAA2 MAC / PHY interface"
++      depends on FSL_MC_BUS && FSL_DPAA2
++      select MDIO_BUS_MUX_MMIOREG
++      select FSL_XGMAC_MDIO
++      select FIXED_PHY
++      ---help---
++      Prototype driver for DPAA2 MAC / PHY interface object.
++      This driver works as a proxy between phylib including phy drivers and
++      the MC firmware.  It receives updates on link state changes from PHY
++      lib and forwards them to MC and receives interrupt from MC whenever
++      a request is made to change the link state.
++
++
++config FSL_DPAA2_MAC_NETDEVS
++      bool "Expose net interfaces for PHYs"
++      default n
++      depends on FSL_DPAA2_MAC
++      ---help---
++      Exposes macX net interfaces which allow direct control over MACs and
++      PHYs.
++      .
++      Leave disabled if unsure.
+--- /dev/null
++++ b/drivers/staging/fsl-dpaa2/mac/Makefile
+@@ -0,0 +1,10 @@
++
++obj-$(CONFIG_FSL_DPAA2_MAC) += dpaa2-mac.o
++
++dpaa2-mac-objs := mac.o dpmac.o
++
++all:
++      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
++
++clean:
++      make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+--- /dev/null
++++ b/drivers/staging/fsl-dpaa2/mac/mac.c
+@@ -0,0 +1,670 @@
++/* Copyright 2015 Freescale Semiconductor Inc.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *     * Redistributions of source code must retain the above copyright
++ *     notice, this list of conditions and the following disclaimer.
++ *     * Redistributions in binary form must reproduce the above copyright
++ *     notice, this list of conditions and the following disclaimer in the
++ *     documentation and/or other materials provided with the distribution.
++ *     * Neither the name of Freescale Semiconductor nor the
++ *     names of its contributors may be used to endorse or promote products
++ *     derived from this software without specific prior written permission.
++ *
++ *
++ * ALTERNATIVELY, this software may be distributed under the terms of the
++ * GNU General Public License ("GPL") as published by the Free Software
++ * Foundation, either version 2 of that License or (at your option) any
++ * later version.
++ *
++ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
++ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++#include <linux/module.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/msi.h>
++#include <linux/rtnetlink.h>
++#include <linux/if_vlan.h>
++
++#include <uapi/linux/if_bridge.h>
++#include <net/netlink.h>
++
++#include <linux/of.h>
++#include <linux/of_mdio.h>
++#include <linux/of_net.h>
++#include <linux/phy.h>
++#include <linux/phy_fixed.h>
++
++#include "../../fsl-mc/include/mc.h"
++#include "../../fsl-mc/include/mc-sys.h"
++
++#include "dpmac.h"
++#include "dpmac-cmd.h"
++
++struct dpaa2_mac_priv {
++      struct net_device               *netdev;
++      struct fsl_mc_device            *mc_dev;
++      struct dpmac_attr               attr;
++      struct dpmac_link_state         old_state;
++};
++
++/* TODO: fix the 10G modes, mapping can't be right:
++ *  XGMII is paralel
++ *  XAUI is serial, using 8b/10b encoding
++ *  XFI is also serial but using 64b/66b encoding
++ * they can't all map to XGMII...
++ *
++ * This must be kept in sync with enum dpmac_eth_if.
++ */
++static phy_interface_t dpaa2_mac_iface_mode[] =  {
++      PHY_INTERFACE_MODE_MII,         /* DPMAC_ETH_IF_MII */
++      PHY_INTERFACE_MODE_RMII,        /* DPMAC_ETH_IF_RMII */
++      PHY_INTERFACE_MODE_SMII,        /* DPMAC_ETH_IF_SMII */
++      PHY_INTERFACE_MODE_GMII,        /* DPMAC_ETH_IF_GMII */
++      PHY_INTERFACE_MODE_RGMII,       /* DPMAC_ETH_IF_RGMII */
++      PHY_INTERFACE_MODE_SGMII,       /* DPMAC_ETH_IF_SGMII */
++      PHY_INTERFACE_MODE_QSGMII,      /* DPMAC_ETH_IF_QSGMII */
++      PHY_INTERFACE_MODE_XGMII,       /* DPMAC_ETH_IF_XAUI */
++      PHY_INTERFACE_MODE_XGMII,       /* DPMAC_ETH_IF_XFI */
++};
++
++static void dpaa2_mac_link_changed(struct net_device *netdev)
++{
++      struct phy_device       *phydev;
++      struct dpmac_link_state state = { 0 };
++      struct dpaa2_mac_priv   *priv = netdev_priv(netdev);
++      int                     err;
++
++      /* the PHY just notified us of link state change */
++      phydev = netdev->phydev;
++
++      state.up = !!phydev->link;
++      if (phydev->link) {
++              state.rate = phydev->speed;
++
++              if (!phydev->duplex)
++                      state.options |= DPMAC_LINK_OPT_HALF_DUPLEX;
++              if (phydev->autoneg)
++                      state.options |= DPMAC_LINK_OPT_AUTONEG;
++
++              netif_carrier_on(netdev);
++      } else {
++              netif_carrier_off(netdev);
++      }
++
++      if (priv->old_state.up != state.up ||
++          priv->old_state.rate != state.rate ||
++          priv->old_state.options != state.options) {
++              priv->old_state = state;
++              phy_print_status(phydev);
++      }
++
++      /* We must interrogate MC at all times, because we don't know
++       * when and whether a potential DPNI may have read the link state.
++       */
++      err = dpmac_set_link_state(priv->mc_dev->mc_io, 0,
++                                 priv->mc_dev->mc_handle, &state);
++      if (unlikely(err))
++              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 */
++      phy_start(netdev->phydev);
++
++      return 0;
++}
++
++static int dpaa2_mac_stop(struct net_device *netdev)
++{
++      if (!netdev->phydev)
++              goto done;
++
++      /* stop PHY state machine */
++      phy_stop(netdev->phydev);
++
++      /* signal link down to firmware */
++      netdev->phydev->link = 0;
++      dpaa2_mac_link_changed(netdev);
++
++done:
++      return 0;
++}
++
++static int dpaa2_mac_get_settings(struct net_device *netdev,
++                                struct ethtool_cmd *cmd)
++{
++      return phy_ethtool_gset(netdev->phydev, cmd);
++}
++
++static int dpaa2_mac_set_settings(struct net_device *netdev,
++                                struct ethtool_cmd *cmd)
++{
++      return phy_ethtool_sset(netdev->phydev, cmd);
++}
++
++static void dpaa2_mac_get_stats(struct net_device *netdev,
++                              struct rtnl_link_stats64 *storage)
++{
++      struct dpaa2_mac_priv   *priv = netdev_priv(netdev);
++      u64                     tmp;
++      int                     err;
++
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_EGR_MCAST_FRAME,
++                              &storage->tx_packets);
++      if (err)
++              goto error;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_EGR_BCAST_FRAME, &tmp);
++      if (err)
++              goto error;
++      storage->tx_packets += tmp;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_EGR_UCAST_FRAME, &tmp);
++      if (err)
++              goto error;
++      storage->tx_packets += tmp;
++
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_EGR_UNDERSIZED, &storage->tx_dropped);
++      if (err)
++              goto error;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_EGR_BYTE, &storage->tx_bytes);
++      if (err)
++              goto error;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_EGR_ERR_FRAME, &storage->tx_errors);
++      if (err)
++              goto error;
++
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_ING_ALL_FRAME, &storage->rx_packets);
++      if (err)
++              goto error;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_ING_MCAST_FRAME, &storage->multicast);
++      if (err)
++              goto error;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_ING_FRAME_DISCARD,
++                              &storage->rx_dropped);
++      if (err)
++              goto error;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_ING_ALIGN_ERR, &storage->rx_errors);
++      if (err)
++              goto error;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_ING_OVERSIZED, &tmp);
++      if (err)
++              goto error;
++      storage->rx_errors += tmp;
++      err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle,
++                              DPMAC_CNT_ING_BYTE, &storage->rx_bytes);
++      if (err)
++              goto error;
++
++      return;
++error:
++      netdev_err(netdev, "dpmac_get_counter err %d\n", err);
++}
++
++static struct {
++      enum dpmac_counter id;
++      char name[ETH_GSTRING_LEN];
++} dpaa2_mac_counters[] =  {
++      {DPMAC_CNT_ING_ALL_FRAME,               "rx all frames"},
++      {DPMAC_CNT_ING_GOOD_FRAME,              "rx frames ok"},
++      {DPMAC_CNT_ING_ERR_FRAME,               "rx frame errors"},
++      {DPMAC_CNT_ING_FRAME_DISCARD,           "rx frame discards"},
++      {DPMAC_CNT_ING_UCAST_FRAME,             "rx u-cast"},
++      {DPMAC_CNT_ING_BCAST_FRAME,             "rx b-cast"},
++      {DPMAC_CNT_ING_MCAST_FRAME,             "rx m-cast"},
++      {DPMAC_CNT_ING_FRAME_64,                "rx 64 bytes"},
++      {DPMAC_CNT_ING_FRAME_127,               "rx 65-127 bytes"},
++      {DPMAC_CNT_ING_FRAME_255,               "rx 128-255 bytes"},
++      {DPMAC_CNT_ING_FRAME_511,               "rx 256-511 bytes"},
++      {DPMAC_CNT_ING_FRAME_1023,              "rx 512-1023 bytes"},
++      {DPMAC_CNT_ING_FRAME_1518,              "rx 1024-1518 bytes"},
++      {DPMAC_CNT_ING_FRAME_1519_MAX,          "rx 1519-max bytes"},
++      {DPMAC_CNT_ING_FRAG,                    "rx frags"},
++      {DPMAC_CNT_ING_JABBER,                  "rx jabber"},
++      {DPMAC_CNT_ING_ALIGN_ERR,               "rx align errors"},
++      {DPMAC_CNT_ING_OVERSIZED,               "rx oversized"},
++      {DPMAC_CNT_ING_VALID_PAUSE_FRAME,       "rx pause"},
++      {DPMAC_CNT_ING_BYTE,                    "rx bytes"},
++      {DPMAC_CNT_ENG_GOOD_FRAME,              "tx frames ok"},
++      {DPMAC_CNT_EGR_UCAST_FRAME,             "tx u-cast"},
++      {DPMAC_CNT_EGR_MCAST_FRAME,             "tx m-cast"},
++      {DPMAC_CNT_EGR_BCAST_FRAME,             "tx b-cast"},
++      {DPMAC_CNT_EGR_ERR_FRAME,               "tx frame errors"},
++      {DPMAC_CNT_EGR_UNDERSIZED,              "tx undersized"},
++      {DPMAC_CNT_EGR_VALID_PAUSE_FRAME,       "tx b-pause"},
++      {DPMAC_CNT_EGR_BYTE,                    "tx bytes"},
++
++};
++
++static void dpaa2_mac_get_strings(struct net_device *netdev,
++                                u32 stringset, u8 *data)
++{
++      int i;
++
++      switch (stringset) {
++      case ETH_SS_STATS:
++              for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++)
++                      memcpy(data + i * ETH_GSTRING_LEN,
++                             dpaa2_mac_counters[i].name,
++                             ETH_GSTRING_LEN);
++              break;
++      }
++}
++
++static void dpaa2_mac_get_ethtool_stats(struct net_device *netdev,
++                                      struct ethtool_stats *stats,
++                                      u64 *data)
++{
++      struct dpaa2_mac_priv   *priv = netdev_priv(netdev);
++      int                     i;
++      int                     err;
++
++      for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++) {
++              err = dpmac_get_counter(priv->mc_dev->mc_io,
++                                      0,
++                                      priv->mc_dev->mc_handle,
++                                      dpaa2_mac_counters[i].id, &data[i]);
++              if (err)
++                      netdev_err(netdev, "dpmac_get_counter[%s] err %d\n",
++                                 dpaa2_mac_counters[i].name, err);
++      }
++}
++
++static int dpaa2_mac_get_sset_count(struct net_device *dev, int sset)
++{
++      switch (sset) {
++      case ETH_SS_STATS:
++              return ARRAY_SIZE(dpaa2_mac_counters);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++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_get_stats64        = &dpaa2_mac_get_stats,
++};
++
++static const struct ethtool_ops dpaa2_mac_ethtool_ops = {
++      .get_settings           = &dpaa2_mac_get_settings,
++      .set_settings           = &dpaa2_mac_set_settings,
++      .get_strings            = &dpaa2_mac_get_strings,
++      .get_ethtool_stats      = &dpaa2_mac_get_ethtool_stats,
++      .get_sset_count         = &dpaa2_mac_get_sset_count,
++};
++#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */
++
++static void configure_link(struct dpaa2_mac_priv *priv,
++                         struct dpmac_link_cfg *cfg)
++{
++      struct phy_device *phydev = priv->netdev->phydev;
++
++      if (unlikely(!phydev))
++              return;
++
++      phydev->speed = cfg->rate;
++      phydev->duplex  = !!(cfg->options & DPMAC_LINK_OPT_HALF_DUPLEX);
++
++      if (cfg->options & DPMAC_LINK_OPT_AUTONEG) {
++              phydev->autoneg = 1;
++              phydev->advertising |= ADVERTISED_Autoneg;
++      } else {
++              phydev->autoneg = 0;
++              phydev->advertising &= ~ADVERTISED_Autoneg;
++      }
++
++      phy_start_aneg(phydev);
++}
++
++static irqreturn_t dpaa2_mac_irq_handler(int irq_num, void *arg)
++{
++      struct device *dev = (struct device *)arg;
++      struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
++      struct dpaa2_mac_priv *priv = dev_get_drvdata(dev);
++      struct dpmac_link_cfg link_cfg;
++      u32 status;
++      int err;
++
++      err = dpmac_get_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle,
++                                 DPMAC_IRQ_INDEX, &status);
++      if (unlikely(err || !status))
++              return IRQ_NONE;
++
++      /* DPNI-initiated link configuration; 'ifconfig up' also calls this */
++      if (status & DPMAC_IRQ_EVENT_LINK_CFG_REQ) {
++              err = dpmac_get_link_cfg(mc_dev->mc_io, 0, mc_dev->mc_handle,
++                                       &link_cfg);
++              if (unlikely(err))
++                      goto out;
++
++              configure_link(priv, &link_cfg);
++      }
++
++out:
++      dpmac_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle,
++                             DPMAC_IRQ_INDEX, status);
++
++      return IRQ_HANDLED;
++}
++
++static int setup_irqs(struct fsl_mc_device *mc_dev)
++{
++      int err = 0;
++      struct fsl_mc_device_irq *irq;
++
++      err = fsl_mc_allocate_irqs(mc_dev);
++      if (err) {
++              dev_err(&mc_dev->dev, "fsl_mc_allocate_irqs err %d\n", err);
++              return err;
++      }
++
++      irq = mc_dev->irqs[0];
++      err = devm_request_threaded_irq(&mc_dev->dev, irq->msi_desc->irq,
++                                      NULL, &dpaa2_mac_irq_handler,
++                                      IRQF_NO_SUSPEND | IRQF_ONESHOT,
++                                      dev_name(&mc_dev->dev), &mc_dev->dev);
++      if (err) {
++              dev_err(&mc_dev->dev, "devm_request_threaded_irq err %d\n",
++                      err);
++              goto free_irq;
++      }
++
++      err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle,
++                               DPMAC_IRQ_INDEX, DPMAC_IRQ_EVENT_LINK_CFG_REQ);
++      if (err) {
++              dev_err(&mc_dev->dev, "dpmac_set_irq_mask err %d\n", err);
++              goto free_irq;
++      }
++      err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle,
++                                 DPMAC_IRQ_INDEX, 1);
++      if (err) {
++              dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err);
++              goto free_irq;
++      }
++
++      return 0;
++
++free_irq:
++      fsl_mc_free_irqs(mc_dev);
++
++      return err;
++}
++
++static void teardown_irqs(struct fsl_mc_device *mc_dev)
++{
++      int err;
++
++      err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle,
++                                 DPMAC_IRQ_INDEX, 0);
++      if (err)
++              dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err);
++
++      fsl_mc_free_irqs(mc_dev);
++}
++
++static struct device_node *find_dpmac_node(struct device *dev, u16 dpmac_id)
++{
++      struct device_node *dpmacs, *dpmac = NULL;
++      struct device_node *mc_node = dev->of_node;
++      u32 id;
++      int err;
++
++      dpmacs = of_find_node_by_name(mc_node, "dpmacs");
++      if (!dpmacs) {
++              dev_err(dev, "No dpmacs subnode in device-tree\n");
++              return NULL;
++      }
++
++      while ((dpmac = of_get_next_child(dpmacs, dpmac))) {
++              err = of_property_read_u32(dpmac, "reg", &id);
++              if (err)
++                      continue;
++              if (id == dpmac_id)
++                      return dpmac;
++      }
++
++      return NULL;
++}
++
++static int dpaa2_mac_probe(struct fsl_mc_device *mc_dev)
++{
++      struct device           *dev;
++      struct dpaa2_mac_priv   *priv = NULL;
++      struct device_node      *phy_node, *dpmac_node;
++      struct net_device       *netdev;
++      phy_interface_t         if_mode;
++      int                     err = 0;
++
++      dev = &mc_dev->dev;
++
++      /* prepare a net_dev structure to make the phy lib API happy */
++      netdev = alloc_etherdev(sizeof(*priv));
++      if (!netdev) {
++              dev_err(dev, "alloc_etherdev error\n");
++              err = -ENOMEM;
++              goto err_exit;
++      }
++      priv = netdev_priv(netdev);
++      priv->mc_dev = mc_dev;
++      priv->netdev = netdev;
++
++      SET_NETDEV_DEV(netdev, dev);
++
++#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
++      snprintf(netdev->name, IFNAMSIZ, "mac%d", mc_dev->obj_desc.id);
++#endif
++
++      dev_set_drvdata(dev, priv);
++
++      err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io);
++      if (err || !mc_dev->mc_io) {
++              dev_err(dev, "fsl_mc_portal_allocate error: %d\n", err);
++              err = -ENODEV;
++              goto err_free_netdev;
++      }
++
++      err = dpmac_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
++                       &mc_dev->mc_handle);
++      if (err || !mc_dev->mc_handle) {
++              dev_err(dev, "dpmac_open error: %d\n", err);
++              err = -ENODEV;
++              goto err_free_mcp;
++      }
++
++      err = dpmac_get_attributes(mc_dev->mc_io, 0,
++                                 mc_dev->mc_handle, &priv->attr);
++      if (err) {
++              dev_err(dev, "dpmac_get_attributes err %d\n", err);
++              err = -EINVAL;
++              goto err_close;
++      }
++
++      /* Look up the DPMAC node in the device-tree. */
++      dpmac_node = find_dpmac_node(dev, priv->attr.id);
++      if (!dpmac_node) {
++              dev_err(dev, "No dpmac@%d subnode found.\n", priv->attr.id);
++              err = -ENODEV;
++              goto err_close;
++      }
++
++      err = setup_irqs(mc_dev);
++      if (err) {
++              err = -EFAULT;
++              goto err_close;
++      }
++
++#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
++      /* OPTIONAL, register netdev just to make it visible to the user */
++      netdev->netdev_ops = &dpaa2_mac_ndo_ops;
++      netdev->ethtool_ops = &dpaa2_mac_ethtool_ops;
++
++      /* phy starts up enabled so netdev should be up too */
++      netdev->flags |= IFF_UP;
++
++      err = register_netdev(priv->netdev);
++      if (err < 0) {
++              dev_err(dev, "register_netdev error %d\n", err);
++              err = -ENODEV;
++              goto err_free_irq;
++      }
++#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */
++
++      /* probe the PHY as a fixed-link if the link type declared in DPC
++       * explicitly mandates this
++       */
++      if (priv->attr.link_type == DPMAC_LINK_TYPE_FIXED)
++              goto probe_fixed_link;
++
++      if (priv->attr.eth_if < ARRAY_SIZE(dpaa2_mac_iface_mode)) {
++              if_mode = dpaa2_mac_iface_mode[priv->attr.eth_if];
++              dev_dbg(dev, "\tusing if mode %s for eth_if %d\n",
++                      phy_modes(if_mode), priv->attr.eth_if);
++      } else {
++              dev_warn(dev, "Unexpected interface mode %d, will probe as fixed link\n",
++                       priv->attr.eth_if);
++              goto probe_fixed_link;
++      }
++
++      /* try to connect to the PHY */
++      phy_node = of_parse_phandle(dpmac_node, "phy-handle", 0);
++      if (!phy_node) {
++              if (!phy_node) {
++                      dev_err(dev, "dpmac node has no phy-handle property\n");
++                      err = -ENODEV;
++                      goto err_no_phy;
++              }
++      }
++      netdev->phydev = of_phy_connect(netdev, phy_node,
++                                      &dpaa2_mac_link_changed, 0, if_mode);
++      if (!netdev->phydev) {
++              /* No need for dev_err(); the kernel's loud enough as it is. */
++              dev_dbg(dev, "Can't of_phy_connect() now.\n");
++              /* We might be waiting for the MDIO MUX to probe, so defer
++               * our own probing.
++               */
++              err = -EPROBE_DEFER;
++              goto err_defer;
++      }
++      dev_info(dev, "Connected to %s PHY.\n", phy_modes(if_mode));
++
++probe_fixed_link:
++      if (!netdev->phydev) {
++              struct fixed_phy_status status = {
++                      .link = 1,
++                      /* fixed-phys don't support 10Gbps speed for now */
++                      .speed = 1000,
++                      .duplex = 1,
++              };
++
++              /* try to register a fixed link phy */
++              netdev->phydev = fixed_phy_register(PHY_POLL, &status, NULL);
++              if (!netdev->phydev || IS_ERR(netdev->phydev)) {
++                      dev_err(dev, "error trying to register fixed PHY\n");
++                      /* So we don't crash unregister_netdev() later on */
++                      netdev->phydev = NULL;
++                      err = -EFAULT;
++                      goto err_no_phy;
++              }
++              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:
++err_no_phy:
++#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
++      unregister_netdev(netdev);
++err_free_irq:
++#endif
++      teardown_irqs(mc_dev);
++err_close:
++      dpmac_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
++err_free_mcp:
++      fsl_mc_portal_free(mc_dev->mc_io);
++err_free_netdev:
++      free_netdev(netdev);
++err_exit:
++      return err;
++}
++
++static int dpaa2_mac_remove(struct fsl_mc_device *mc_dev)
++{
++      struct device           *dev = &mc_dev->dev;
++      struct dpaa2_mac_priv   *priv = dev_get_drvdata(dev);
++
++#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS
++      unregister_netdev(priv->netdev);
++#endif
++      teardown_irqs(priv->mc_dev);
++      dpmac_close(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle);
++      fsl_mc_portal_free(priv->mc_dev->mc_io);
++      free_netdev(priv->netdev);
++
++      dev_set_drvdata(dev, NULL);
++      kfree(priv);
++
++      return 0;
++}
++
++static const struct fsl_mc_device_id dpaa2_mac_match_id_table[] = {
++      {
++              .vendor = FSL_MC_VENDOR_FREESCALE,
++              .obj_type = "dpmac",
++      },
++      { .vendor = 0x0 }
++};
++MODULE_DEVICE_TABLE(fslmc, dpaa2_mac_match_id_table);
++
++static struct fsl_mc_driver dpaa2_mac_drv = {
++      .driver = {
++              .name           = KBUILD_MODNAME,
++              .owner          = THIS_MODULE,
++      },
++      .probe          = dpaa2_mac_probe,
++      .remove         = dpaa2_mac_remove,
++      .match_id_table = dpaa2_mac_match_id_table,
++};
++
++module_fsl_mc_driver(dpaa2_mac_drv);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("DPAA2 PHY proxy interface driver");