[PATCH v2 5/6] phy: Add USB3 PHY support for Broadcom NS2 SoC

Jon Mason jon.mason at broadcom.com
Wed Jul 26 15:56:26 PDT 2017


From: Yendapally Reddy Dhananjaya Reddy <yendapally.reddy at broadcom.com>

This patch adds support for Broadcom NS2 USB3 PHY

Signed-off-by: Yendapally Reddy Dhananjaya Reddy <yendapally.reddy at broadcom.com>
Signed-off-by: Jon Mason <jon.mason at broadcom.com>
---
 drivers/phy/broadcom/Kconfig            |   9 +
 drivers/phy/broadcom/Makefile           |   1 +
 drivers/phy/broadcom/phy-bcm-ns2-usb3.c | 580 ++++++++++++++++++++++++++++++++
 3 files changed, 590 insertions(+)
 create mode 100644 drivers/phy/broadcom/phy-bcm-ns2-usb3.c

diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig
index 37371b89b14f..e7d238efd539 100644
--- a/drivers/phy/broadcom/Kconfig
+++ b/drivers/phy/broadcom/Kconfig
@@ -45,6 +45,15 @@ config PHY_NS2_PCIE
 	  Enable this to support the Broadcom Northstar2 PCIe PHY.
 	  If unsure, say N.
 
+config PHY_NS2_USB3
+	tristate "Broadcom NorthStar2 USB3 PHY driver"
+	depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST)
+	select GENERIC_PHY
+	default ARCH_BCM_IPROC
+	help
+	  Enable this to support the Broadcom Northstar2 USB3 PHY.
+	  If unsure, say N.
+
 config PHY_NS2_USB_DRD
 	tristate "Broadcom Northstar2 USB DRD PHY support"
 	depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST)
diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile
index 4eb82ec8d491..a40e6e6f56b8 100644
--- a/drivers/phy/broadcom/Makefile
+++ b/drivers/phy/broadcom/Makefile
@@ -3,5 +3,6 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY)		+= phy-bcm-kona-usb2.o
 obj-$(CONFIG_PHY_BCM_NS_USB2)		+= phy-bcm-ns-usb2.o
 obj-$(CONFIG_PHY_BCM_NS_USB3)		+= phy-bcm-ns-usb3.o
 obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
+obj-$(CONFIG_PHY_NS2_USB3)		+= phy-bcm-ns2-usb3.o
 obj-$(CONFIG_PHY_NS2_USB_DRD)		+= phy-bcm-ns2-usbdrd.o
 obj-$(CONFIG_PHY_BRCM_SATA)		+= phy-brcm-sata.o
diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usb3.c b/drivers/phy/broadcom/phy-bcm-ns2-usb3.c
new file mode 100644
index 000000000000..7f272757434b
--- /dev/null
+++ b/drivers/phy/broadcom/phy-bcm-ns2-usb3.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+
+#define NS2_USB3_PHY_MAX			0x02
+
+#define NS2_USB3_PHY_CONFIG_CTRL_REG		0x10
+#define NS2_USB3_PHY_CONFIG_CTRL_MASK		(BIT(3) | BIT(4) | BIT(5))
+#define NS2_USB3_PHY_CONFIG_CTRL_PLL_SEQ_START	BIT(6)
+
+#define NS2_USB3_PHY_P0CTL_REG			0x14
+#define NS2_USB3_PHY_P1CTL_REG			0x18
+#define NS2_USB3_PHY_PXCTL_I_BIT		BIT(1)
+
+#define NS2_USB3_PHY_MISC_STATUS_REG		0x20
+
+#define NS2_IDM_RST_CTRL_P0_OFFSET		0x800
+#define NS2_IDM_RST_CTRL_P1_OFFSET		0x1800
+#define NS2_IDM_RESET_CONTROL_BIT		BIT(0)
+
+#define NS2_IDM_IO_CTRL_P0_OFFSET		0x408
+#define NS2_IDM_IO_CTRL_P1_OFFSET		0x1408
+/* Bit 23 for PPC Polarity, Bit 24 for PPC NANDNOR select */
+#define NS2_IDM_IO_CTRL_PPC_CFG			(BIT(23) | BIT(24))
+
+#define NS2_PHY_RESET_BIT			BIT(5)
+#define NS2_PHY_PLL_RESET_BIT			BIT(6)
+
+/* NS2 USB3 MDIO */
+#define NS2_USB3_MDIO_PLL30_ADDR		0x8000
+#define NS2_USB3_MDIO_BLK_ACCESS		0x1F
+#define NS2_USB3_MDIO_PLL30_ANAPLL_CTRL		0x14
+#define NS2_USB3_MDIO_PLL30_ANAPLL_CTRL_VAL	0x23
+#define NS2_USB3_MDIO_PLL30_GEN_PLL		0xF
+#define NS2_USB3_MDIO_PLL30_GEN_PLL_PCLK_SEL	BIT(11)
+#define NS2_USB3_MDIO_P0_AFE30_ADDR		0x8080
+#define NS2_USB3_MDIO_P1_AFE30_ADDR		0x9080
+#define NS2_USB3_MDIO_AFE30_RX_SIG_DETECT	0x5
+#define NS2_USB3_MDIO_AFE30_RX_SIG_DETECT_VAL	0xAC0D
+
+#define NS2_USB3_MDIO_P0_PIPE_BLK_ADDR		0x8060
+#define NS2_USB3_MDIO_P1_PIPE_BLK_ADDR		0x9060
+#define NS2_USB3_MDIO_PIPE_BLK_REG_1_OFFSET	0x1
+#define NS2_USB3_MDIO_PIPE_BLK_REG_1_VAL	0x207
+
+#define NS2_USB3_MDIO_P0_AEQ_BLK_ADDR		0x80E0
+#define NS2_USB3_MDIO_P1_AEQ_BLK_ADDR		0x90E0
+#define NS2_USB3_MDIO_AEQ_BLK_REG_1_OFFSET	0x1
+#define NS2_USB3_MDIO_AEQ_BLK_REG_1_VAL		0x3000
+
+enum ns2_phy_block {
+	PHY_RESET,
+	PHY_PLL_RESET,
+	PHY_SOFT_RESET,
+	PHY_PIPE_RESET,
+	PHY_REF_CLOCK,
+	PHY_PLL_SEQ_START,
+	PHY_PLL_STATUS,
+	PHY_VBUS_PPC,
+};
+
+enum ns2_reg_base {
+	NS2_USB3_CTRL = 1,
+	NS2_USB3_PHY_CFG,
+	NS2_USB3_RST_CTRL,
+	NS2_USB3_REG_BASE_MAX
+};
+
+struct ns2_usb3_phy {
+	void __iomem *reg_base[NS2_USB3_REG_BASE_MAX];
+	struct ns2_usb3_phy_master *mphy;
+	struct phy *phy;
+	int port_no;
+};
+
+struct ns2_usb3_phy_master {
+	struct ns2_usb3_phy iphys[NS2_USB3_PHY_MAX];
+	struct mdio_device *mdiodev;
+	struct mutex phy_mutex;
+	int init_count; /* PHY is dual port phy, so init once*/
+};
+
+static int iproc_ns2_phy_action(struct ns2_usb3_phy *iphy,
+				enum ns2_phy_block block, bool assert)
+{
+	void __iomem *addr;
+	u32  data, count;
+	u32 offset = 0;
+	int ret = 0;
+
+	switch (block) {
+	case PHY_RESET:
+		addr = iphy->reg_base[NS2_USB3_CTRL];
+
+		data = readl(addr + offset);
+
+		if (assert)
+			data &= ~NS2_PHY_RESET_BIT;
+		else
+			data |= NS2_PHY_RESET_BIT;
+
+		writel(data, addr + offset);
+		break;
+
+	case PHY_PLL_RESET:
+		addr = iphy->reg_base[NS2_USB3_CTRL];
+
+		data = readl(addr + offset);
+
+		if (assert)
+			data &= ~NS2_PHY_PLL_RESET_BIT;
+		else
+			data |= NS2_PHY_PLL_RESET_BIT;
+
+		writel(data, addr + offset);
+		break;
+
+	case PHY_SOFT_RESET:
+		addr = iphy->reg_base[NS2_USB3_PHY_CFG];
+		offset = NS2_USB3_PHY_P0CTL_REG;
+
+		data = readl(addr + offset);
+
+		if (assert)
+			data &= ~NS2_USB3_PHY_PXCTL_I_BIT;
+		else
+			data |= NS2_USB3_PHY_PXCTL_I_BIT;
+
+		writel(data, addr + offset);
+
+		offset = NS2_USB3_PHY_P1CTL_REG;
+
+		data = readl(addr + offset);
+
+		if (assert)
+			data &= ~NS2_USB3_PHY_PXCTL_I_BIT;
+		else
+			data |= NS2_USB3_PHY_PXCTL_I_BIT;
+
+		writel(data, addr + offset);
+		break;
+
+	case PHY_PIPE_RESET:
+		addr = iphy->reg_base[NS2_USB3_RST_CTRL];
+		offset = NS2_IDM_RST_CTRL_P0_OFFSET;
+
+		data = readl(addr + offset);
+
+		if (assert)
+			data |= NS2_IDM_RESET_CONTROL_BIT;
+		else
+			data &= ~NS2_IDM_RESET_CONTROL_BIT;
+
+		writel(data, addr + offset);
+
+		offset = NS2_IDM_RST_CTRL_P1_OFFSET;
+		data = readl(addr + offset);
+
+		if (assert)
+			data |= NS2_IDM_RESET_CONTROL_BIT;
+		else
+			data &= ~NS2_IDM_RESET_CONTROL_BIT;
+
+		writel(data, addr + offset);
+		break;
+
+	case PHY_VBUS_PPC:
+		addr = iphy->reg_base[NS2_USB3_RST_CTRL];
+		offset = NS2_IDM_IO_CTRL_P0_OFFSET;
+
+		data = readl(addr + offset);
+
+		if (assert)
+			data |= NS2_IDM_IO_CTRL_PPC_CFG;
+		else
+			data &= ~NS2_IDM_IO_CTRL_PPC_CFG;
+
+		writel(data, addr + offset);
+
+		offset = NS2_IDM_IO_CTRL_P1_OFFSET;
+		data = readl(addr + offset);
+
+		if (assert)
+			data |= NS2_IDM_IO_CTRL_PPC_CFG;
+		else
+			data &= ~NS2_IDM_IO_CTRL_PPC_CFG;
+
+		writel(data, addr + offset);
+		break;
+
+	case PHY_REF_CLOCK:
+		addr = iphy->reg_base[NS2_USB3_PHY_CFG];
+		offset = NS2_USB3_PHY_CONFIG_CTRL_REG;
+
+		data = readl(addr + offset);
+
+		data &= ~NS2_USB3_PHY_CONFIG_CTRL_MASK;
+
+		writel(data, addr + offset);
+		break;
+
+	case PHY_PLL_SEQ_START:
+		addr = iphy->reg_base[NS2_USB3_PHY_CFG];
+		offset = NS2_USB3_PHY_CONFIG_CTRL_REG;
+
+		data = readl(addr + offset);
+
+		data |= NS2_USB3_PHY_CONFIG_CTRL_PLL_SEQ_START;
+
+		writel(data, addr + offset);
+		break;
+
+	case PHY_PLL_STATUS:
+		count = 2000;
+		addr = iphy->reg_base[NS2_USB3_PHY_CFG];
+		offset = NS2_USB3_PHY_MISC_STATUS_REG;
+
+		do {
+			udelay(1);
+
+			data = readl(addr + offset);
+			if (data == 1)
+				break;
+		} while (--count);
+
+		if (!count)
+			ret = -ETIMEDOUT;
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int ns2_usb3_phy_exit(struct phy *phy)
+{
+	struct ns2_usb3_phy *iphy = phy_get_drvdata(phy);
+	int rc = 0;
+
+	mutex_lock(&iphy->mphy->phy_mutex);
+
+	if (iphy->mphy->init_count <= 0) {
+		mutex_unlock(&iphy->mphy->phy_mutex);
+		return 0;
+	} else if (iphy->mphy->init_count == 1) {
+		/* Only put in to reset for last port to exit */
+		rc = iproc_ns2_phy_action(iphy, PHY_PLL_RESET, true);
+		if (rc)
+			goto out;
+
+		rc = iproc_ns2_phy_action(iphy, PHY_SOFT_RESET, true);
+		if (rc)
+			goto out;
+
+		rc = iproc_ns2_phy_action(iphy, PHY_RESET, true);
+		if (rc)
+			goto out;
+
+		rc = iproc_ns2_phy_action(iphy, PHY_PIPE_RESET, true);
+		if (rc)
+			goto out;
+	}
+
+out:
+	iphy->mphy->init_count--;
+	mutex_unlock(&iphy->mphy->phy_mutex);
+
+	return rc;
+}
+
+static int ns2_usb3_phy_init(struct phy *phy)
+{
+	struct ns2_usb3_phy *iphy = phy_get_drvdata(phy);
+	u16 addr;
+	u16 reg_val;
+	int rc;
+
+	mutex_lock(&iphy->mphy->phy_mutex);
+
+	if (iphy->mphy->init_count) {
+		/* Use count to identify last port to call phy_exit. */
+		iphy->mphy->init_count++;
+		mutex_unlock(&iphy->mphy->phy_mutex);
+		return 0;
+	}
+
+	rc = iproc_ns2_phy_action(iphy, PHY_RESET, false);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_SOFT_RESET, true);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_PIPE_RESET, true);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_REF_CLOCK, true);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_PLL_RESET, true);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_RESET, true);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_RESET, false);
+	if (rc)
+		goto out;
+
+	/* PLL programming */
+	/* PHY PLL30 Block */
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_BLK_ACCESS,
+				NS2_USB3_MDIO_PLL30_ADDR);
+	if (rc)
+		goto out;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_PLL30_ANAPLL_CTRL,
+				NS2_USB3_MDIO_PLL30_ANAPLL_CTRL_VAL);
+	if (rc)
+		goto out;
+
+	reg_val = (u16) mdiobus_read(iphy->mphy->mdiodev->bus,
+				iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_PLL30_GEN_PLL);
+	reg_val |= NS2_USB3_MDIO_PLL30_GEN_PLL_PCLK_SEL;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_PLL30_GEN_PLL, reg_val);
+	if (rc)
+		goto out;
+
+	/* PHY AFE30 Block */
+	addr = NS2_USB3_MDIO_P0_AFE30_ADDR;
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_BLK_ACCESS, addr);
+	if (rc)
+		goto out;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_AFE30_RX_SIG_DETECT,
+				NS2_USB3_MDIO_AFE30_RX_SIG_DETECT_VAL);
+	if (rc)
+		goto out;
+
+	addr = NS2_USB3_MDIO_P1_AFE30_ADDR;
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_BLK_ACCESS, addr);
+	if (rc)
+		goto out;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_AFE30_RX_SIG_DETECT,
+				NS2_USB3_MDIO_AFE30_RX_SIG_DETECT_VAL);
+	if (rc)
+		goto out;
+
+	/* PHY PIPE Block */
+	addr = NS2_USB3_MDIO_P0_PIPE_BLK_ADDR;
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_BLK_ACCESS, addr);
+	if (rc)
+		goto out;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_PIPE_BLK_REG_1_OFFSET,
+				NS2_USB3_MDIO_PIPE_BLK_REG_1_VAL);
+	if (rc)
+		goto out;
+
+	addr = NS2_USB3_MDIO_P1_PIPE_BLK_ADDR;
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_BLK_ACCESS, addr);
+	if (rc)
+		goto out;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_PIPE_BLK_REG_1_OFFSET,
+				NS2_USB3_MDIO_PIPE_BLK_REG_1_VAL);
+	if (rc)
+		goto out;
+
+	/* AEQ Block */
+	addr = NS2_USB3_MDIO_P0_AEQ_BLK_ADDR;
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_BLK_ACCESS, addr);
+	if (rc)
+		goto out;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_AEQ_BLK_REG_1_OFFSET,
+				NS2_USB3_MDIO_AEQ_BLK_REG_1_VAL);
+	if (rc)
+		goto out;
+
+	/* PHY PORT_1 */
+	addr = NS2_USB3_MDIO_P1_AEQ_BLK_ADDR;
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_BLK_ACCESS, addr);
+	if (rc)
+		goto out;
+
+	rc = mdiobus_write(iphy->mphy->mdiodev->bus, iphy->mphy->mdiodev->addr,
+				NS2_USB3_MDIO_AEQ_BLK_REG_1_OFFSET,
+				NS2_USB3_MDIO_AEQ_BLK_REG_1_VAL);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_PLL_SEQ_START, true);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_PIPE_RESET, false);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_SOFT_RESET, false);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_PLL_RESET, false);
+	if (rc)
+		goto out;
+
+	rc = iproc_ns2_phy_action(iphy, PHY_PLL_STATUS, true);
+	if (rc)
+		goto out;
+
+	/* Set USB3H VBUS PPC Polarity and NandNor select */
+	rc = iproc_ns2_phy_action(iphy, PHY_VBUS_PPC, true);
+
+out:
+	iphy->mphy->init_count++;
+	mutex_unlock(&iphy->mphy->phy_mutex);
+
+	return rc;
+}
+
+static struct phy_ops ns2_usb3_phy_ops = {
+	.init = ns2_usb3_phy_init,
+	.exit = ns2_usb3_phy_exit,
+	.owner = THIS_MODULE,
+};
+
+static int ns2_usb3_phy_map_by_name(struct ns2_usb3_phy *iphy,
+				    enum ns2_reg_base reg, const char *compat)
+{
+	struct device_node *temp;
+	void __iomem *base;
+	struct device *dev = &iphy->mphy->mdiodev->dev;
+
+	temp = of_find_compatible_node(NULL, NULL, compat);
+	if (!temp) {
+		dev_err(dev, "Unable to find %s in Device Tree\n", compat);
+		return -EINVAL;
+	}
+
+	base = of_iomap(temp, 0);
+	if (!base) {
+		dev_err(dev, "Unable to map %s\n", compat);
+		return -EINVAL;
+	}
+	iphy->reg_base[reg] = base;
+
+	return 0;
+}
+
+static int ns2_usb3_phy_probe(struct mdio_device *mdiodev)
+{
+	struct device *dev = &mdiodev->dev;
+	struct device_node *dn = dev->of_node, *child;
+	struct ns2_usb3_phy_master *mphy;
+	struct phy_provider *provider;
+	int cnt;
+
+	mphy = devm_kzalloc(dev, sizeof(*mphy), GFP_KERNEL);
+	if (!mphy)
+		return -ENOMEM;
+	mphy->mdiodev = mdiodev;
+	mutex_init(&mphy->phy_mutex);
+	mphy->init_count = 0;
+
+	cnt = 0;
+	for_each_available_child_of_node(dn, child) {
+		struct ns2_usb3_phy *iphy;
+		unsigned int val;
+		int rc;
+
+		iphy = &mphy->iphys[cnt];
+		if (of_property_read_u32(child, "reg", &val)) {
+			dev_err(dev, "missing reg property in node %s\n",
+					child->name);
+			return -EINVAL;
+		}
+		iphy->port_no = val;
+		iphy->mphy = mphy;
+
+		rc = ns2_usb3_phy_map_by_name(iphy, NS2_USB3_CTRL,
+					      "brcm,cdru-usb3-ctrl");
+		if (rc)
+			return rc;
+
+		rc = ns2_usb3_phy_map_by_name(iphy, NS2_USB3_PHY_CFG,
+					      "brcm,icfg-usb");
+		if (rc)
+			return rc;
+
+		rc = ns2_usb3_phy_map_by_name(iphy, NS2_USB3_RST_CTRL,
+					      "brcm,usb3-idm");
+		if (rc)
+			return rc;
+
+		iphy->phy = devm_phy_create(dev, child, &ns2_usb3_phy_ops);
+		if (IS_ERR(iphy->phy)) {
+			dev_err(dev, "failed to create PHY\n");
+			return PTR_ERR(iphy->phy);
+		}
+
+		phy_set_drvdata(iphy->phy, iphy);
+		cnt++;
+	}
+
+	dev_set_drvdata(dev, mphy);
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(provider)) {
+		dev_err(dev, "could not register PHY provider\n");
+		return PTR_ERR(provider);
+	}
+
+	dev_info(dev, "registered %d phy(s)\n", cnt);
+	return 0;
+}
+
+static const struct of_device_id ns2_usb3_phy_of_match[] = {
+	{.compatible = "brcm,ns2-usb3-phy",},
+	{ /* sentinel */ }
+};
+
+static struct mdio_driver ns2_usb3_phy_driver = {
+	.mdiodrv = {
+		.driver = {
+			.name = "ns2-usb3-phy",
+			.of_match_table = ns2_usb3_phy_of_match,
+		},
+	},
+	.probe = ns2_usb3_phy_probe,
+};
+mdio_module_driver(ns2_usb3_phy_driver);
+
+MODULE_DESCRIPTION("Broadcom NS2 USB3 PHY driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
-- 
2.7.4




More information about the linux-arm-kernel mailing list