summaryrefslogtreecommitdiff
path: root/drivers/staging/fsl-dpaa2/mac/mac.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/fsl-dpaa2/mac/mac.c')
-rw-r--r--drivers/staging/fsl-dpaa2/mac/mac.c666
1 files changed, 666 insertions, 0 deletions
diff --git a/drivers/staging/fsl-dpaa2/mac/mac.c b/drivers/staging/fsl-dpaa2/mac/mac.c
new file mode 100644
index 0000000..3016963
--- /dev/null
+++ b/drivers/staging/fsl-dpaa2/mac/mac.c
@@ -0,0 +1,666 @@
+/* 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
+ */
+
+ phy_node = of_parse_phandle(dpmac_node, "phy-handle", 0);
+ if (!phy_node) {
+ 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 */
+ 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, -1,
+ 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");