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