lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1560470153-26155-5-git-send-email-ioana.ciornei@nxp.com>
Date:   Fri, 14 Jun 2019 02:55:51 +0300
From:   Ioana Ciornei <ioana.ciornei@....com>
To:     linux@...linux.org.uk, hkallweit1@...il.com, f.fainelli@...il.com,
        andrew@...n.ch, davem@...emloft.net
Cc:     netdev@...r.kernel.org, alexandru.marginean@....com,
        ruxandra.radulescu@....com, Ioana Ciornei <ioana.ciornei@....com>
Subject: [PATCH RFC 4/6] dpaa2-mac: add initial driver

The dpaa2-mac driver binds to DPAA2 DPMAC objects, dynamically
discovered on the fsl-mc bus. It acts as a proxy between the PHY
management layer and the MC firmware, delivering any configuration
changes to the firmware and also setting any new configuration requested
though PHYLINK.

A in-depth view of the software architecture and the implementation can
be found in
'Documentation/networking/device_drivers/freescale/dpaa2/dpmac-driver.rst'.

Signed-off-by: Ioana Ciornei <ioana.ciornei@....com>
---
 MAINTAINERS                                      |   7 +
 drivers/net/ethernet/freescale/dpaa2/Kconfig     |  13 +
 drivers/net/ethernet/freescale/dpaa2/Makefile    |   2 +
 drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 541 +++++++++++++++++++++++
 4 files changed, 563 insertions(+)
 create mode 100644 drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c

diff --git a/MAINTAINERS b/MAINTAINERS
index dd247a059889..a024ab2b2548 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4929,6 +4929,13 @@ S:	Maintained
 F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-ptp*
 F:	drivers/net/ethernet/freescale/dpaa2/dprtc*
 
+DPAA2 MAC DRIVER
+M:	Ioana Ciornei <ioana.ciornei@....com>
+L:	netdev@...r.kernel.org
+S:	Maintained
+F:	drivers/net/ethernet/freescale/dpaa2/dpaa2-mac*
+F:	drivers/net/ethernet/freescale/dpaa2/dpmac*
+
 DPT_I2O SCSI RAID DRIVER
 M:	Adaptec OEM Raid Solutions <aacraid@...rosemi.com>
 L:	linux-scsi@...r.kernel.org
diff --git a/drivers/net/ethernet/freescale/dpaa2/Kconfig b/drivers/net/ethernet/freescale/dpaa2/Kconfig
index 8bd384720f80..4ffa666c0a43 100644
--- a/drivers/net/ethernet/freescale/dpaa2/Kconfig
+++ b/drivers/net/ethernet/freescale/dpaa2/Kconfig
@@ -16,3 +16,16 @@ config FSL_DPAA2_PTP_CLOCK
 	help
 	  This driver adds support for using the DPAA2 1588 timer module
 	  as a PTP clock.
+
+config FSL_DPAA2_MAC
+	tristate "DPAA2 MAC / PHY proxy interface"
+	depends on FSL_MC_BUS
+	select MDIO_BUS_MUX_MMIOREG
+	select FSL_XGMAC_MDIO
+	select PHYLINK
+	help
+	  Prototype driver for DPAA2 MAC / PHY interface object.
+	  This driver works as a proxy between PHYLINK including phy drivers and
+	  the MC firmware.  It receives updates on link state changes from PHYLINK
+	  and forwards them to MC and receives interrupt from MC whenever a
+	  request is made to change the link state or configuration.
diff --git a/drivers/net/ethernet/freescale/dpaa2/Makefile b/drivers/net/ethernet/freescale/dpaa2/Makefile
index d1e78cdd512f..e96386ab23ea 100644
--- a/drivers/net/ethernet/freescale/dpaa2/Makefile
+++ b/drivers/net/ethernet/freescale/dpaa2/Makefile
@@ -5,10 +5,12 @@
 
 obj-$(CONFIG_FSL_DPAA2_ETH)		+= fsl-dpaa2-eth.o
 obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK)	+= fsl-dpaa2-ptp.o
+obj-$(CONFIG_FSL_DPAA2_MAC)		+= fsl-dpaa2-mac.o
 
 fsl-dpaa2-eth-objs	:= dpaa2-eth.o dpaa2-ethtool.o dpni.o
 fsl-dpaa2-eth-${CONFIG_DEBUG_FS} += dpaa2-eth-debugfs.o
 fsl-dpaa2-ptp-objs	:= dpaa2-ptp.o dprtc.o
+fsl-dpaa2-mac-objs	:= dpaa2-mac.o dpmac.o
 
 # Needed by the tracing framework
 CFLAGS_dpaa2-eth.o := -I$(src)
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
new file mode 100644
index 000000000000..145ab4771788
--- /dev/null
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* Copyright 2015 Freescale Semiconductor Inc.
+ * Copyright 2018-2019 NXP
+ */
+#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 <net/netlink.h>
+#include <uapi/linux/if_bridge.h>
+
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/phylink.h>
+#include <linux/notifier.h>
+
+#include <linux/fsl/mc.h>
+
+#include "dpmac.h"
+#include "dpmac-cmd.h"
+
+#define to_dpaa2_mac_priv(phylink_config) \
+	container_of(config, struct dpaa2_mac_priv, phylink_config)
+
+struct dpaa2_mac_priv {
+	struct fsl_mc_device *mc_dev;
+	struct dpmac_attr attr;
+	struct dpmac_link_state state;
+	u16 dpmac_ver_major;
+	u16 dpmac_ver_minor;
+
+	struct phylink *phylink;
+	struct phylink_config phylink_config;
+	struct ethtool_link_ksettings kset;
+};
+
+static phy_interface_t phy_mode(enum dpmac_eth_if eth_if)
+{
+	switch (eth_if) {
+	case DPMAC_ETH_IF_RGMII:
+		return PHY_INTERFACE_MODE_RGMII;
+	case DPMAC_ETH_IF_XFI:
+		return PHY_INTERFACE_MODE_10GKR;
+	case DPMAC_ETH_IF_USXGMII:
+		return PHY_INTERFACE_MODE_USXGMII;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int cmp_dpmac_ver(struct dpaa2_mac_priv *priv,
+			 u16 ver_major, u16 ver_minor)
+{
+	if (priv->dpmac_ver_major == ver_major)
+		return priv->dpmac_ver_minor - ver_minor;
+	return priv->dpmac_ver_major - ver_major;
+}
+
+struct dpaa2_mac_link_mode_map {
+	u64 dpmac_lm;
+	enum ethtool_link_mode_bit_indices ethtool_lm;
+};
+
+static const struct dpaa2_mac_link_mode_map dpaa2_mac_lm_map[] = {
+	{DPMAC_ADVERTISED_10BASET_FULL, ETHTOOL_LINK_MODE_10baseT_Full_BIT},
+	{DPMAC_ADVERTISED_100BASET_FULL, ETHTOOL_LINK_MODE_100baseT_Full_BIT},
+	{DPMAC_ADVERTISED_1000BASET_FULL, ETHTOOL_LINK_MODE_1000baseT_Full_BIT},
+	{DPMAC_ADVERTISED_10000BASET_FULL, ETHTOOL_LINK_MODE_10000baseT_Full_BIT},
+	{DPMAC_ADVERTISED_AUTONEG, ETHTOOL_LINK_MODE_Autoneg_BIT},
+};
+
+static void link_mode_phydev2dpmac(unsigned long *phydev_lm,
+				   u64 *dpmac_lm)
+{
+	enum ethtool_link_mode_bit_indices link_mode;
+	int i;
+
+	*dpmac_lm = 0;
+	for (i = 0; i < ARRAY_SIZE(dpaa2_mac_lm_map); i++) {
+		link_mode = dpaa2_mac_lm_map[i].ethtool_lm;
+		if (linkmode_test_bit(link_mode, phydev_lm))
+			*dpmac_lm |= dpaa2_mac_lm_map[i].dpmac_lm;
+	}
+}
+
+static void dpaa2_mac_ksettings_change(struct dpaa2_mac_priv *priv)
+{
+	struct fsl_mc_device *mc_dev = priv->mc_dev;
+	struct dpmac_link_cfg link_cfg = { 0 };
+	int err, i;
+
+	err = dpmac_get_link_cfg(mc_dev->mc_io, 0,
+				 mc_dev->mc_handle,
+				 &link_cfg);
+
+	if (err) {
+		dev_err(&mc_dev->dev, "dpmac_get_link_cfg() = %d\n", err);
+		return;
+	}
+
+	phylink_ethtool_ksettings_get(priv->phylink, &priv->kset);
+
+	priv->kset.base.speed = link_cfg.rate;
+	priv->kset.base.duplex = !!(link_cfg.options & DPMAC_LINK_OPT_HALF_DUPLEX);
+
+	ethtool_link_ksettings_zero_link_mode(&priv->kset, advertising);
+	for (i = 0; i < ARRAY_SIZE(dpaa2_mac_lm_map); i++) {
+		if (link_cfg.advertising & dpaa2_mac_lm_map[i].dpmac_lm)
+			__set_bit(dpaa2_mac_lm_map[i].ethtool_lm,
+				  priv->kset.link_modes.advertising);
+	}
+
+	if (link_cfg.options & DPMAC_LINK_OPT_AUTONEG) {
+		priv->kset.base.autoneg = AUTONEG_ENABLE;
+		__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+			  priv->kset.link_modes.advertising);
+	} else {
+		priv->kset.base.autoneg = AUTONEG_DISABLE;
+		__clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+			    priv->kset.link_modes.advertising);
+	}
+
+	phylink_ethtool_ksettings_set(priv->phylink, &priv->kset);
+}
+
+static irqreturn_t dpaa2_mac_irq_handler(int irq_num, void *arg)
+{
+	struct device *dev = arg;
+	struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+	struct dpaa2_mac_priv *priv = dev_get_drvdata(dev);
+	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;
+
+	rtnl_lock();
+	if (status & DPMAC_IRQ_EVENT_LINK_CFG_REQ)
+		dpaa2_mac_ksettings_change(priv);
+
+	if (status & DPMAC_IRQ_EVENT_LINK_UP_REQ)
+		phylink_start(priv->phylink);
+
+	if (status & DPMAC_IRQ_EVENT_LINK_DOWN_REQ)
+		phylink_stop(priv->phylink);
+	rtnl_unlock();
+
+	dpmac_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle,
+			       DPMAC_IRQ_INDEX, status);
+
+	return IRQ_HANDLED;
+}
+
+static int dpaa2_mac_setup_irqs(struct fsl_mc_device *mc_dev)
+{
+	struct device *dev = &mc_dev->dev;
+	struct fsl_mc_device_irq *irq;
+	u32 irq_mask;
+	int err;
+
+	err = fsl_mc_allocate_irqs(mc_dev);
+	if (err) {
+		dev_err(dev, "fsl_mc_allocate_irqs() = %d\n", err);
+		return err;
+	}
+
+	irq = mc_dev->irqs[0];
+	err = devm_request_threaded_irq(dev, irq->msi_desc->irq,
+					NULL, &dpaa2_mac_irq_handler,
+					IRQF_NO_SUSPEND | IRQF_ONESHOT,
+					dev_name(&mc_dev->dev), dev);
+	if (err) {
+		dev_err(dev, "devm_request_threaded_irq() = %d\n", err);
+		goto free_irq;
+	}
+
+	irq_mask = DPMAC_IRQ_EVENT_LINK_CFG_REQ |
+		   DPMAC_IRQ_EVENT_LINK_CHANGED |
+		   DPMAC_IRQ_EVENT_LINK_UP_REQ |
+		   DPMAC_IRQ_EVENT_LINK_DOWN_REQ;
+
+	err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle,
+				 DPMAC_IRQ_INDEX, irq_mask);
+	if (err) {
+		dev_err(dev, "dpmac_set_irq_mask() = %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(dev, "dpmac_set_irq_enable() = %d\n", err);
+		goto free_irq;
+	}
+
+	return 0;
+
+free_irq:
+	fsl_mc_free_irqs(mc_dev);
+
+	return err;
+}
+
+static void dpaa2_mac_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 *of_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 void dpaa2_mac_validate(struct phylink_config *config,
+			       unsigned long *supported,
+			       struct phylink_link_state *state)
+{
+	struct dpaa2_mac_priv *priv = to_dpaa2_mac_priv(phylink_config);
+	struct dpmac_link_state *dpmac_state = &priv->state;
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+	phylink_set(mask, Autoneg);
+	phylink_set_port_modes(mask);
+
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_10GKR:
+		phylink_set(mask, 10baseT_Full);
+		phylink_set(mask, 100baseT_Full);
+		phylink_set(mask, 1000baseT_Full);
+		phylink_set(mask, 10000baseT_Full);
+		break;
+	case PHY_INTERFACE_MODE_QSGMII:
+	case PHY_INTERFACE_MODE_RGMII:
+	case PHY_INTERFACE_MODE_RGMII_ID:
+	case PHY_INTERFACE_MODE_RGMII_RXID:
+	case PHY_INTERFACE_MODE_RGMII_TXID:
+		phylink_set(mask, 10baseT_Full);
+		phylink_set(mask, 100baseT_Full);
+		phylink_set(mask, 1000baseT_Full);
+		break;
+	case PHY_INTERFACE_MODE_USXGMII:
+		phylink_set(mask, 10baseT_Full);
+		phylink_set(mask, 100baseT_Full);
+		phylink_set(mask, 1000baseT_Full);
+		phylink_set(mask, 10000baseT_Full);
+		break;
+	default:
+		goto empty_set;
+	}
+
+	bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+	link_mode_phydev2dpmac(supported, &dpmac_state->supported);
+	link_mode_phydev2dpmac(state->advertising, &dpmac_state->advertising);
+
+	return;
+
+empty_set:
+	bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode,
+			     const struct phylink_link_state *state)
+{
+	struct dpaa2_mac_priv *priv = to_dpaa2_mac_priv(phylink_config);
+	struct dpmac_link_state *dpmac_state = &priv->state;
+	struct device *dev = &priv->mc_dev->dev;
+	int err;
+
+	if (state->speed == SPEED_UNKNOWN && state->duplex == DUPLEX_UNKNOWN)
+		return;
+
+	dpmac_state->up = !!state->link;
+	if (dpmac_state->up) {
+		dpmac_state->rate = state->speed;
+
+		if (!state->duplex)
+			dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
+		else
+			dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
+
+		if (state->an_enabled)
+			dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG;
+		else
+			dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG;
+	}
+
+	err = dpmac_set_link_state(priv->mc_dev->mc_io, 0,
+				   priv->mc_dev->mc_handle, dpmac_state);
+	if (err)
+		dev_err(dev, "dpmac_set_link_state() = %d\n", err);
+}
+
+static void dpaa2_mac_link_up(struct phylink_config *config, unsigned int mode,
+			      phy_interface_t interface, struct phy_device *phy)
+{
+	struct dpaa2_mac_priv *priv = to_dpaa2_mac_priv(phylink_config);
+	struct dpmac_link_state *dpmac_state = &priv->state;
+	struct device *dev = &priv->mc_dev->dev;
+	int err;
+
+	dpmac_state->up = 1;
+	err = dpmac_set_link_state(priv->mc_dev->mc_io, 0,
+				   priv->mc_dev->mc_handle, dpmac_state);
+	if (err)
+		dev_err(dev, "dpmac_set_link_state() = %d\n", err);
+}
+
+static void dpaa2_mac_link_down(struct phylink_config *config,
+				unsigned int mode,
+				phy_interface_t interface)
+{
+	struct dpaa2_mac_priv *priv = to_dpaa2_mac_priv(phylink_config);
+	struct dpmac_link_state *dpmac_state = &priv->state;
+	struct device *dev = &priv->mc_dev->dev;
+	int err;
+
+	dpmac_state->up = 0;
+
+	err = dpmac_set_link_state(priv->mc_dev->mc_io, 0,
+				   priv->mc_dev->mc_handle, dpmac_state);
+	if (err)
+		dev_err(dev, "dpmac_set_link_state() = %d\n", err);
+}
+
+static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
+	.validate = dpaa2_mac_validate,
+	.mac_config = dpaa2_mac_config,
+	.mac_link_up = dpaa2_mac_link_up,
+	.mac_link_down = dpaa2_mac_link_down,
+};
+
+static int dpaa2_mac_probe(struct fsl_mc_device *mc_dev)
+{
+	struct dpaa2_mac_priv *priv = NULL;
+	struct device_node *dpmac_node;
+	struct phylink *phylink;
+	int if_mode, err = 0;
+	struct device *dev;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev = &mc_dev->dev;
+	priv->mc_dev = mc_dev;
+	dev_set_drvdata(dev, priv);
+
+	/* We may need to issue MC commands while in atomic context */
+	err = fsl_mc_portal_allocate(mc_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
+				     &mc_dev->mc_io);
+	if (err || !mc_dev->mc_io) {
+		dev_dbg(dev, "fsl_mc_portal_allocate error: %d\n", err);
+		err = -EPROBE_DEFER;
+		goto err_exit;
+	}
+
+	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_api_version(mc_dev->mc_io, 0, &priv->dpmac_ver_major,
+				    &priv->dpmac_ver_minor);
+	if (err) {
+		dev_err(dev, "dpmac_get_api_version failed\n");
+		goto err_version;
+	}
+
+	if (cmp_dpmac_ver(priv, DPMAC_VER_MAJOR, DPMAC_VER_MINOR) < 0) {
+		dev_err(dev, "DPMAC version %u.%u lower than supported %u.%u\n",
+			priv->dpmac_ver_major, priv->dpmac_ver_minor,
+			DPMAC_VER_MAJOR, DPMAC_VER_MINOR);
+		err = -ENOTSUPP;
+		goto err_version;
+	}
+
+	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;
+	}
+
+	if (priv->attr.link_type == DPMAC_LINK_TYPE_FIXED) {
+		dev_err(dev, "will not be probed because it's listed as TYPE_FIXED\n");
+		err = -EINVAL;
+		goto err_close;
+	}
+
+	/* Look up the DPMAC node in the device-tree. */
+	dpmac_node = of_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 = dpaa2_mac_setup_irqs(mc_dev);
+	if (err) {
+		err = -EFAULT;
+		goto err_close;
+	}
+
+	/* Get the interface mode from the dpmac of node or
+	 * from the MC attributes
+	 */
+	if_mode = of_get_phy_mode(dpmac_node);
+	if (if_mode >= 0) {
+		dev_dbg(dev, "\tusing if mode %s for eth_if %d\n",
+			phy_modes(if_mode), priv->attr.eth_if);
+		goto operation_mode;
+	}
+
+	if_mode = phy_mode(priv->attr.eth_if);
+	if (if_mode >= 0) {
+		dev_dbg(dev, "\tusing if mode %s for eth_if %d\n",
+			phy_modes(if_mode), priv->attr.eth_if);
+	} else {
+		dev_err(dev, "Unexpected interface mode %d\n",
+			priv->attr.eth_if);
+		err = -EINVAL;
+		goto err_no_if_mode;
+	}
+
+operation_mode:
+	priv->phylink_config.dev = dev;
+	priv->phylink_config.type = PHYLINK_DEV;
+
+	phylink = phylink_create(&priv->phylink_config,
+				 of_fwnode_handle(dpmac_node), if_mode,
+				 &dpaa2_mac_phylink_ops);
+	if (IS_ERR(phylink)) {
+		err = PTR_ERR(phylink);
+		goto err_phylink_create;
+	}
+	priv->phylink = phylink;
+
+	err = phylink_of_phy_connect(priv->phylink, dpmac_node, 0);
+	if (err) {
+		pr_err("phylink_of_phy_connect() = %d\n", err);
+		goto err_phylink_connect;
+	}
+
+	return 0;
+
+err_phylink_connect:
+	phylink_destroy(priv->phylink);
+err_phylink_create:
+err_no_if_mode:
+	dpaa2_mac_teardown_irqs(mc_dev);
+err_version:
+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_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);
+
+	/* PHY teardown */
+	phylink_stop(priv->phylink);
+	phylink_disconnect_phy(priv->phylink);
+	phylink_destroy(priv->phylink);
+
+	/* free resources */
+	dpaa2_mac_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);
+
+	kfree(priv);
+	dev_set_drvdata(dev, NULL);
+
+	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");
-- 
1.9.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ