CC: bcm-kernel-feedback-list, Jon Le 28/03/2016 14:59, Rafał Miłecki a écrit : > 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; We are mixing timeout in micro seconds with jiffies here, should that be msecs_to_jiffies(timeout) instead? > + u32 val; > + > + do { > + val = readl(addr); > + if ((val & mask) == value) > + return true; > + cpu_relax(); > + udelay(10); Delay + relax, why not a msleep() instead? > + } 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); We might want to make the first write check whether the MII bus was idle or not, just in case? > + > + /* 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); We could propagate an error back to this function in case any of these two fails because the bus is not idle? -- Florian -- 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