diff --git a/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt index 7f81ef90146a..d87ab7c127b8 100644 --- a/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt +++ b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt @@ -2,6 +2,7 @@ Required properties: - compatible: should be one or more of + "brcm,bcm7425-sata-phy" "brcm,bcm7445-sata-phy" "brcm,phy-sata3" - address-cells: should be 1 diff --git a/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt b/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt new file mode 100644 index 000000000000..f17a56e2152f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt @@ -0,0 +1,16 @@ +Hisilicon hi6220 usb PHY +----------------------- + +Required properties: +- compatible: should be "hisilicon,hi6220-usb-phy" +- #phy-cells: must be 0 +- hisilicon,peripheral-syscon: phandle of syscon used to control phy. +Refer to phy/phy-bindings.txt for the generic PHY binding properties + +Example: + usb_phy: usbphy { + compatible = "hisilicon,hi6220-usb-phy"; + #phy-cells = <0>; + phy-supply = <&fixed_5v_hub>; + hisilicon,peripheral-syscon = <&sys_ctrl>; + }; diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt new file mode 100644 index 000000000000..2390e4e9c84c --- /dev/null +++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt @@ -0,0 +1,39 @@ +* Renesas R-Car generation 3 USB 2.0 PHY + +This file provides information on what the device node for the R-Car generation +3 USB 2.0 PHY contains. + +Required properties: +- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795 + SoC. +- reg: offset and length of the partial USB 2.0 Host register block. +- reg-names: must be "usb2_host". +- clocks: clock phandle and specifier pair(s). +- #phy-cells: see phy-bindings.txt in the same directory, must be <0>. + +Optional properties: +To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are +combined, the device tree node should set HSUSB properties to reg and reg-names +properties. This is because HSUSB has registers to select USB 2.0 host or +peripheral at that channel: +- reg: offset and length of the partial HSUSB register block. +- reg-names: must be "hsusb". +- interrupts: interrupt specifier for the PHY. + +Example (R-Car H3): + + usb-phy@ee080200 { + compatible = "renesas,usb2-phy-r8a7795"; + reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>; + reg-names = "usb2_host", "hsusb"; + interrupts = ; + clocks = <&mstp7_clks R8A7795_CLK_EHCI0>, + <&mstp7_clks R8A7795_CLK_HSUSB>; + }; + + usb-phy@ee0a0200 { + compatible = "renesas,usb2-phy-r8a7795"; + reg = <0 0xee0a0200 0 0x700>; + reg-names = "usb2_host"; + clocks = <&mstp7_clks R8A7795_CLK_EHCI0>; + }; diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt index 826454ac43bb..68498d560354 100644 --- a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt +++ b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt @@ -1,7 +1,10 @@ ROCKCHIP USB2 PHY Required properties: - - compatible: rockchip,rk3288-usb-phy + - compatible: matching the soc type, one of + "rockchip,rk3066a-usb-phy" + "rockchip,rk3188-usb-phy" + "rockchip,rk3288-usb-phy" - rockchip,grf : phandle to the syscon managing the "general register files" - #address-cells: should be 1 @@ -21,6 +24,7 @@ required properties: Optional Properties: - clocks : phandle + clock specifier for the phy clocks - clock-names: string, clock name, must be "phyclk" +- #clock-cells: for users of the phy-pll, should be 0 Example: diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt index 0cebf7454517..95736d77fbb7 100644 --- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt +++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt @@ -9,6 +9,7 @@ Required properties: * allwinner,sun7i-a20-usb-phy * allwinner,sun8i-a23-usb-phy * allwinner,sun8i-a33-usb-phy + * allwinner,sun8i-h3-usb-phy - reg : a list of offset + length pairs - reg-names : * "phy_ctrl" diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt index 9cf9446eaf2e..a3b394587874 100644 --- a/Documentation/devicetree/bindings/phy/ti-phy.txt +++ b/Documentation/devicetree/bindings/phy/ti-phy.txt @@ -31,6 +31,8 @@ OMAP USB2 PHY Required properties: - compatible: Should be "ti,omap-usb2" + Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY + in DRA7x - reg : Address and length of the register set for the device. - #phy-cells: determine the number of cells that should be given in the phandle while referencing this phy. @@ -40,10 +42,14 @@ Required properties: * "wkupclk" - wakeup clock. * "refclk" - reference clock (optional). -Optional properties: +Deprecated properties: - ctrl-module : phandle of the control module used by PHY driver to power on the PHY. +Recommended properies: +- syscon-phy-power : phandle/offset pair. Phandle to the system control + module and the register offset to power on/off the PHY. + This is usually a subnode of ocp2scp to which it is connected. usb2phy@4a0ad080 { @@ -77,14 +83,22 @@ Required properties: * "div-clk" - apll clock Optional properties: - - ctrl-module : phandle of the control module used by PHY driver to power on - the PHY. - id: If there are multiple instance of the same type, in order to differentiate between each instance "id" can be used (e.g., multi-lane PCIe PHY). If "id" is not provided, it is set to default value of '1'. - syscon-pllreset: Handle to system control region that contains the CTRL_CORE_SMA_SW_0 register and register offset to the CTRL_CORE_SMA_SW_0 register that contains the SATA_PLL_SOFT_RESET bit. Only valid for sata_phy. + - syscon-pcs : phandle/offset pair. Phandle to the system control module and the + register offset to write the PCS delay value. + +Deprecated properties: + - ctrl-module : phandle of the control module used by PHY driver to power on + the PHY. + +Recommended properies: + - syscon-phy-power : phandle/offset pair. Phandle to the system control + module and the register offset to power on/off the PHY. This is usually a subnode of ocp2scp to which it is connected. diff --git a/MAINTAINERS b/MAINTAINERS index 233f83464814..f18cd5e41221 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8961,6 +8961,12 @@ L: linux-sh@vger.kernel.org F: drivers/net/ethernet/renesas/ F: include/linux/sh_eth.h +RENESAS USB2 PHY DRIVER +M: Yoshihiro Shimoda +L: linux-sh@vger.kernel.org +S: Maintained +F: drivers/phy/phy-rcar-gen3-usb2.c + RESET CONTROLLER FRAMEWORK M: Philipp Zabel S: Maintained diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 03cb3ea2d2c0..e7e117d5dbbe 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -118,6 +118,13 @@ config PHY_RCAR_GEN2 help Support for USB PHY found on Renesas R-Car generation 2 SoCs. +config PHY_RCAR_GEN3_USB2 + tristate "Renesas R-Car generation 3 USB 2.0 PHY driver" + depends on OF && ARCH_SHMOBILE + select GENERIC_PHY + help + Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs. + config OMAP_CONTROL_PHY tristate "OMAP CONTROL PHY Driver" depends on ARCH_OMAP2PLUS || COMPILE_TEST @@ -215,6 +222,15 @@ config PHY_MT65XX_USB3 for mt65xx SoCs. it supports two usb2.0 ports and one usb3.0 port. +config PHY_HI6220_USB + tristate "hi6220 USB PHY support" + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the HISILICON HI6220 USB PHY. + + To compile this driver as a module, choose M here. + config PHY_SUN4I_USB tristate "Allwinner sunxi SoC USB PHY driver" depends on ARCH_SUNXI && HAS_IOMEM && OF @@ -374,11 +390,11 @@ config PHY_TUSB1210 config PHY_BRCMSTB_SATA tristate "Broadcom STB SATA PHY driver" - depends on ARCH_BRCMSTB + depends on ARCH_BRCMSTB || BMIPS_GENERIC depends on OF select GENERIC_PHY help - Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs. + Enable this to support the SATA3 PHY on 28nm or 40nm Broadcom STB SoCs. Likely useful only with CONFIG_SATA_BRCMSTB enabled. config PHY_CYGNUS_PCIE diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 075db1a81aa5..c80f09df3bb8 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -17,12 +17,14 @@ obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o +obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o +obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c index 797ba17c404f..2017751ede26 100644 --- a/drivers/phy/phy-berlin-usb.c +++ b/drivers/phy/phy-berlin-usb.c @@ -9,11 +9,9 @@ * warranty of any kind, whether express or implied. */ -#include #include #include #include -#include #include #include #include @@ -195,7 +193,6 @@ static int phy_berlin_usb_probe(struct platform_device *pdev) return PTR_ERR(phy); } - platform_set_drvdata(pdev, priv); phy_set_drvdata(phy, priv); phy_provider = diff --git a/drivers/phy/phy-brcmstb-sata.c b/drivers/phy/phy-brcmstb-sata.c index cd9dba820566..a23172ff40e3 100644 --- a/drivers/phy/phy-brcmstb-sata.c +++ b/drivers/phy/phy-brcmstb-sata.c @@ -26,13 +26,21 @@ #define SATA_MDIO_BANK_OFFSET 0x23c #define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4) -#define SATA_MDIO_REG_SPACE_SIZE 0x1000 -#define SATA_MDIO_REG_LENGTH 0x1f00 #define MAX_PORTS 2 /* Register offset between PHYs in PCB space */ -#define SATA_MDIO_REG_SPACE_SIZE 0x1000 +#define SATA_MDIO_REG_28NM_SPACE_SIZE 0x1000 + +/* The older SATA PHY registers duplicated per port registers within the map, + * rather than having a separate map per port. + */ +#define SATA_MDIO_REG_40NM_SPACE_SIZE 0x10 + +enum brcm_sata_phy_version { + BRCM_SATA_PHY_28NM, + BRCM_SATA_PHY_40NM, +}; struct brcm_sata_port { int portnum; @@ -44,11 +52,12 @@ struct brcm_sata_port { struct brcm_sata_phy { struct device *dev; void __iomem *phy_base; + enum brcm_sata_phy_version version; struct brcm_sata_port phys[MAX_PORTS]; }; -enum sata_mdio_phy_regs_28nm { +enum sata_mdio_phy_regs { PLL_REG_BANK_0 = 0x50, PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, @@ -66,8 +75,16 @@ enum sata_mdio_phy_regs_28nm { static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port) { struct brcm_sata_phy *priv = port->phy_priv; + u32 offset = 0; - return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE); + if (priv->version == BRCM_SATA_PHY_28NM) + offset = SATA_MDIO_REG_28NM_SPACE_SIZE; + else if (priv->version == BRCM_SATA_PHY_40NM) + offset = SATA_MDIO_REG_40NM_SPACE_SIZE; + else + dev_err(priv->dev, "invalid phy version\n"); + + return priv->phy_base + (port->portnum * offset); } static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs, @@ -86,7 +103,7 @@ static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs, #define FMAX_VAL_DEFAULT 0x3df #define FMAX_VAL_SSC 0x83 -static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port) +static void brcm_sata_cfg_ssc(struct brcm_sata_port *port) { void __iomem *base = brcm_sata_phy_base(port); struct brcm_sata_phy *priv = port->phy_priv; @@ -117,18 +134,21 @@ static int brcm_sata_phy_init(struct phy *phy) { struct brcm_sata_port *port = phy_get_drvdata(phy); - brcm_sata_cfg_ssc_28nm(port); + brcm_sata_cfg_ssc(port); return 0; } -static const struct phy_ops phy_ops_28nm = { +static const struct phy_ops phy_ops = { .init = brcm_sata_phy_init, .owner = THIS_MODULE, }; static const struct of_device_id brcm_sata_phy_of_match[] = { - { .compatible = "brcm,bcm7445-sata-phy" }, + { .compatible = "brcm,bcm7445-sata-phy", + .data = (void *)BRCM_SATA_PHY_28NM }, + { .compatible = "brcm,bcm7425-sata-phy", + .data = (void *)BRCM_SATA_PHY_40NM }, {}, }; MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); @@ -137,6 +157,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node, *child; + const struct of_device_id *of_id; struct brcm_sata_phy *priv; struct resource *res; struct phy_provider *provider; @@ -156,6 +177,12 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(priv->phy_base)) return PTR_ERR(priv->phy_base); + of_id = of_match_node(brcm_sata_phy_of_match, dn); + if (of_id) + priv->version = (enum brcm_sata_phy_version)of_id->data; + else + priv->version = BRCM_SATA_PHY_28NM; + for_each_available_child_of_node(dn, child) { unsigned int id; struct brcm_sata_port *port; @@ -181,7 +208,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) port = &priv->phys[id]; port->portnum = id; port->phy_priv = priv; - port->phy = devm_phy_create(dev, child, &phy_ops_28nm); + port->phy = devm_phy_create(dev, child, &phy_ops); port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); if (IS_ERR(port->phy)) { dev_err(dev, "failed to create PHY\n"); diff --git a/drivers/phy/phy-hi6220-usb.c b/drivers/phy/phy-hi6220-usb.c new file mode 100644 index 000000000000..b2141cbd4cf6 --- /dev/null +++ b/drivers/phy/phy-hi6220-usb.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015 Linaro Ltd. + * Copyright (c) 2015 Hisilicon Limited. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#define SC_PERIPH_CTRL4 0x00c + +#define CTRL4_PICO_SIDDQ BIT(6) +#define CTRL4_PICO_OGDISABLE BIT(8) +#define CTRL4_PICO_VBUSVLDEXT BIT(10) +#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11) +#define CTRL4_OTG_PHY_SEL BIT(21) + +#define SC_PERIPH_CTRL5 0x010 + +#define CTRL5_USBOTG_RES_SEL BIT(3) +#define CTRL5_PICOPHY_ACAENB BIT(4) +#define CTRL5_PICOPHY_BC_MODE BIT(5) +#define CTRL5_PICOPHY_CHRGSEL BIT(6) +#define CTRL5_PICOPHY_VDATSRCEND BIT(7) +#define CTRL5_PICOPHY_VDATDETENB BIT(8) +#define CTRL5_PICOPHY_DCDENB BIT(9) +#define CTRL5_PICOPHY_IDDIG BIT(10) + +#define SC_PERIPH_CTRL8 0x018 +#define SC_PERIPH_RSTEN0 0x300 +#define SC_PERIPH_RSTDIS0 0x304 + +#define RST0_USBOTG_BUS BIT(4) +#define RST0_POR_PICOPHY BIT(5) +#define RST0_USBOTG BIT(6) +#define RST0_USBOTG_32K BIT(7) + +#define EYE_PATTERN_PARA 0x7053348c + +struct hi6220_priv { + struct regmap *reg; + struct device *dev; +}; + +static void hi6220_phy_init(struct hi6220_priv *priv) +{ + struct regmap *reg = priv->reg; + u32 val, mask; + + val = RST0_USBOTG_BUS | RST0_POR_PICOPHY | + RST0_USBOTG | RST0_USBOTG_32K; + mask = val; + regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val); + regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val); +} + +static int hi6220_phy_setup(struct hi6220_priv *priv, bool on) +{ + struct regmap *reg = priv->reg; + u32 val, mask; + int ret; + + if (on) { + val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB; + mask = val | CTRL5_PICOPHY_BC_MODE; + ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val); + if (ret) + goto out; + + val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL | + CTRL4_OTG_PHY_SEL; + mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE; + ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); + if (ret) + goto out; + + ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA); + if (ret) + goto out; + } else { + val = CTRL4_PICO_SIDDQ; + mask = val; + ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); + if (ret) + goto out; + } + + return 0; +out: + dev_err(priv->dev, "failed to setup phy ret: %d\n", ret); + return ret; +} + +static int hi6220_phy_start(struct phy *phy) +{ + struct hi6220_priv *priv = phy_get_drvdata(phy); + + return hi6220_phy_setup(priv, true); +} + +static int hi6220_phy_exit(struct phy *phy) +{ + struct hi6220_priv *priv = phy_get_drvdata(phy); + + return hi6220_phy_setup(priv, false); +} + +static struct phy_ops hi6220_phy_ops = { + .init = hi6220_phy_start, + .exit = hi6220_phy_exit, + .owner = THIS_MODULE, +}; + +static int hi6220_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct phy *phy; + struct hi6220_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,peripheral-syscon"); + if (IS_ERR(priv->reg)) { + dev_err(dev, "no hisilicon,peripheral-syscon\n"); + return PTR_ERR(priv->reg); + } + + hi6220_phy_init(priv); + + phy = devm_phy_create(dev, NULL, &hi6220_phy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id hi6220_phy_of_match[] = { + {.compatible = "hisilicon,hi6220-usb-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, hi6220_phy_of_match); + +static struct platform_driver hi6220_phy_driver = { + .probe = hi6220_phy_probe, + .driver = { + .name = "hi6220-usb-phy", + .of_match_table = hi6220_phy_of_match, + } +}; +module_platform_driver(hi6220_phy_driver); + +MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver"); +MODULE_ALIAS("platform:hi6220-usb-phy"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-mt65xx-usb3.c b/drivers/phy/phy-mt65xx-usb3.c index e427c3b788ff..c0e7b4b0cf5c 100644 --- a/drivers/phy/phy-mt65xx-usb3.c +++ b/drivers/phy/phy-mt65xx-usb3.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ * relative to USB3_SIF2_BASE base address */ #define SSUSB_SIFSLV_SPLLC 0x0000 +#define SSUSB_SIFSLV_U2FREQ 0x0100 /* offsets of sub-segment in each port registers */ #define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000 @@ -41,6 +43,7 @@ #define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18) #define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014) +#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15) #define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12) #define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) #define PA5_RG_U2_HS_100U_U3_EN BIT(11) @@ -49,6 +52,8 @@ #define PA6_RG_U2_ISO_EN BIT(31) #define PA6_RG_U2_BC11_SW_EN BIT(23) #define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20) +#define PA6_RG_U2_SQTH GENMASK(3, 0) +#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x)) #define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020) #define P2C_RG_USB20_GPIO_CTL BIT(9) @@ -111,6 +116,24 @@ #define XC3_RG_U3_XTAL_RX_PWD BIT(9) #define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8) +#define U3P_U2FREQ_FMCR0 (SSUSB_SIFSLV_U2FREQ + 0x00) +#define P2F_RG_MONCLK_SEL GENMASK(27, 26) +#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26) +#define P2F_RG_FREQDET_EN BIT(24) +#define P2F_RG_CYCLECNT GENMASK(23, 0) +#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x)) + +#define U3P_U2FREQ_VALUE (SSUSB_SIFSLV_U2FREQ + 0x0c) + +#define U3P_U2FREQ_FMMONR1 (SSUSB_SIFSLV_U2FREQ + 0x10) +#define P2F_USB_FM_VALID BIT(0) +#define P2F_RG_FRCK_EN BIT(8) + +#define U3P_REF_CLK 26 /* MHZ */ +#define U3P_SLEW_RATE_COEF 28 +#define U3P_SR_COEF_DIVISOR 1000 +#define U3P_FM_DET_CYCLE_CNT 1024 + struct mt65xx_phy_instance { struct phy *phy; void __iomem *port_base; @@ -126,6 +149,77 @@ struct mt65xx_u3phy { int nphys; }; +static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy, + struct mt65xx_phy_instance *instance) +{ + void __iomem *sif_base = u3phy->sif_base; + int calibration_val; + int fm_out; + u32 tmp; + + /* enable USB ring oscillator */ + tmp = readl(instance->port_base + U3P_USBPHYACR5); + tmp |= PA5_RG_U2_HSTX_SRCAL_EN; + writel(tmp, instance->port_base + U3P_USBPHYACR5); + udelay(1); + + /*enable free run clock */ + tmp = readl(sif_base + U3P_U2FREQ_FMMONR1); + tmp |= P2F_RG_FRCK_EN; + writel(tmp, sif_base + U3P_U2FREQ_FMMONR1); + + /* set cycle count as 1024, and select u2 channel */ + tmp = readl(sif_base + U3P_U2FREQ_FMCR0); + tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL); + tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT); + tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index); + writel(tmp, sif_base + U3P_U2FREQ_FMCR0); + + /* enable frequency meter */ + tmp = readl(sif_base + U3P_U2FREQ_FMCR0); + tmp |= P2F_RG_FREQDET_EN; + writel(tmp, sif_base + U3P_U2FREQ_FMCR0); + + /* ignore return value */ + readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp, + (tmp & P2F_USB_FM_VALID), 10, 200); + + fm_out = readl(sif_base + U3P_U2FREQ_VALUE); + + /* disable frequency meter */ + tmp = readl(sif_base + U3P_U2FREQ_FMCR0); + tmp &= ~P2F_RG_FREQDET_EN; + writel(tmp, sif_base + U3P_U2FREQ_FMCR0); + + /*disable free run clock */ + tmp = readl(sif_base + U3P_U2FREQ_FMMONR1); + tmp &= ~P2F_RG_FRCK_EN; + writel(tmp, sif_base + U3P_U2FREQ_FMMONR1); + + if (fm_out) { + /* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */ + tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF; + tmp /= fm_out; + calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR); + } else { + /* if FM detection fail, set default value */ + calibration_val = 4; + } + dev_dbg(u3phy->dev, "phy:%d, fm_out:%d, calib:%d\n", + instance->index, fm_out, calibration_val); + + /* set HS slew rate */ + tmp = readl(instance->port_base + U3P_USBPHYACR5); + tmp &= ~PA5_RG_U2_HSTX_SRCTRL; + tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val); + writel(tmp, instance->port_base + U3P_USBPHYACR5); + + /* disable USB ring oscillator */ + tmp = readl(instance->port_base + U3P_USBPHYACR5); + tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN; + writel(tmp, instance->port_base + U3P_USBPHYACR5); +} + static void phy_instance_init(struct mt65xx_u3phy *u3phy, struct mt65xx_phy_instance *instance) { @@ -165,9 +259,10 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy, writel(tmp, port_base + U3P_U2PHYDTM0); } - /* DP/DM BC1.1 path Disable */ tmp = readl(port_base + U3P_USBPHYACR6); - tmp &= ~PA6_RG_U2_BC11_SW_EN; + tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */ + tmp &= ~PA6_RG_U2_SQTH; + tmp |= PA6_RG_U2_SQTH_VAL(2); writel(tmp, port_base + U3P_USBPHYACR6); tmp = readl(port_base + U3P_U3PHYA_DA_REG0); @@ -223,9 +318,9 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy, tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD; writel(tmp, u3phy->sif_base + U3P_XTALCTL3); - /* [mt8173]disable Change 100uA current from SSUSB */ + /* [mt8173]switch 100uA current to SSUSB */ tmp = readl(port_base + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HS_100U_U3_EN; + tmp |= PA5_RG_U2_HS_100U_U3_EN; writel(tmp, port_base + U3P_USBPHYACR5); } @@ -270,7 +365,7 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy, writel(tmp, port_base + U3P_USBPHYACR6); if (!index) { - /* (also disable)Change 100uA current switch to USB2.0 */ + /* switch 100uA current back to USB2.0 */ tmp = readl(port_base + U3P_USBPHYACR5); tmp &= ~PA5_RG_U2_HS_100U_U3_EN; writel(tmp, port_base + U3P_USBPHYACR5); @@ -340,6 +435,7 @@ static int mt65xx_phy_power_on(struct phy *phy) struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent); phy_instance_power_on(u3phy, instance); + hs_slew_rate_calibrate(u3phy, instance); return 0; } diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index 0fe80589ffbe..c134989052f5 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #define USB2PHY_DISCON_BYP_LATCH (1 << 31) @@ -97,22 +99,38 @@ static int omap_usb_set_peripheral(struct usb_otg *otg, return 0; } +static int omap_usb_phy_power(struct omap_usb *phy, int on) +{ + u32 val; + int ret; + + if (!phy->syscon_phy_power) { + omap_control_phy_power(phy->control_dev, on); + return 0; + } + + if (on) + val = phy->power_on; + else + val = phy->power_off; + + ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg, + phy->mask, val); + return ret; +} + static int omap_usb_power_off(struct phy *x) { struct omap_usb *phy = phy_get_drvdata(x); - omap_control_phy_power(phy->control_dev, 0); - - return 0; + return omap_usb_phy_power(phy, false); } static int omap_usb_power_on(struct phy *x) { struct omap_usb *phy = phy_get_drvdata(x); - omap_control_phy_power(phy->control_dev, 1); - - return 0; + return omap_usb_phy_power(phy, true); } static int omap_usb_init(struct phy *x) @@ -147,21 +165,38 @@ static const struct phy_ops ops = { static const struct usb_phy_data omap_usb2_data = { .label = "omap_usb2", .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS, + .mask = OMAP_DEV_PHY_PD, + .power_off = OMAP_DEV_PHY_PD, }; static const struct usb_phy_data omap5_usb2_data = { .label = "omap5_usb2", .flags = 0, + .mask = OMAP_DEV_PHY_PD, + .power_off = OMAP_DEV_PHY_PD, }; static const struct usb_phy_data dra7x_usb2_data = { .label = "dra7x_usb2", .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, + .mask = OMAP_DEV_PHY_PD, + .power_off = OMAP_DEV_PHY_PD, +}; + +static const struct usb_phy_data dra7x_usb2_phy2_data = { + .label = "dra7x_usb2_phy2", + .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, + .mask = OMAP_USB2_PHY_PD, + .power_off = OMAP_USB2_PHY_PD, }; static const struct usb_phy_data am437x_usb2_data = { .label = "am437x_usb2", .flags = 0, + .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD | + AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, + .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, + .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, }; static const struct of_device_id omap_usb2_id_table[] = { @@ -177,6 +212,10 @@ static const struct of_device_id omap_usb2_id_table[] = { .compatible = "ti,dra7x-usb2", .data = &dra7x_usb2_data, }, + { + .compatible = "ti,dra7x-usb2-phy2", + .data = &dra7x_usb2_phy2_data, + }, { .compatible = "ti,am437x-usb2", .data = &am437x_usb2_data, @@ -219,6 +258,9 @@ static int omap_usb2_probe(struct platform_device *pdev) phy->phy.label = phy_data->label; phy->phy.otg = otg; phy->phy.type = USB_PHY_TYPE_USB2; + phy->mask = phy_data->mask; + phy->power_on = phy_data->power_on; + phy->power_off = phy_data->power_off; if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -228,20 +270,35 @@ static int omap_usb2_probe(struct platform_device *pdev) phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT; } - control_node = of_parse_phandle(node, "ctrl-module", 0); - if (!control_node) { - dev_err(&pdev->dev, "Failed to get control device phandle\n"); - return -EINVAL; - } + phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, + "syscon-phy-power"); + if (IS_ERR(phy->syscon_phy_power)) { + dev_dbg(&pdev->dev, + "can't get syscon-phy-power, using control device\n"); + phy->syscon_phy_power = NULL; - control_pdev = of_find_device_by_node(control_node); - if (!control_pdev) { - dev_err(&pdev->dev, "Failed to get control device\n"); - return -EINVAL; - } + control_node = of_parse_phandle(node, "ctrl-module", 0); + if (!control_node) { + dev_err(&pdev->dev, + "Failed to get control device phandle\n"); + return -EINVAL; + } - phy->control_dev = &control_pdev->dev; - omap_control_phy_power(phy->control_dev, 0); + control_pdev = of_find_device_by_node(control_node); + if (!control_pdev) { + dev_err(&pdev->dev, "Failed to get control device\n"); + return -EINVAL; + } + phy->control_dev = &control_pdev->dev; + } else { + if (of_property_read_u32_index(node, + "syscon-phy-power", 1, + &phy->power_reg)) { + dev_err(&pdev->dev, + "couldn't get power reg. offset\n"); + return -EINVAL; + } + } otg->set_host = omap_usb_set_host; otg->set_peripheral = omap_usb_set_peripheral; @@ -261,6 +318,7 @@ static int omap_usb2_probe(struct platform_device *pdev) } phy_set_drvdata(generic_phy, phy); + omap_usb_power_off(generic_phy); phy_provider = devm_of_phy_provider_register(phy->dev, of_phy_simple_xlate); diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c new file mode 100644 index 000000000000..ef332ef4abc7 --- /dev/null +++ b/drivers/phy/phy-rcar-gen3-usb2.c @@ -0,0 +1,378 @@ +/* + * Renesas R-Car Gen3 for USB2.0 PHY driver + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * This is based on the phy-rcar-gen2 driver: + * Copyright (C) 2014 Renesas Solutions Corp. + * Copyright (C) 2014 Cogent Embedded, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include + +/******* USB2.0 Host registers (original offset is +0x200) *******/ +#define USB2_INT_ENABLE 0x000 +#define USB2_USBCTR 0x00c +#define USB2_SPD_RSM_TIMSET 0x10c +#define USB2_OC_TIMSET 0x110 +#define USB2_COMMCTRL 0x600 +#define USB2_OBINTSTA 0x604 +#define USB2_OBINTEN 0x608 +#define USB2_VBCTRL 0x60c +#define USB2_LINECTRL1 0x610 +#define USB2_ADPCTRL 0x630 + +/* INT_ENABLE */ +#define USB2_INT_ENABLE_UCOM_INTEN BIT(3) +#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) +#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) +#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \ + USB2_INT_ENABLE_USBH_INTB_EN | \ + USB2_INT_ENABLE_USBH_INTA_EN) + +/* USBCTR */ +#define USB2_USBCTR_DIRPD BIT(2) +#define USB2_USBCTR_PLL_RST BIT(1) + +/* SPD_RSM_TIMSET */ +#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b + +/* OC_TIMSET */ +#define USB2_OC_TIMSET_INIT 0x000209ab + +/* COMMCTRL */ +#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ + +/* OBINTSTA and OBINTEN */ +#define USB2_OBINT_SESSVLDCHG BIT(12) +#define USB2_OBINT_IDDIGCHG BIT(11) +#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ + USB2_OBINT_IDDIGCHG) + +/* VBCTRL */ +#define USB2_VBCTRL_DRVVBUSSEL BIT(8) + +/* LINECTRL1 */ +#define USB2_LINECTRL1_DPRPD_EN BIT(19) +#define USB2_LINECTRL1_DP_RPD BIT(18) +#define USB2_LINECTRL1_DMRPD_EN BIT(17) +#define USB2_LINECTRL1_DM_RPD BIT(16) + +/* ADPCTRL */ +#define USB2_ADPCTRL_OTGSESSVLD BIT(20) +#define USB2_ADPCTRL_IDDIG BIT(19) +#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ +#define USB2_ADPCTRL_DRVVBUS BIT(4) + +/******* HSUSB registers (original offset is +0x100) *******/ +#define HSUSB_LPSTS 0x02 +#define HSUSB_UGCTRL2 0x84 + +/* Low Power Status register (LPSTS) */ +#define HSUSB_LPSTS_SUSPM 0x4000 + +/* USB General control register 2 (UGCTRL2) */ +#define HSUSB_UGCTRL2_MASK 0x00000031 /* bit[31:6] should be 0 */ +#define HSUSB_UGCTRL2_USB0SEL 0x00000030 +#define HSUSB_UGCTRL2_USB0SEL_HOST 0x00000010 +#define HSUSB_UGCTRL2_USB0SEL_HS_USB 0x00000020 +#define HSUSB_UGCTRL2_USB0SEL_OTG 0x00000030 + +struct rcar_gen3_data { + void __iomem *base; + struct clk *clk; +}; + +struct rcar_gen3_chan { + struct rcar_gen3_data usb2; + struct rcar_gen3_data hsusb; + struct phy *phy; +}; + +static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) +{ + void __iomem *usb2_base = ch->usb2.base; + u32 val = readl(usb2_base + USB2_COMMCTRL); + + dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host); + if (host) + val &= ~USB2_COMMCTRL_OTG_PERI; + else + val |= USB2_COMMCTRL_OTG_PERI; + writel(val, usb2_base + USB2_COMMCTRL); +} + +static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) +{ + void __iomem *usb2_base = ch->usb2.base; + u32 val = readl(usb2_base + USB2_LINECTRL1); + + dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); + val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); + if (dp) + val |= USB2_LINECTRL1_DP_RPD; + if (dm) + val |= USB2_LINECTRL1_DM_RPD; + writel(val, usb2_base + USB2_LINECTRL1); +} + +static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) +{ + void __iomem *usb2_base = ch->usb2.base; + u32 val = readl(usb2_base + USB2_ADPCTRL); + + dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus); + if (vbus) + val |= USB2_ADPCTRL_DRVVBUS; + else + val &= ~USB2_ADPCTRL_DRVVBUS; + writel(val, usb2_base + USB2_ADPCTRL); +} + +static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) +{ + rcar_gen3_set_linectrl(ch, 1, 1); + rcar_gen3_set_host_mode(ch, 1); + rcar_gen3_enable_vbus_ctrl(ch, 1); +} + +static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) +{ + rcar_gen3_set_linectrl(ch, 0, 1); + rcar_gen3_set_host_mode(ch, 0); + rcar_gen3_enable_vbus_ctrl(ch, 0); +} + +static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch) +{ + return !!(readl(ch->usb2.base + USB2_ADPCTRL) & + USB2_ADPCTRL_OTGSESSVLD); +} + +static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) +{ + return !!(readl(ch->usb2.base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); +} + +static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) +{ + bool is_host = true; + + /* B-device? */ + if (rcar_gen3_check_id(ch) && rcar_gen3_check_vbus(ch)) + is_host = false; + + if (is_host) + rcar_gen3_init_for_host(ch); + else + rcar_gen3_init_for_peri(ch); +} + +static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) +{ + void __iomem *usb2_base = ch->usb2.base; + u32 val; + + val = readl(usb2_base + USB2_VBCTRL); + writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); + val = readl(usb2_base + USB2_OBINTEN); + writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); + val = readl(usb2_base + USB2_ADPCTRL); + writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); + val = readl(usb2_base + USB2_LINECTRL1); + rcar_gen3_set_linectrl(ch, 0, 0); + writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN, + usb2_base + USB2_LINECTRL1); + + rcar_gen3_device_recognition(ch); +} + +static int rcar_gen3_phy_usb2_init(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + void __iomem *usb2_base = channel->usb2.base; + void __iomem *hsusb_base = channel->hsusb.base; + u32 val; + + /* Initialize USB2 part */ + writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); + writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); + writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); + + /* Initialize HSUSB part */ + if (hsusb_base) { + val = readl(hsusb_base + HSUSB_UGCTRL2); + val = (val & ~HSUSB_UGCTRL2_USB0SEL) | + HSUSB_UGCTRL2_USB0SEL_OTG; + writel(val & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2); + + /* Initialize otg part */ + rcar_gen3_init_otg(channel); + } + + return 0; +} + +static int rcar_gen3_phy_usb2_exit(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + + writel(0, channel->usb2.base + USB2_INT_ENABLE); + + return 0; +} + +static int rcar_gen3_phy_usb2_power_on(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + void __iomem *usb2_base = channel->usb2.base; + void __iomem *hsusb_base = channel->hsusb.base; + u32 val; + + val = readl(usb2_base + USB2_USBCTR); + val |= USB2_USBCTR_PLL_RST; + writel(val, usb2_base + USB2_USBCTR); + val &= ~USB2_USBCTR_PLL_RST; + writel(val, usb2_base + USB2_USBCTR); + + /* + * TODO: To reduce power consuming, this driver should set the SUSPM + * after the PHY detects ID pin as peripheral. + */ + if (hsusb_base) { + /* Power on HSUSB PHY */ + val = readw(hsusb_base + HSUSB_LPSTS); + val |= HSUSB_LPSTS_SUSPM; + writew(val, hsusb_base + HSUSB_LPSTS); + } + + return 0; +} + +static int rcar_gen3_phy_usb2_power_off(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + void __iomem *hsusb_base = channel->hsusb.base; + u32 val; + + if (hsusb_base) { + /* Power off HSUSB PHY */ + val = readw(hsusb_base + HSUSB_LPSTS); + val &= ~HSUSB_LPSTS_SUSPM; + writew(val, hsusb_base + HSUSB_LPSTS); + } + + return 0; +} + +static struct phy_ops rcar_gen3_phy_usb2_ops = { + .init = rcar_gen3_phy_usb2_init, + .exit = rcar_gen3_phy_usb2_exit, + .power_on = rcar_gen3_phy_usb2_power_on, + .power_off = rcar_gen3_phy_usb2_power_off, + .owner = THIS_MODULE, +}; + +static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) +{ + struct rcar_gen3_chan *ch = _ch; + void __iomem *usb2_base = ch->usb2.base; + u32 status = readl(usb2_base + USB2_OBINTSTA); + irqreturn_t ret = IRQ_NONE; + + if (status & USB2_OBINT_BITS) { + dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status); + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); + rcar_gen3_device_recognition(ch); + ret = IRQ_HANDLED; + } + + return ret; +} + +static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { + { .compatible = "renesas,usb2-phy-r8a7795" }, + { } +}; +MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); + +static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gen3_chan *channel; + struct phy_provider *provider; + struct resource *res; + + if (!dev->of_node) { + dev_err(dev, "This driver needs device tree\n"); + return -EINVAL; + } + + channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2_host"); + channel->usb2.base = devm_ioremap_resource(dev, res); + if (IS_ERR(channel->usb2.base)) + return PTR_ERR(channel->usb2.base); + + /* "hsusb" memory resource is optional */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb"); + + /* To avoid error message by devm_ioremap_resource() */ + if (res) { + int irq; + + channel->hsusb.base = devm_ioremap_resource(dev, res); + if (IS_ERR(channel->hsusb.base)) + channel->hsusb.base = NULL; + /* call request_irq for OTG */ + irq = platform_get_irq(pdev, 0); + if (irq >= 0) + irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, + IRQF_SHARED, dev_name(dev), + channel); + if (irq < 0) + dev_err(dev, "No irq handler (%d)\n", irq); + } + + /* devm_phy_create() will call pm_runtime_enable(dev); */ + channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops); + if (IS_ERR(channel->phy)) { + dev_err(dev, "Failed to create USB2 PHY\n"); + return PTR_ERR(channel->phy); + } + + phy_set_drvdata(channel->phy, channel); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + dev_err(dev, "Failed to register PHY provider\n"); + + return PTR_ERR_OR_ZERO(provider); +} + +static struct platform_driver rcar_gen3_phy_usb2_driver = { + .driver = { + .name = "phy_rcar_gen3_usb2", + .of_match_table = rcar_gen3_phy_usb2_match_table, + }, + .probe = rcar_gen3_phy_usb2_probe, +}; +module_platform_driver(rcar_gen3_phy_usb2_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY"); +MODULE_AUTHOR("Yoshihiro Shimoda "); diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index 62c43c435194..33a80eba1cb4 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c @@ -15,12 +15,14 @@ */ #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -36,31 +38,91 @@ #define SIDDQ_ON BIT(13) #define SIDDQ_OFF (0 << 13) +struct rockchip_usb_phys { + int reg; + const char *pll_name; +}; + +struct rockchip_usb_phy_pdata { + struct rockchip_usb_phys *phys; +}; + +struct rockchip_usb_phy_base { + struct device *dev; + struct regmap *reg_base; + const struct rockchip_usb_phy_pdata *pdata; +}; + struct rockchip_usb_phy { + struct rockchip_usb_phy_base *base; + struct device_node *np; unsigned int reg_offset; - struct regmap *reg_base; struct clk *clk; + struct clk *clk480m; + struct clk_hw clk480m_hw; struct phy *phy; }; static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, bool siddq) { - return regmap_write(phy->reg_base, phy->reg_offset, + return regmap_write(phy->base->reg_base, phy->reg_offset, SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF)); } +static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 480000000; +} + +static void rockchip_usb_phy480m_disable(struct clk_hw *hw) +{ + struct rockchip_usb_phy *phy = container_of(hw, + struct rockchip_usb_phy, + clk480m_hw); + + /* Power down usb phy analog blocks by set siddq 1 */ + rockchip_usb_phy_power(phy, 1); +} + +static int rockchip_usb_phy480m_enable(struct clk_hw *hw) +{ + struct rockchip_usb_phy *phy = container_of(hw, + struct rockchip_usb_phy, + clk480m_hw); + + /* Power up usb phy analog blocks by set siddq 0 */ + return rockchip_usb_phy_power(phy, 0); +} + +static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw) +{ + struct rockchip_usb_phy *phy = container_of(hw, + struct rockchip_usb_phy, + clk480m_hw); + int ret; + u32 val; + + ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val); + if (ret < 0) + return ret; + + return (val & SIDDQ_ON) ? 0 : 1; +} + +static const struct clk_ops rockchip_usb_phy480m_ops = { + .enable = rockchip_usb_phy480m_enable, + .disable = rockchip_usb_phy480m_disable, + .is_enabled = rockchip_usb_phy480m_is_enabled, + .recalc_rate = rockchip_usb_phy480m_recalc_rate, +}; + static int rockchip_usb_phy_power_off(struct phy *_phy) { struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); - int ret = 0; - /* Power down usb phy analog blocks by set siddq 1 */ - ret = rockchip_usb_phy_power(phy, 1); - if (ret) - return ret; - - clk_disable_unprepare(phy->clk); + clk_disable_unprepare(phy->clk480m); return 0; } @@ -68,20 +130,8 @@ static int rockchip_usb_phy_power_off(struct phy *_phy) static int rockchip_usb_phy_power_on(struct phy *_phy) { struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); - int ret = 0; - ret = clk_prepare_enable(phy->clk); - if (ret) - return ret; - - /* Power up usb phy analog blocks by set siddq 0 */ - ret = rockchip_usb_phy_power(phy, 0); - if (ret) { - clk_disable_unprepare(phy->clk); - return ret; - } - - return 0; + return clk_prepare_enable(phy->clk480m); } static const struct phy_ops ops = { @@ -90,66 +140,179 @@ static const struct phy_ops ops = { .owner = THIS_MODULE, }; +static void rockchip_usb_phy_action(void *data) +{ + struct rockchip_usb_phy *rk_phy = data; + + of_clk_del_provider(rk_phy->np); + clk_unregister(rk_phy->clk480m); + + if (rk_phy->clk) + clk_put(rk_phy->clk); +} + +static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, + struct device_node *child) +{ + struct rockchip_usb_phy *rk_phy; + unsigned int reg_offset; + const char *clk_name; + struct clk_init_data init; + int err, i; + + rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL); + if (!rk_phy) + return -ENOMEM; + + rk_phy->base = base; + rk_phy->np = child; + + if (of_property_read_u32(child, "reg", ®_offset)) { + dev_err(base->dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + + rk_phy->reg_offset = reg_offset; + + rk_phy->clk = of_clk_get_by_name(child, "phyclk"); + if (IS_ERR(rk_phy->clk)) + rk_phy->clk = NULL; + + i = 0; + init.name = NULL; + while (base->pdata->phys[i].reg) { + if (base->pdata->phys[i].reg == reg_offset) { + init.name = base->pdata->phys[i].pll_name; + break; + } + i++; + } + + if (!init.name) { + dev_err(base->dev, "phy data not found\n"); + return -EINVAL; + } + + if (rk_phy->clk) { + clk_name = __clk_get_name(rk_phy->clk); + init.flags = 0; + init.parent_names = &clk_name; + init.num_parents = 1; + } else { + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + } + + init.ops = &rockchip_usb_phy480m_ops; + rk_phy->clk480m_hw.init = &init; + + rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); + if (IS_ERR(rk_phy->clk480m)) { + err = PTR_ERR(rk_phy->clk480m); + goto err_clk; + } + + err = of_clk_add_provider(child, of_clk_src_simple_get, + rk_phy->clk480m); + if (err < 0) + goto err_clk_prov; + + err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy); + if (err) + goto err_devm_action; + + rk_phy->phy = devm_phy_create(base->dev, child, &ops); + if (IS_ERR(rk_phy->phy)) { + dev_err(base->dev, "failed to create PHY\n"); + return PTR_ERR(rk_phy->phy); + } + phy_set_drvdata(rk_phy->phy, rk_phy); + + /* only power up usb phy when it use, so disable it when init*/ + return rockchip_usb_phy_power(rk_phy, 1); + +err_devm_action: + of_clk_del_provider(child); +err_clk_prov: + clk_unregister(rk_phy->clk480m); +err_clk: + if (rk_phy->clk) + clk_put(rk_phy->clk); + return err; +} + +static const struct rockchip_usb_phy_pdata rk3066a_pdata = { + .phys = (struct rockchip_usb_phys[]){ + { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" }, + { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" }, + { /* sentinel */ } + }, +}; + +static const struct rockchip_usb_phy_pdata rk3188_pdata = { + .phys = (struct rockchip_usb_phys[]){ + { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" }, + { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" }, + { /* sentinel */ } + }, +}; + +static const struct rockchip_usb_phy_pdata rk3288_pdata = { + .phys = (struct rockchip_usb_phys[]){ + { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, + { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" }, + { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, + { /* sentinel */ } + }, +}; + static int rockchip_usb_phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct rockchip_usb_phy *rk_phy; + struct rockchip_usb_phy_base *phy_base; struct phy_provider *phy_provider; + const struct of_device_id *match; struct device_node *child; - struct regmap *grf; - unsigned int reg_offset; int err; - grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); - if (IS_ERR(grf)) { + phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL); + if (!phy_base) + return -ENOMEM; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) { + dev_err(dev, "missing phy data\n"); + return -EINVAL; + } + + phy_base->pdata = match->data; + + phy_base->dev = dev; + phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(phy_base->reg_base)) { dev_err(&pdev->dev, "Missing rockchip,grf property\n"); - return PTR_ERR(grf); + return PTR_ERR(phy_base->reg_base); } for_each_available_child_of_node(dev->of_node, child) { - rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); - if (!rk_phy) { - err = -ENOMEM; - goto put_child; + err = rockchip_usb_phy_init(phy_base, child); + if (err) { + of_node_put(child); + return err; } - - if (of_property_read_u32(child, "reg", ®_offset)) { - dev_err(dev, "missing reg property in node %s\n", - child->name); - err = -EINVAL; - goto put_child; - } - - rk_phy->reg_offset = reg_offset; - rk_phy->reg_base = grf; - - rk_phy->clk = of_clk_get_by_name(child, "phyclk"); - if (IS_ERR(rk_phy->clk)) - rk_phy->clk = NULL; - - rk_phy->phy = devm_phy_create(dev, child, &ops); - if (IS_ERR(rk_phy->phy)) { - dev_err(dev, "failed to create PHY\n"); - err = PTR_ERR(rk_phy->phy); - goto put_child; - } - phy_set_drvdata(rk_phy->phy, rk_phy); - - /* only power up usb phy when it use, so disable it when init*/ - err = rockchip_usb_phy_power(rk_phy, 1); - if (err) - goto put_child; } phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); return PTR_ERR_OR_ZERO(phy_provider); -put_child: - of_node_put(child); - return err; } static const struct of_device_id rockchip_usb_phy_dt_ids[] = { - { .compatible = "rockchip,rk3288-usb-phy" }, + { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata }, + { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata }, + { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, {} }; diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index b12964b70625..bae54f7a1f48 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,9 @@ #define REG_PHYBIST 0x08 #define REG_PHYTUNE 0x0c #define REG_PHYCTL_A33 0x10 +#define REG_PHY_UNK_H3 0x20 + +#define REG_PMU_UNK_H3 0x10 #define PHYCTL_DATA BIT(7) @@ -79,7 +83,7 @@ #define PHY_DISCON_TH_SEL 0x2a #define PHY_SQUELCH_DETECT 0x3c -#define MAX_PHYS 3 +#define MAX_PHYS 4 /* * Note do not raise the debounce time, we must report Vusb high within 100ms @@ -88,12 +92,24 @@ #define DEBOUNCE_TIME msecs_to_jiffies(50) #define POLL_TIME msecs_to_jiffies(250) +enum sun4i_usb_phy_type { + sun4i_a10_phy, + sun8i_a33_phy, + sun8i_h3_phy, +}; + +struct sun4i_usb_phy_cfg { + int num_phys; + enum sun4i_usb_phy_type type; + u32 disc_thresh; + u8 phyctl_offset; + bool dedicated_clocks; +}; + struct sun4i_usb_phy_data { void __iomem *base; + const struct sun4i_usb_phy_cfg *cfg; struct mutex mutex; - int num_phys; - u32 disc_thresh; - bool has_a33_phyctl; struct sun4i_usb_phy { struct phy *phy; void __iomem *pmu; @@ -159,17 +175,14 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, { struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); u32 temp, usbc_bit = BIT(phy->index * 2); - void *phyctl; + void *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; int i; mutex_lock(&phy_data->mutex); - if (phy_data->has_a33_phyctl) { - phyctl = phy_data->base + REG_PHYCTL_A33; + if (phy_data->cfg->type == sun8i_a33_phy) { /* A33 needs us to set phyctl to 0 explicitly */ writel(0, phyctl); - } else { - phyctl = phy_data->base + REG_PHYCTL_A10; } for (i = 0; i < len; i++) { @@ -230,6 +243,7 @@ static int sun4i_usb_phy_init(struct phy *_phy) struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); int ret; + u32 val; ret = clk_prepare_enable(phy->clk); if (ret) @@ -241,15 +255,26 @@ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } - /* Enable USB 45 Ohm resistor calibration */ - if (phy->index == 0) - sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); + if (data->cfg->type == sun8i_h3_phy) { + if (phy->index == 0) { + val = readl(data->base + REG_PHY_UNK_H3); + writel(val & ~1, data->base + REG_PHY_UNK_H3); + } - /* Adjust PHY's magnitude and rate */ - sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); + val = readl(phy->pmu + REG_PMU_UNK_H3); + writel(val & ~2, phy->pmu + REG_PMU_UNK_H3); + } else { + /* Enable USB 45 Ohm resistor calibration */ + if (phy->index == 0) + sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); - /* Disconnect threshold adjustment */ - sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2); + /* Adjust PHY's magnitude and rate */ + sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); + + /* Disconnect threshold adjustment */ + sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, + data->cfg->disc_thresh, 2); + } sun4i_usb_phy_passby(phy, 1); @@ -476,7 +501,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, { struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); - if (args->args[0] >= data->num_phys) + if (args->args[0] >= data->cfg->num_phys) return ERR_PTR(-ENODEV); return data->phys[args->args[0]].phy; @@ -511,7 +536,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct phy_provider *phy_provider; - bool dedicated_clocks; struct resource *res; int i, ret; @@ -522,29 +546,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) mutex_init(&data->mutex); INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); dev_set_drvdata(dev, data); - - if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") || - of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") || - of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy")) - data->num_phys = 2; - else - data->num_phys = 3; - - if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") || - of_device_is_compatible(np, "allwinner,sun7i-a20-usb-phy")) - data->disc_thresh = 2; - else - data->disc_thresh = 3; - - if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy") || - of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") || - of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy")) - dedicated_clocks = true; - else - dedicated_clocks = false; - - if (of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy")) - data->has_a33_phyctl = true; + data->cfg = of_device_get_match_data(dev); + if (!data->cfg) + return -EINVAL; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); data->base = devm_ioremap_resource(dev, res); @@ -590,7 +594,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) } } - for (i = 0; i < data->num_phys; i++) { + for (i = 0; i < data->cfg->num_phys; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[16]; @@ -602,7 +606,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) phy->vbus = NULL; } - if (dedicated_clocks) + if (data->cfg->dedicated_clocks) snprintf(name, sizeof(name), "usb%d_phy", i); else strlcpy(name, "usb_phy", sizeof(name)); @@ -689,13 +693,69 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return 0; } +static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { + .num_phys = 3, + .type = sun4i_a10_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = false, +}; + +static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { + .num_phys = 2, + .type = sun4i_a10_phy, + .disc_thresh = 2, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = false, +}; + +static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { + .num_phys = 3, + .type = sun4i_a10_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = true, +}; + +static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { + .num_phys = 3, + .type = sun4i_a10_phy, + .disc_thresh = 2, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = false, +}; + +static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { + .num_phys = 2, + .type = sun4i_a10_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = true, +}; + +static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { + .num_phys = 2, + .type = sun8i_a33_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, +}; + +static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { + .num_phys = 4, + .type = sun8i_h3_phy, + .disc_thresh = 3, + .dedicated_clocks = true, +}; + static const struct of_device_id sun4i_usb_phy_of_match[] = { - { .compatible = "allwinner,sun4i-a10-usb-phy" }, - { .compatible = "allwinner,sun5i-a13-usb-phy" }, - { .compatible = "allwinner,sun6i-a31-usb-phy" }, - { .compatible = "allwinner,sun7i-a20-usb-phy" }, - { .compatible = "allwinner,sun8i-a23-usb-phy" }, - { .compatible = "allwinner,sun8i-a33-usb-phy" }, + { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, + { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, + { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg }, + { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg }, + { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, + { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, + { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, { }, }; MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 93bc1120af12..0a477d24cf76 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -56,6 +56,18 @@ #define SATA_PLL_SOFT_RESET BIT(18) +#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 +#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14 + +#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 +#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22 + +#define PIPE3_PHY_TX_RX_POWERON 0x3 +#define PIPE3_PHY_TX_RX_POWEROFF 0x0 + +#define PCIE_PCS_MASK 0xFF0000 +#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 + /* * This is an Empirical value that works, need to confirm the actual * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status @@ -86,8 +98,12 @@ struct ti_pipe3 { struct clk *refclk; struct clk *div_clk; struct pipe3_dpll_map *dpll_map; + struct regmap *phy_power_syscon; /* ctrl. reg. acces */ + struct regmap *pcs_syscon; /* ctrl. reg. acces */ struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */ unsigned int dpll_reset_reg; /* reg. index within syscon */ + unsigned int power_reg; /* power reg. index within syscon */ + unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */ bool sata_refclk_enabled; }; @@ -144,20 +160,49 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy); static int ti_pipe3_power_off(struct phy *x) { + u32 val; + int ret; struct ti_pipe3 *phy = phy_get_drvdata(x); - omap_control_phy_power(phy->control_dev, 0); + if (!phy->phy_power_syscon) { + omap_control_phy_power(phy->control_dev, 0); + return 0; + } - return 0; + val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + + ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, + PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val); + return ret; } static int ti_pipe3_power_on(struct phy *x) { + u32 val; + u32 mask; + int ret; + unsigned long rate; struct ti_pipe3 *phy = phy_get_drvdata(x); - omap_control_phy_power(phy->control_dev, 1); + if (!phy->phy_power_syscon) { + omap_control_phy_power(phy->control_dev, 1); + return 0; + } - return 0; + rate = clk_get_rate(phy->sys_clk); + if (!rate) { + dev_err(phy->dev, "Invalid clock rate\n"); + return -EINVAL; + } + rate = rate / 1000000; + mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; + val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; + + ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, + mask, val); + return ret; } static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy) @@ -229,8 +274,15 @@ static int ti_pipe3_init(struct phy *x) * 18-1804. */ if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { - omap_control_pcie_pcs(phy->control_dev, 0x96); - return 0; + if (!phy->pcs_syscon) { + omap_control_pcie_pcs(phy->control_dev, 0x96); + return 0; + } + + val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT; + ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg, + PCIE_PCS_MASK, val); + return ret; } /* Bring it out of IDLE if it is IDLE */ @@ -308,51 +360,15 @@ static const struct phy_ops ops = { static const struct of_device_id ti_pipe3_id_table[]; -static int ti_pipe3_probe(struct platform_device *pdev) +static int ti_pipe3_get_clk(struct ti_pipe3 *phy) { - struct ti_pipe3 *phy; - struct phy *generic_phy; - struct phy_provider *phy_provider; - struct resource *res; - struct device_node *node = pdev->dev.of_node; - struct device_node *control_node; - struct platform_device *control_pdev; - const struct of_device_id *match; struct clk *clk; + struct device *dev = phy->dev; + struct device_node *node = dev->of_node; - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - phy->dev = &pdev->dev; - - if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { - match = of_match_device(ti_pipe3_id_table, &pdev->dev); - if (!match) - return -EINVAL; - - phy->dpll_map = (struct pipe3_dpll_map *)match->data; - if (!phy->dpll_map) { - dev_err(&pdev->dev, "no DPLL data\n"); - return -EINVAL; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->pll_ctrl_base)) - return PTR_ERR(phy->pll_ctrl_base); - - phy->sys_clk = devm_clk_get(phy->dev, "sysclk"); - if (IS_ERR(phy->sys_clk)) { - dev_err(&pdev->dev, "unable to get sysclk\n"); - return -EINVAL; - } - } - - phy->refclk = devm_clk_get(phy->dev, "refclk"); + phy->refclk = devm_clk_get(dev, "refclk"); if (IS_ERR(phy->refclk)) { - dev_err(&pdev->dev, "unable to get refclk\n"); + dev_err(dev, "unable to get refclk\n"); /* older DTBs have missing refclk in SATA PHY * so don't bail out in case of SATA PHY. */ @@ -361,80 +377,194 @@ static int ti_pipe3_probe(struct platform_device *pdev) } if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { - phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); + phy->wkupclk = devm_clk_get(dev, "wkupclk"); if (IS_ERR(phy->wkupclk)) { - dev_err(&pdev->dev, "unable to get wkupclk\n"); + dev_err(dev, "unable to get wkupclk\n"); return PTR_ERR(phy->wkupclk); } } else { phy->wkupclk = ERR_PTR(-ENODEV); - phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, - "syscon-pllreset"); - if (IS_ERR(phy->dpll_reset_syscon)) { - dev_info(&pdev->dev, - "can't get syscon-pllreset, sata dpll won't idle\n"); - phy->dpll_reset_syscon = NULL; - } else { - if (of_property_read_u32_index(node, - "syscon-pllreset", 1, - &phy->dpll_reset_reg)) { - dev_err(&pdev->dev, - "couldn't get pllreset reg. offset\n"); - return -EINVAL; - } + } + + if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") || + phy->phy_power_syscon) { + phy->sys_clk = devm_clk_get(dev, "sysclk"); + if (IS_ERR(phy->sys_clk)) { + dev_err(dev, "unable to get sysclk\n"); + return -EINVAL; } } if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { - - clk = devm_clk_get(phy->dev, "dpll_ref"); + clk = devm_clk_get(dev, "dpll_ref"); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "unable to get dpll ref clk\n"); + dev_err(dev, "unable to get dpll ref clk\n"); return PTR_ERR(clk); } clk_set_rate(clk, 1500000000); - clk = devm_clk_get(phy->dev, "dpll_ref_m2"); + clk = devm_clk_get(dev, "dpll_ref_m2"); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n"); + dev_err(dev, "unable to get dpll ref m2 clk\n"); return PTR_ERR(clk); } clk_set_rate(clk, 100000000); - clk = devm_clk_get(phy->dev, "phy-div"); + clk = devm_clk_get(dev, "phy-div"); if (IS_ERR(clk)) { - dev_err(&pdev->dev, "unable to get phy-div clk\n"); + dev_err(dev, "unable to get phy-div clk\n"); return PTR_ERR(clk); } clk_set_rate(clk, 100000000); - phy->div_clk = devm_clk_get(phy->dev, "div-clk"); + phy->div_clk = devm_clk_get(dev, "div-clk"); if (IS_ERR(phy->div_clk)) { - dev_err(&pdev->dev, "unable to get div-clk\n"); + dev_err(dev, "unable to get div-clk\n"); return PTR_ERR(phy->div_clk); } } else { phy->div_clk = ERR_PTR(-ENODEV); } - control_node = of_parse_phandle(node, "ctrl-module", 0); - if (!control_node) { - dev_err(&pdev->dev, "Failed to get control device phandle\n"); + return 0; +} + +static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) +{ + struct device *dev = phy->dev; + struct device_node *node = dev->of_node; + struct device_node *control_node; + struct platform_device *control_pdev; + + phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node, + "syscon-phy-power"); + if (IS_ERR(phy->phy_power_syscon)) { + dev_dbg(dev, + "can't get syscon-phy-power, using control device\n"); + phy->phy_power_syscon = NULL; + } else { + if (of_property_read_u32_index(node, + "syscon-phy-power", 1, + &phy->power_reg)) { + dev_err(dev, "couldn't get power reg. offset\n"); + return -EINVAL; + } + } + + if (!phy->phy_power_syscon) { + control_node = of_parse_phandle(node, "ctrl-module", 0); + if (!control_node) { + dev_err(dev, "Failed to get control device phandle\n"); + return -EINVAL; + } + + control_pdev = of_find_device_by_node(control_node); + if (!control_pdev) { + dev_err(dev, "Failed to get control device\n"); + return -EINVAL; + } + + phy->control_dev = &control_pdev->dev; + } + + if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { + phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node, + "syscon-pcs"); + if (IS_ERR(phy->pcs_syscon)) { + dev_dbg(dev, + "can't get syscon-pcs, using omap control\n"); + phy->pcs_syscon = NULL; + } else { + if (of_property_read_u32_index(node, + "syscon-pcs", 1, + &phy->pcie_pcs_reg)) { + dev_err(dev, + "couldn't get pcie pcs reg. offset\n"); + return -EINVAL; + } + } + } + + if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { + phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, + "syscon-pllreset"); + if (IS_ERR(phy->dpll_reset_syscon)) { + dev_info(dev, + "can't get syscon-pllreset, sata dpll won't idle\n"); + phy->dpll_reset_syscon = NULL; + } else { + if (of_property_read_u32_index(node, + "syscon-pllreset", 1, + &phy->dpll_reset_reg)) { + dev_err(dev, + "couldn't get pllreset reg. offset\n"); + return -EINVAL; + } + } + } + + return 0; +} + +static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) +{ + struct resource *res; + const struct of_device_id *match; + struct device *dev = phy->dev; + struct device_node *node = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); + + if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) + return 0; + + match = of_match_device(ti_pipe3_id_table, dev); + if (!match) + return -EINVAL; + + phy->dpll_map = (struct pipe3_dpll_map *)match->data; + if (!phy->dpll_map) { + dev_err(dev, "no DPLL data\n"); return -EINVAL; } - control_pdev = of_find_device_by_node(control_node); - if (!control_pdev) { - dev_err(&pdev->dev, "Failed to get control device\n"); - return -EINVAL; - } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pll_ctrl"); + phy->pll_ctrl_base = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->pll_ctrl_base)) + return PTR_ERR(phy->pll_ctrl_base); - phy->control_dev = &control_pdev->dev; + return 0; +} - omap_control_phy_power(phy->control_dev, 0); +static int ti_pipe3_probe(struct platform_device *pdev) +{ + struct ti_pipe3 *phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->dev = dev; + + ret = ti_pipe3_get_pll_base(phy); + if (ret) + return ret; + + ret = ti_pipe3_get_sysctrl(phy); + if (ret) + return ret; + + ret = ti_pipe3_get_clk(phy); + if (ret) + return ret; platform_set_drvdata(pdev, phy); - pm_runtime_enable(phy->dev); + pm_runtime_enable(dev); /* * Prevent auto-disable of refclk for SATA PHY due to Errata i783 @@ -446,13 +576,15 @@ static int ti_pipe3_probe(struct platform_device *pdev) } } - generic_phy = devm_phy_create(phy->dev, NULL, &ops); + generic_phy = devm_phy_create(dev, NULL, &ops); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); phy_set_drvdata(generic_phy, phy); - phy_provider = devm_of_phy_provider_register(phy->dev, - of_phy_simple_xlate); + + ti_pipe3_power_off(generic_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (IS_ERR(phy_provider)) return PTR_ERR(phy_provider); diff --git a/include/linux/phy/omap_usb.h b/include/linux/phy/omap_usb.h index dc2c541a619b..2e5fb870efa9 100644 --- a/include/linux/phy/omap_usb.h +++ b/include/linux/phy/omap_usb.h @@ -30,6 +30,12 @@ struct usb_dpll_params { u32 mf; }; +enum omap_usb_phy_type { + TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */ + TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */ + TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */ +}; + struct omap_usb { struct usb_phy phy; struct phy_companion *comparator; @@ -40,11 +46,20 @@ struct omap_usb { struct clk *wkupclk; struct clk *optclk; u8 flags; + enum omap_usb_phy_type type; + struct regmap *syscon_phy_power; /* ctrl. reg. acces */ + unsigned int power_reg; /* power reg. index within syscon */ + u32 mask; + u32 power_on; + u32 power_off; }; struct usb_phy_data { const char *label; u8 flags; + u32 mask; + u32 power_on; + u32 power_off; }; /* Driver Flags */ @@ -52,6 +67,14 @@ struct usb_phy_data { #define OMAP_USB2_HAS_SET_VBUS (1 << 1) #define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT (1 << 2) +#define OMAP_DEV_PHY_PD BIT(0) +#define OMAP_USB2_PHY_PD BIT(28) + +#define AM437X_USB2_PHY_PD BIT(0) +#define AM437X_USB2_OTG_PD BIT(1) +#define AM437X_USB2_OTGVDET_EN BIT(19) +#define AM437X_USB2_OTGSESSEND_EN BIT(20) + #define phy_to_omapusb(x) container_of((x), struct omap_usb, phy) #if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)