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] [day] [month] [year] [list]
Message-Id: <20190828173321.25334-5-boon.leong.ong@intel.com>
Date:   Thu, 29 Aug 2019 01:33:20 +0800
From:   Ong Boon Leong <boon.leong.ong@...el.com>
To:     davem@...emloft.net, linux@...linux.org.uk,
        mcoquelin.stm32@...il.com, joabreu@...opsys.com,
        f.fainelli@...il.com, andrew@...n.ch
Cc:     netdev@...r.kernel.org, linux-kernel@...r.kernel.org,
        peppe.cavallaro@...comi, alexandre.torgue@...com,
        weifeng.voon@...el.com
Subject: [RFC net-next v1 4/5] net: phy: introducing support for DWC xPCS logics for EHL & TGL

xPCS is DWC Ethernet Physical Coding Sublayer that can be integrated with
Ethernet MAC controller and acts as converter between GMII and SGMII. An
example is as shown below whereby DWC xPCS is integrated with DW EQoS MAC
to form GbE Controller.

  <-----------------GbE Controller---------->|<--External PHY chip-->

  +----------+         +----+    +---+               +--------------+
  |   EQoS   | <-GMII->| DW |<-->|PHY| <-- SGMII --> | External GbE |
  |   MAC    |         |xPCS|    |IF |               | PHY Chip     |
  +----------+         +----+    +---+               +--------------+
         ^               ^                                  ^
         |               |                                  |
         +---------------------MDIO-------------------------+

xPCS is a Clause-45 MDIO Manageable Device (MMD) and supports basic
functionalities for initializing xPCS and configuring auto negotiation(AN),
loopback, link status, AN advertisement and Link Partner ability are
implemented. The implementation supports the C37 AN for 1000BASE-X and
SGMII (MAC side SGMII only).

Tested-by: Tan, Tee Min <tee.min.tan@...el.com>
Reviewed-by: Voon Weifeng <weifeng.voon@...el.com>
Reviewed-by: Kweh Hock Leong <hock.leong.kweh@...el.com>
Signed-off-by: Ong Boon Leong <boon.leong.ong@...el.com>
---
 drivers/net/phy/Kconfig  |   9 +
 drivers/net/phy/Makefile |   1 +
 drivers/net/phy/dwxpcs.c | 417 +++++++++++++++++++++++++++++++++++++++
 include/linux/dwxpcs.h   |  16 ++
 4 files changed, 443 insertions(+)
 create mode 100644 drivers/net/phy/dwxpcs.c
 create mode 100644 include/linux/dwxpcs.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 03be30cde552..fd037f5366d8 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -364,6 +364,15 @@ config DP83867_PHY
 	---help---
 	  Currently supports the DP83867 PHY.
 
+config DWXPCS
+	tristate "Synopsys DesignWare PCS converter driver"
+	help
+	  This driver supports DW PCS IP that provides the Serial Gigabit
+	  Media Independent Interface(SGMII) between Ethernet physical media
+	  devices and the Gigabit Ethernet controller.
+
+	  Currently tested with stmmac.
+
 config FIXED_PHY
 	tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs"
 	depends on PHYLIB
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index a03437e091f3..5def985ae3ca 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_DP83822_PHY)	+= dp83822.o
 obj-$(CONFIG_DP83TC811_PHY)	+= dp83tc811.o
 obj-$(CONFIG_DP83848_PHY)	+= dp83848.o
 obj-$(CONFIG_DP83867_PHY)	+= dp83867.o
+obj-$(CONFIG_DWXPCS)		+= dwxpcs.o
 obj-$(CONFIG_FIXED_PHY)		+= fixed_phy.o
 obj-$(CONFIG_ICPLUS_PHY)	+= icplus.o
 obj-$(CONFIG_INTEL_XWAY_PHY)	+= intel-xway.o
diff --git a/drivers/net/phy/dwxpcs.c b/drivers/net/phy/dwxpcs.c
new file mode 100644
index 000000000000..f0003cec6871
--- /dev/null
+++ b/drivers/net/phy/dwxpcs.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019, Intel Corporation.
+ * DWC Ethernet Physical Coding Sublayer for GMII2SGMII Converter
+ */
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/dwxpcs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+
+/* XPCS MII MMD Device Addresses */
+#define XPCS_MDIO_MII_MMD	MDIO_MMD_VEND2
+
+/* MII MMD registers offsets */
+#define MDIO_MII_MMD_DIGITAL_CTRL_1	0x8000	/* Digital Control 1 */
+#define MDIO_MII_MMD_AN_CTRL		0x8001	/* AN Control */
+#define MDIO_MII_MMD_AN_STAT		0x8002	/* AN Status */
+
+/* MII MMD SR AN Advertisement & Link Partner Ability are slightly
+ * different from MII_ADVERTISEMENT & MII_LPA in below fields:
+ */
+#define MDIO_MII_MMD_HD			BIT(6)	/* Half duplex */
+#define MDIO_MII_MMD_FD			BIT(5)	/* Full duplex */
+#define MDIO_MII_MMD_PSE_SHIFT		7	/* Pause Ability shift */
+#define MDIO_MII_MMD_PSE		GENMASK(8, 7)	/* Pause Ability */
+#define MDIO_MII_MMD_PSE_NO		0x0
+#define MDIO_MII_MMD_PSE_ASYM		0x1
+#define MDIO_MII_MMD_PSE_SYM		0x2
+#define MDIO_MII_MMD_PSE_BOTH		0x3
+
+/* Automatic Speed Mode Change for MAC side SGMII AN */
+#define MDIO_MII_MMD_DIGI_CTRL_1_MAC_AUTO_SW	BIT(9)
+
+/* MII MMD AN Control defines */
+#define MDIO_MII_MMD_AN_CTRL_TX_CONFIG_SHIFT	3 /* TX Config shift */
+#define AN_CTRL_TX_CONF_PHY_SIDE_SGMII		0x1 /* PHY side SGMII mode */
+#define AN_CTRL_TX_CONF_MAC_SIDE_SGMII		0x0 /* MAC side SGMII mode */
+#define MDIO_MII_MMD_AN_CTRL_PCS_MD_SHIFT	1  /* PCS Mode shift */
+#define MDIO_MII_MMD_AN_CTRL_PCS_MD	GENMASK(2, 1) /* PCS Mode */
+#define AN_CTRL_PCS_MD_C37_1000BASEX	0x0	/* C37 AN for 1000BASE-X */
+#define AN_CTRL_PCS_MD_C37_SGMII	0x2	/* C37 AN for SGMII */
+#define MDIO_MII_MMD_AN_CTRL_AN_INTR_EN	BIT(0)	/* AN Complete Intr Enable */
+
+/* MII MMD AN Status defines for SGMII AN Status */
+#define AN_STAT_C37_AN_CMPLT		BIT(0)	/* AN Complete Intr */
+#define AN_STAT_SGMII_AN_FD		BIT(1)	/* Full Duplex */
+#define AN_STAT_SGMII_AN_SPEED_SHIFT	2	/* AN Speed shift */
+#define AN_STAT_SGMII_AN_SPEED		GENMASK(3, 2)	/* AN Speed */
+#define AN_STAT_SGMII_AN_10MBPS		0x0	/* 10 Mbps */
+#define AN_STAT_SGMII_AN_100MBPS	0x1	/* 100 Mbps */
+#define AN_STAT_SGMII_AN_1000MBPS	0x2	/* 1000 Mbps */
+#define AN_STAT_SGMII_AN_LNKSTS		BIT(4)	/* Link Status */
+
+enum dwxpcs_state_t {
+	__DWXPCS_REMOVING,
+	__DWXPCS_TASK_SCHED,
+};
+
+struct pcs_stats {
+	int link;
+	int speed;
+	int duplex;
+};
+
+struct dwxpcs_priv {
+	struct phy_device *phy_dev;
+	struct phy_driver *phy_drv;
+	struct phy_device cached_phy_dev;
+	struct phy_driver conv_phy_drv;
+	struct mdio_device *mdiodev;
+	struct pcs_stats stats;
+	struct dwxpcs_platform_data *pdata;
+	char int_name[IFNAMSIZ];
+	unsigned long state;
+	struct workqueue_struct *int_wq;
+	struct work_struct an_task;
+};
+
+/* DW xPCS mdiobus_read and mdiobus_write helper functions */
+#define xpcs_read(dev, reg) \
+	mdiobus_read(bus, xpcs_addr, \
+		     MII_ADDR_C45 | (reg) | \
+		     ((dev) << MII_DEVADDR_C45_SHIFT))
+#define xpcs_write(dev, reg, val) \
+	mdiobus_write(bus, xpcs_addr, \
+		      MII_ADDR_C45 | (reg) | \
+		      ((dev) << MII_DEVADDR_C45_SHIFT), val)
+
+static void dwxpcs_init(struct dwxpcs_priv *priv)
+{
+	struct mii_bus *bus = priv->mdiodev->bus;
+	int xpcs_addr = priv->mdiodev->addr;
+	int pcs_mode = priv->pdata->mode;
+	int phydata;
+
+	if (pcs_mode == DWXPCS_MODE_SGMII_AN) {
+		/* For AN for SGMII mode, the settings are :-
+		 * 1) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 10b (SGMII AN)
+		 * 2) VR_MII_AN_CTRL Bit(3) [TX_CONFIG] = 0b (MAC side SGMII)
+		 *    DW xPCS used with DW EQoS MAC is always MAC
+		 *    side SGMII.
+		 * 3) VR_MII_AN_CTRL Bit(0) [AN_INTR_EN] = 1b (AN Interrupt
+		 *    enabled)
+		 * 4) VR_MII_DIG_CTRL1 Bit(9) [MAC_AUTO_SW] = 1b (Automatic
+		 *    speed/duplex mode change by HW after SGMII AN complete)
+		 * Note: Since it is MAC side SGMII, there is no need to set
+		 *	 SR_MII_AN_ADV. MAC side SGMII receives AN Tx Config
+		 *	 from PHY about the link state change after C28 AN
+		 *	 is completed between PHY and Link Partner.
+		 */
+		phydata = xpcs_read(XPCS_MDIO_MII_MMD, MDIO_MII_MMD_AN_CTRL);
+		phydata &= ~MDIO_MII_MMD_AN_CTRL_PCS_MD;
+
+		phydata |= MDIO_MII_MMD_AN_CTRL_AN_INTR_EN |
+			   (AN_CTRL_PCS_MD_C37_SGMII <<
+			    MDIO_MII_MMD_AN_CTRL_PCS_MD_SHIFT &
+			    MDIO_MII_MMD_AN_CTRL_PCS_MD) |
+			   (AN_CTRL_TX_CONF_MAC_SIDE_SGMII <<
+			    MDIO_MII_MMD_AN_CTRL_TX_CONFIG_SHIFT);
+		xpcs_write(XPCS_MDIO_MII_MMD, MDIO_MII_MMD_AN_CTRL, phydata);
+
+		phydata = xpcs_read(XPCS_MDIO_MII_MMD,
+				    MDIO_MII_MMD_DIGITAL_CTRL_1);
+		phydata |= MDIO_MII_MMD_DIGI_CTRL_1_MAC_AUTO_SW;
+		xpcs_write(XPCS_MDIO_MII_MMD, MDIO_MII_MMD_DIGITAL_CTRL_1,
+			   phydata);
+	} else {
+		/* For AN for 1000BASE-X mode, the settings are :-
+		 * 1) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 00b (1000BASE-X C37)
+		 * 2) VR_MII_AN_CTRL Bit(0) [AN_INTR_EN] = 1b (AN Interrupt
+		 *    enabled)
+		 * 3) SR_MII_AN_ADV Bit(6)[FD] = 1b (Full Duplex)
+		 *    Note: Half Duplex is rarely used, so don't advertise.
+		 * 4) SR_MII_AN_ADV Bit(8:7)[PSE] = 11b (Sym & Asym Pause)
+		 */
+		phydata = xpcs_read(XPCS_MDIO_MII_MMD, MDIO_MII_MMD_AN_CTRL);
+		phydata &= ~MDIO_MII_MMD_AN_CTRL_PCS_MD;
+		phydata |= MDIO_MII_MMD_AN_CTRL_AN_INTR_EN |
+			   (AN_CTRL_PCS_MD_C37_1000BASEX <<
+			    MDIO_MII_MMD_AN_CTRL_PCS_MD_SHIFT &
+			    MDIO_MII_MMD_AN_CTRL_PCS_MD);
+		xpcs_write(XPCS_MDIO_MII_MMD, MDIO_MII_MMD_AN_CTRL, phydata);
+
+		phydata = xpcs_read(XPCS_MDIO_MII_MMD, MII_ADVERTISE);
+		phydata |= MDIO_MII_MMD_FD |
+			   (MDIO_MII_MMD_PSE_BOTH << MDIO_MII_MMD_PSE_SHIFT);
+		xpcs_write(XPCS_MDIO_MII_MMD, MII_ADVERTISE, phydata);
+	}
+}
+
+static int dwxpcs_read_status(struct phy_device *phydev)
+{
+	struct dwxpcs_priv *priv = (struct dwxpcs_priv *)phydev->priv;
+	struct mii_bus *bus = priv->mdiodev->bus;
+	int xpcs_addr = priv->mdiodev->addr;
+	int pcs_mode = priv->pdata->mode;
+	int phydata;
+	int err;
+
+	if (priv->phy_drv->read_status)
+		err = priv->phy_drv->read_status(phydev);
+	else
+		err = genphy_read_status(phydev);
+
+	if (err < 0)
+		return err;
+
+	/* For SGMII AN, we are done as the speed/duplex are automatically
+	 * set because we have initialized 'MAC_AUTO_SW' for MAC side SGMII.
+	 */
+	if (pcs_mode == DWXPCS_MODE_1000BASEX_AN) {
+		/* For 1000BASE-X AN, we need to adjust duplex mode according
+		 * to link partner. No need to update speed as it is always
+		 * 1000Mbps.
+		 */
+		phydata = xpcs_read(XPCS_MDIO_MII_MMD, MII_BMCR);
+		phydata &= ~BMCR_FULLDPLX;
+		phydata |= phydev->duplex ? BMCR_FULLDPLX : 0;
+		xpcs_write(XPCS_MDIO_MII_MMD, MII_BMCR, phydata);
+	}
+
+	return 0;
+}
+
+static void dwxpcs_get_linkstatus(struct dwxpcs_priv *priv, int an_stat)
+{
+	struct mii_bus *bus = priv->mdiodev->bus;
+	struct pcs_stats *stats = &priv->stats;
+	int xpcs_addr = priv->mdiodev->addr;
+	int pcs_mode = priv->pdata->mode;
+
+	if (pcs_mode == DWXPCS_MODE_SGMII_AN) {
+		/* Check the SGMII AN link status */
+		if (an_stat & AN_STAT_SGMII_AN_LNKSTS) {
+			int speed_value;
+
+			stats->link = 1;
+
+			speed_value = ((an_stat & AN_STAT_SGMII_AN_SPEED) >>
+					AN_STAT_SGMII_AN_SPEED_SHIFT);
+			if (speed_value == AN_STAT_SGMII_AN_1000MBPS)
+				stats->speed = SPEED_1000;
+			else if (speed_value == AN_STAT_SGMII_AN_100MBPS)
+				stats->speed = SPEED_100;
+			else
+				stats->speed = SPEED_10;
+
+			if (an_stat & AN_STAT_SGMII_AN_FD)
+				stats->duplex = 1;
+			else
+				stats->duplex = 0;
+		} else {
+			stats->link = 0;
+		}
+	} else if (pcs_mode == DWXPCS_MODE_1000BASEX_AN) {
+		/* For 1000BASE-X AN, 1000BASE-X is always 1000Mbps.
+		 * For duplex mode, we read from BMCR_FULLDPLX which is
+		 * only valid if BMCR_ANENABLE is not enabeld.
+		 */
+		int phydata = xpcs_read(XPCS_MDIO_MII_MMD, MII_BMCR);
+
+		stats->link = 1;
+		stats->speed = SPEED_1000;
+		if (!(phydata & BMCR_ANENABLE))
+			stats->duplex = phydata & BMCR_FULLDPLX ? 1 : 0;
+	}
+}
+
+static void dwxpcs_irq_handle(struct dwxpcs_priv *priv)
+{
+	struct mii_bus *bus = priv->mdiodev->bus;
+	struct device *dev = &priv->mdiodev->dev;
+	int xpcs_addr = priv->mdiodev->addr;
+	int an_stat;
+
+	/* AN status */
+	an_stat = xpcs_read(XPCS_MDIO_MII_MMD, MDIO_MII_MMD_AN_STAT);
+
+	if (an_stat & AN_STAT_C37_AN_CMPLT) {
+		struct pcs_stats *stats = &priv->stats;
+
+		dwxpcs_get_linkstatus(priv, an_stat);
+
+		/* Clear C37 AN complete status by writing zero */
+		xpcs_write(XPCS_MDIO_MII_MMD, MDIO_MII_MMD_AN_STAT, 0);
+
+		dev_info(dev, "%s: Link = %d - %d/%s\n",
+			 __func__,
+			 stats->link,
+			 stats->speed,
+			 stats->duplex ? "Full" : "Half");
+	}
+}
+
+static void dwxpcs_an_task(struct work_struct *work)
+{
+	struct dwxpcs_priv *priv = container_of(work,
+						struct dwxpcs_priv,
+						an_task);
+	dwxpcs_irq_handle(priv);
+
+	clear_bit(__DWXPCS_TASK_SCHED, &priv->state);
+}
+
+static irqreturn_t dwxpcs_interrupt(int irq, void *dev_id)
+{
+	struct dwxpcs_priv *priv = (struct dwxpcs_priv *)dev_id;
+
+	/* Handle the clearing of AN status outside of interrupt context
+	 * as it involves mdiobus_read() & mdiobus_write().
+	 */
+	if (!test_bit(__DWXPCS_REMOVING, &priv->state) &&
+	    !test_and_set_bit(__DWXPCS_TASK_SCHED, &priv->state)) {
+		queue_work(priv->int_wq, &priv->an_task);
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id dwxpcs_acpi_match[] = {
+	{ "INTC1033" }, /* EHL Ethernet PCS */
+	{ "INTC1034" }, /* TGL Ethernet PCS */
+	{ },
+};
+
+MODULE_DEVICE_TABLE(acpi, dwxpcs_acpi_match);
+#endif // CONFIG_ACPI
+
+static int dwxpcs_probe(struct mdio_device *mdiodev)
+{
+	struct device_node *phy_node;
+	struct dwxpcs_priv *priv;
+	struct device_node *np;
+	struct device *dev;
+	int ret;
+
+	dev = &mdiodev->dev;
+	np = dev->of_node;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	if (np) {
+		/* Handle mdio_device registered through devicetree */
+		phy_node = of_parse_phandle(np, "phy-handle", 0);
+		if (!phy_node) {
+			dev_err(dev, "Couldn't parse phy-handle\n");
+			return -ENODEV;
+		}
+
+		priv->phy_dev = of_phy_find_device(phy_node);
+		of_node_put(phy_node);
+		if (!priv->phy_dev) {
+			dev_info(dev, "Couldn't find phydev\n");
+			return -EPROBE_DEFER;
+		}
+	} else {
+		/* Handle mdio_device registered through mdio_board_info */
+		priv->pdata = (struct dwxpcs_platform_data *)dev->platform_data;
+
+		priv->phy_dev = mdiobus_get_phy(mdiodev->bus,
+						priv->pdata->ext_phy_addr);
+	}
+
+	if (!priv->phy_dev) {
+		dev_info(dev, "Couldn't find phydev\n");
+		return -EPROBE_DEFER;
+	}
+
+	if (!priv->phy_dev->drv) {
+		dev_info(dev, "Attached phy not ready\n");
+		return -EPROBE_DEFER;
+	}
+
+	priv->mdiodev = mdiodev;
+
+	/* Initialize DW XPCS */
+	dwxpcs_init(priv);
+
+	priv->phy_drv = priv->phy_dev->drv;
+	memcpy(&priv->conv_phy_drv, priv->phy_dev->drv,
+	       sizeof(struct phy_driver));
+	priv->conv_phy_drv.read_status = dwxpcs_read_status;
+	/* Store a copy of phy_dev info for remove() later */
+	priv->cached_phy_dev.priv = priv->phy_dev->priv;
+	priv->cached_phy_dev.drv = priv->phy_dev->drv;
+	priv->phy_dev->priv = priv;
+	priv->phy_dev->drv = &priv->conv_phy_drv;
+
+	if (priv->pdata->irq > 0) {
+		char *int_name;
+
+		INIT_WORK(&priv->an_task, dwxpcs_an_task);
+		clear_bit(__DWXPCS_TASK_SCHED, &priv->state);
+
+		int_name = priv->int_name;
+		sprintf(int_name, "%s-%d", "dwxpcs", priv->mdiodev->dev.id);
+		priv->int_wq = create_singlethread_workqueue(int_name);
+		if (!priv->int_wq) {
+			dev_err(dev, "%s: Failed to create workqueue\n",
+				int_name);
+			return -ENOMEM;
+		}
+
+		ret = request_irq(priv->pdata->irq, dwxpcs_interrupt,
+				  IRQF_SHARED, int_name, priv);
+		if (unlikely(ret < 0)) {
+			destroy_workqueue(priv->int_wq);
+			dev_err(dev, "%s: Allocating DW XPCS IRQ %d (%d)\n",
+				__func__, priv->pdata->irq, ret);
+			return ret;
+		}
+	}
+	dev_info(dev, "%s: DW XPCS mdio device (IRQ: %d) probed successful\n",
+		 __func__, priv->pdata->irq);
+
+	mdiodev->priv = priv;
+
+	return 0;
+}
+
+static void dwxpcs_remove(struct mdio_device *mdiodev)
+{
+	struct dwxpcs_priv *priv = (struct dwxpcs_priv *)mdiodev->priv;
+
+	set_bit(__DWXPCS_REMOVING, &priv->state);
+
+	/* Restore the original phy_device info */
+	priv->phy_dev->priv = priv->cached_phy_dev.priv;
+	priv->phy_dev->drv = priv->cached_phy_dev.drv;
+
+	free_irq(priv->pdata->irq, priv);
+	if (priv->int_wq)
+		destroy_workqueue(priv->int_wq);
+}
+
+static struct mdio_driver dwxpcs_driver = {
+	.probe = dwxpcs_probe,
+	.remove = dwxpcs_remove,
+	.mdiodrv.driver = {
+		.name = "dwxpcs",
+		.acpi_match_table = ACPI_PTR(dwxpcs_acpi_match),
+	},
+};
+
+mdio_module_driver(dwxpcs_driver);
+
+MODULE_DESCRIPTION("DW xPCS converter driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/dwxpcs.h b/include/linux/dwxpcs.h
new file mode 100644
index 000000000000..2082e800ee04
--- /dev/null
+++ b/include/linux/dwxpcs.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_DWXPCS_H
+#define __LINUX_DWXPCS_H
+
+enum dwxpcs_pcs_mode {
+	DWXPCS_MODE_SGMII_AN,
+	DWXPCS_MODE_1000BASEX_AN,
+};
+
+struct dwxpcs_platform_data {
+	int irq;
+	enum dwxpcs_pcs_mode mode;
+	int ext_phy_addr;
+};
+
+#endif
-- 
2.17.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ