[PATCH] usb: phy: bcm-ns-usb3: new driver for USB 3.0 PHY on Northstar

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Northstar is a family of SoCs used in home routers. They have USB 2.0
and 3.0 controllers with PHYs that need to be properly initialized.
This driver provides PHY init support in a generic way and can be bound
with XHCI controller driver.

Signed-off-by: Rafał Miłecki <zajec5@xxxxxxxxx>
---
 .../devicetree/bindings/usb/ns-usb3-phy.txt        |  14 ++
 drivers/usb/phy/Kconfig                            |   8 +
 drivers/usb/phy/Makefile                           |   1 +
 drivers/usb/phy/phy-bcm-ns-usb3.c                  | 256 +++++++++++++++++++++
 include/linux/bcma/bcma_driver_chipcommon.h        |   3 +
 5 files changed, 282 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/ns-usb3-phy.txt
 create mode 100644 drivers/usb/phy/phy-bcm-ns-usb3.c

diff --git a/Documentation/devicetree/bindings/usb/ns-usb3-phy.txt b/Documentation/devicetree/bindings/usb/ns-usb3-phy.txt
new file mode 100644
index 0000000..ded03a9
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/ns-usb3-phy.txt
@@ -0,0 +1,14 @@
+Driver for Broadcom Northstar USB 3.0 PHY
+
+Required properties:
+- compatible: brcm,ns-usb3-phy
+- bus: phandle of brcm,bus-axi
+
+To initialize USB 3.0 PHY driver needs to access various bus cores (devices).
+This is why phandle to bcma bus is needed.
+
+Example:
+	usb3-phy {
+		compatible = "brcm,ns-usb3-phy";
+		bus = <&axi>;
+	};
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index c690474..aa28ea7 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -18,6 +18,14 @@ config AB8500_USB
 	  This transceiver supports high and full speed devices plus,
 	  in host mode, low speed.
 
+config BCM_NS_USB3_PHY
+	tristate "Broadcom Northstar USB 3.0 PHY Driver"
+	depends on BCMA
+	select USB_PHY
+	help
+	  Enable this to support USB 3.0 PHY connected to the USB controller on
+	  bcma bus.
+
 config FSL_USB2_OTG
 	bool "Freescale USB OTG Transceiver Driver"
 	depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index b433e5d..5247572 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_OF)			+= of.o
 # transceiver drivers, keep the list sorted
 
 obj-$(CONFIG_AB8500_USB)		+= phy-ab8500-usb.o
+obj-$(CONFIG_BCM_NS_USB3_PHY)		+= phy-bcm-ns-usb3.o
 obj-$(CONFIG_FSL_USB2_OTG)		+= phy-fsl-usb.o
 obj-$(CONFIG_ISP1301_OMAP)		+= phy-isp1301-omap.o
 obj-$(CONFIG_NOP_USB_XCEIV)		+= phy-generic.o
diff --git a/drivers/usb/phy/phy-bcm-ns-usb3.c b/drivers/usb/phy/phy-bcm-ns-usb3.c
new file mode 100644
index 0000000..23c95f4
--- /dev/null
+++ b/drivers/usb/phy/phy-bcm-ns-usb3.c
@@ -0,0 +1,256 @@
+/*
+ * Broadcom Northstar USB 3.0 PHY Driver
+ *
+ * Copyright (C) 2016 Rafał Miłecki <zajec5@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/bcma/bcma.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/usb/otg.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#define BCM_NS_USB3_MII_MNG_TIMEOUT	1000	/* us */
+
+struct bcm_ns_usb3 {
+	struct device *dev;
+	struct bcma_bus *bus;
+	struct bcma_device *core;
+	struct usb_phy phy;
+};
+
+static inline struct bcm_ns_usb3 *phy_to_usb3(struct usb_phy *phy)
+{
+	return container_of(phy, struct bcm_ns_usb3, phy);
+}
+
+static bool bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr,
+				 u32 mask, u32 value, int timeout)
+{
+	unsigned long deadline = jiffies + timeout;
+	u32 val;
+
+	do {
+		val = readl(addr);
+		if ((val & mask) == value)
+			return true;
+		cpu_relax();
+		udelay(10);
+	} while (!time_after_eq(jiffies, deadline));
+
+	dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
+
+	return false;
+}
+
+static inline bool bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
+{
+	struct bcma_drv_cc_b *ccb = &usb3->bus->drv_cc_b;
+
+	return bcm_ns_usb3_wait_reg(usb3, ccb->mii + BCMA_CCB_MII_MNG_CTL,
+				    0x0100, 0x0000,
+				    BCM_NS_USB3_MII_MNG_TIMEOUT);
+}
+
+static void bcm_ns_usb3_mii_mng_write32(struct bcm_ns_usb3 *usb3, u32 value)
+{
+	struct bcma_drv_cc_b *ccb = &usb3->bus->drv_cc_b;
+
+	bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+	iowrite32(value, ccb->mii + BCMA_CCB_MII_MNG_CMD_DATA);
+}
+
+static void bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
+{
+	struct bcma_drv_cc_b *ccb = &usb3->bus->drv_cc_b;
+
+	/* Enable MDIO. Setting MDCDIV as 26  */
+	iowrite32(0x0000009a, ccb->mii + BCMA_CCB_MII_MNG_CTL);
+	udelay(2);
+
+	/* USB3 PLL Block */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
+
+	/* Assert Ana_Pllseq start */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x58061000);
+
+	/* Assert CML Divider ratio to 26 */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
+
+	/* Asserting PLL Reset */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x582ec000);
+
+	/* Deaaserting PLL Reset */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x582e8000);
+
+	/* Waiting MII Mgt interface idle */
+	bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+	/* Deasserting USB3 system reset */
+	bcma_awrite32(usb3->core, BCMA_RESET_CTL, 0);
+
+	/* PLL frequency monitor enable */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x58069000);
+
+	/* PIPE Block */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8060);
+
+	/* CMPMAX & CMPMINTH setting */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x580af30d);
+
+	/* DEGLITCH MIN & MAX setting */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x580e6302);
+
+	/* TXPMD block */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
+
+	/* Enabling SSC */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
+
+	/* Waiting MII Mgt interface idle */
+	bcm_ns_usb3_mii_mng_wait_idle(usb3);
+}
+
+static void bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3)
+{
+	struct bcma_drv_cc_b *ccb = &usb3->bus->drv_cc_b;
+
+	/* Enable MDIO. Setting MDCDIV as 26  */
+	iowrite32(0x0000009a, ccb->mii + BCMA_CCB_MII_MNG_CTL);
+	udelay(2);
+
+	/* PLL30 block */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
+
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
+
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x587e80e0);
+
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x580a009c);
+
+	/* Enable SSC */
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
+
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x580a21d3);
+
+	bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
+
+	/* Waiting MII Mgt interface idle */
+	bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+	/* Deasserting USB3 system reset */
+	bcma_awrite32(usb3->core, BCMA_RESET_CTL, 0);
+}
+
+static int bcm_ns_usb3_phy_init(struct usb_phy *phy)
+{
+	struct bcm_ns_usb3 *usb3 = phy_to_usb3(phy);
+	struct bcma_chipinfo *chipinfo = &usb3->bus->chipinfo;
+
+	/* Perform USB3 system soft reset */
+	bcma_awrite32(usb3->core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
+
+	if (chipinfo->id == BCMA_CHIP_ID_BCM53018 ||
+	    (chipinfo->id == BCMA_CHIP_ID_BCM4707 && (chipinfo->rev == 4 || chipinfo->rev == 6)) ||
+	    chipinfo->id == BCMA_CHIP_ID_BCM47094) {
+		bcm_ns_usb3_phy_init_ns_bx(usb3);
+	} else if (chipinfo->id == BCMA_CHIP_ID_BCM4707) {
+		bcm_ns_usb3_phy_init_ns_ax(usb3);
+	} else {
+		WARN_ON(1);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static struct bcma_bus *bcm_ns_usb3_get_bus(struct platform_device *pdev)
+{
+	struct device_node *node;
+	struct platform_device *bus_pdev;
+
+	node = of_parse_phandle(pdev->dev.of_node, "bus", 0);
+	if (!node)
+		return NULL;
+
+	bus_pdev = of_find_device_by_node(node);
+	if (!bus_pdev)
+		return NULL;
+
+	return platform_get_drvdata(bus_pdev);
+}
+
+static int bcm_ns_usb3_probe(struct platform_device *pdev)
+{
+	struct bcm_ns_usb3 *usb3;
+	struct bcma_bus *bus;
+	int err;
+
+	bus = bcm_ns_usb3_get_bus(pdev);
+	if (!bus)
+		return -EPROBE_DEFER;
+
+	usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
+	if (!usb3)
+		return -ENOMEM;
+
+	usb3->dev		= &pdev->dev;
+	usb3->bus		= bus;
+	usb3->phy.dev		= usb3->dev;
+	usb3->phy.label		= "bcm_ns_usb3";
+	usb3->phy.init		= bcm_ns_usb3_phy_init;
+
+	usb3->core = bcma_find_core(usb3->bus, BCMA_CORE_NS_USB30);
+	if (!usb3->core)
+		return -ENODEV;
+
+	err = usb_add_phy(&usb3->phy, USB_PHY_TYPE_USB3);
+	if (err) {
+		dev_err(usb3->dev, "Failed to add PHY: %d\n", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, usb3);
+
+	dev_info(usb3->dev, "Registered driver for Broadcom Northstar USB PHY for bcma chip with id %d\n",
+		 usb3->bus->chipinfo.id);
+
+	return 0;
+}
+
+static int bcm_ns_usb3_remove(struct platform_device *pdev)
+{
+	struct bcm_ns_usb3 *usb3 = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&usb3->phy);
+
+	return 0;
+}
+
+static const struct of_device_id bcm_ns_usb3_id_table[] = {
+	{ .compatible = "brcm,ns-usb3-phy", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table);
+
+static struct platform_driver bcm_ns_usb3_driver = {
+	.probe		= bcm_ns_usb3_probe,
+	.remove		= bcm_ns_usb3_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "bcm_ns_usb3",
+		.of_match_table = bcm_ns_usb3_id_table,
+	},
+};
+module_platform_driver(bcm_ns_usb3_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 846513c..3a86e48 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -504,6 +504,9 @@
 #define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_MASK	0x1ff00000
 #define BCMA_CC_PMU1_PLL0_PC2_NDIV_INT_SHIFT	20
 
+#define BCMA_CCB_MII_MNG_CTL		0x0000
+#define BCMA_CCB_MII_MNG_CMD_DATA	0x0004
+
 /* BCM4331 ChipControl numbers. */
 #define BCMA_CHIPCTL_4331_BT_COEXIST		BIT(0)	/* 0 disable */
 #define BCMA_CHIPCTL_4331_SECI			BIT(1)	/* 0 SECI is disabled (JATG functional) */
-- 
1.8.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux