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 <[email protected]>
---
 .../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 <[email protected]>
+ *
+ * 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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to