diff --git a/Documentation/ABI/testing/sysfs-platform-phy-rcar-gen3-usb2 b/Documentation/ABI/testing/sysfs-platform-phy-rcar-gen3-usb2 new file mode 100644 index 000000000000..6212697bbf6f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-phy-rcar-gen3-usb2 @@ -0,0 +1,15 @@ +What: /sys/devices/platform//role +Date: October 2016 +KernelVersion: 4.10 +Contact: Yoshihiro Shimoda +Description: + This file can be read and write. + The file can show/change the phy mode for role swap of usb. + + Write the following strings to change the mode: + "host" - switching mode from peripheral to host. + "peripheral" - switching mode from host to peripheral. + + Read the file, then it shows the following strings: + "host" - The mode is host now. + "peripheral" - The mode is peripheral now. diff --git a/Documentation/devicetree/bindings/phy/meson-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson8b-usb2-phy.txt similarity index 87% rename from Documentation/devicetree/bindings/phy/meson-usb2-phy.txt rename to Documentation/devicetree/bindings/phy/meson8b-usb2-phy.txt index 9da5ea234154..5fa73b9d20f5 100644 --- a/Documentation/devicetree/bindings/phy/meson-usb2-phy.txt +++ b/Documentation/devicetree/bindings/phy/meson8b-usb2-phy.txt @@ -1,4 +1,4 @@ -* Amlogic USB2 PHY +* Amlogic Meson8b and GXBB USB2 PHY Required properties: - compatible: Depending on the platform this should be one of: @@ -16,10 +16,10 @@ Optional properties: Example: -usb0_phy: usb_phy@0 { +usb0_phy: usb-phy@c0000000 { compatible = "amlogic,meson-gxbb-usb2-phy"; #phy-cells = <0>; - reg = <0x0 0x0 0x0 0x20>; + reg = <0x0 0xc0000000 0x0 0x20>; resets = <&reset RESET_USB_OTG>; clocks = <&clkc CLKID_USB>, <&clkc CLKID_USB0>; clock-names = "usb_general", "usb"; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index fe00f9134d51..e8eb7f225a88 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -129,16 +129,6 @@ config PHY_MIPHY28LP Enable this to support the miphy transceiver (for SATA/PCIE/USB3) that is part of STMicroelectronics STiH407 SoC. -config PHY_MIPHY365X - tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series" - depends on ARCH_STI - depends on HAS_IOMEM - depends on OF - select GENERIC_PHY - help - Enable this to support the miphy transceiver (for SATA/PCIE) - that is part of STMicroelectronics STiH41x SoC series. - config PHY_RCAR_GEN2 tristate "Renesas R-Car generation 2 USB PHY driver" depends on ARCH_RENESAS @@ -373,7 +363,9 @@ config PHY_ROCKCHIP_INNO_USB2 tristate "Rockchip INNO USB2PHY Driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF depends on COMMON_CLK + depends on USB_SUPPORT select GENERIC_PHY + select USB_COMMON help Support for Rockchip USB2.0 PHY with Innosilicon IP block. @@ -438,14 +430,6 @@ config PHY_STIH407_USB Enable this support to enable the picoPHY device used by USB2 and USB3 controllers on STMicroelectronics STiH407 SoC families. -config PHY_STIH41X_USB - tristate "STMicroelectronics USB2 PHY driver for STiH41x series" - depends on ARCH_STI - select GENERIC_PHY - help - Enable this to support the USB transceiver that is part of - STMicroelectronics STiH41x SoC series. - config PHY_QCOM_UFS tristate "Qualcomm UFS PHY driver" depends on OF && ARCH_QCOM @@ -489,4 +473,17 @@ config PHY_NS2_PCIE help Enable this to support the Broadcom Northstar2 PCIe PHY. If unsure, say N. + +config PHY_MESON8B_USB2 + tristate "Meson8b and GXBB USB2 PHY driver" + default ARCH_MESON + depends on OF && (ARCH_MESON || COMPILE_TEST) + depends on USB_SUPPORT + select USB_COMMON + select GENERIC_PHY + help + Enable this to support the Meson USB2 PHYs found in Meson8b + and GXBB SoCs. + If unsure, say N. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a534cf5be07d..65eb2f436a41 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o 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 @@ -50,7 +49,6 @@ obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o -obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o @@ -60,3 +58,4 @@ obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o +obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index f84a33a1bdd9..2c7a57f2d595 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -85,7 +85,6 @@ static int phy_berlin_sata_power_on(struct phy *phy) struct phy_berlin_desc *desc = phy_get_drvdata(phy); struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); - int ret = 0; u32 regval; clk_prepare_enable(priv->clk); @@ -130,7 +129,7 @@ static int phy_berlin_sata_power_on(struct phy *phy) clk_disable_unprepare(priv->clk); - return ret; + return 0; } static int phy_berlin_sata_power_off(struct phy *phy) diff --git a/drivers/phy/phy-brcm-sata.c b/drivers/phy/phy-brcm-sata.c index 8ffc44afdb75..ccbc3d994998 100644 --- a/drivers/phy/phy-brcm-sata.c +++ b/drivers/phy/phy-brcm-sata.c @@ -140,7 +140,7 @@ static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) default: dev_err(priv->dev, "invalid phy version\n"); break; - }; + } return priv->phy_base + (port->portnum * size); } @@ -157,7 +157,7 @@ static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port) default: dev_err(priv->dev, "invalid phy version\n"); break; - }; + } return priv->ctrl_base + (port->portnum * size); } @@ -365,7 +365,7 @@ static int brcm_sata_phy_init(struct phy *phy) break; default: rc = -ENODEV; - }; + } return rc; } diff --git a/drivers/phy/phy-da8xx-usb.c b/drivers/phy/phy-da8xx-usb.c index c85fb0b59729..1b82bff6330f 100644 --- a/drivers/phy/phy-da8xx-usb.c +++ b/drivers/phy/phy-da8xx-usb.c @@ -23,6 +23,8 @@ #include #include +#define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN) + struct da8xx_usb_phy { struct phy_provider *phy_provider; struct phy *usb11_phy; @@ -208,6 +210,9 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev) dev_warn(dev, "Failed to create usb20 phy lookup\n"); } + regmap_write_bits(d_phy->regmap, CFGCHIP(2), + PHY_INIT_BITS, PHY_INIT_BITS); + return 0; } diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 8b851f718123..6bee04cc4d53 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -229,19 +229,6 @@ struct exynos_mipi_video_phy { spinlock_t slock; }; -static inline int __is_running(const struct exynos_mipi_phy_desc *data, - struct exynos_mipi_video_phy *state) -{ - u32 val; - int ret; - - ret = regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val); - if (ret) - return 0; - - return val & data->resetn_val; -} - static int __set_phy_state(const struct exynos_mipi_phy_desc *data, struct exynos_mipi_video_phy *state, unsigned int on) { @@ -251,7 +238,7 @@ static int __set_phy_state(const struct exynos_mipi_phy_desc *data, /* disable in PMU sysreg */ if (!on && data->coupled_phy_id >= 0 && - !__is_running(state->phys[data->coupled_phy_id].data, state)) { + state->phys[data->coupled_phy_id].phy->power_count == 0) { regmap_read(state->regmaps[data->enable_map], data->enable_reg, &val); val &= ~data->enable_val; diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c index f30bbb0fb3b2..1f50e1004828 100644 --- a/drivers/phy/phy-exynos4210-usb2.c +++ b/drivers/phy/phy-exynos4210-usb2.c @@ -141,7 +141,7 @@ static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) break; default: return; - }; + } regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); } @@ -179,7 +179,7 @@ static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | EXYNOS_4210_URSTCON_HOST_LINK_P2; break; - }; + } if (on) { clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c index 765da90a536f..7f27a91acf87 100644 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ b/drivers/phy/phy-exynos4x12-usb2.c @@ -187,7 +187,7 @@ static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) break; default: return; - }; + } regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); } @@ -237,7 +237,7 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) rstbits = EXYNOS_4x12_URSTCON_HSIC1 | EXYNOS_4x12_URSTCON_HOST_LINK_P1; break; - }; + } if (on) { pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c index 2ed1735a076a..aad806272305 100644 --- a/drivers/phy/phy-exynos5250-usb2.c +++ b/drivers/phy/phy-exynos5250-usb2.c @@ -192,7 +192,7 @@ static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on) break; default: return; - }; + } regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); } diff --git a/drivers/phy/phy-meson8b-usb2.c b/drivers/phy/phy-meson8b-usb2.c new file mode 100644 index 000000000000..33c9f4ba157d --- /dev/null +++ b/drivers/phy/phy-meson8b-usb2.c @@ -0,0 +1,286 @@ +/* + * Meson8b and GXBB USB2 PHY driver + * + * Copyright (C) 2016 Martin Blumenstingl + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CONFIG 0x00 + #define REG_CONFIG_CLK_EN BIT(0) + #define REG_CONFIG_CLK_SEL_MASK GENMASK(3, 1) + #define REG_CONFIG_CLK_DIV_MASK GENMASK(10, 4) + #define REG_CONFIG_CLK_32k_ALTSEL BIT(15) + #define REG_CONFIG_TEST_TRIG BIT(31) + +#define REG_CTRL 0x04 + #define REG_CTRL_SOFT_PRST BIT(0) + #define REG_CTRL_SOFT_HRESET BIT(1) + #define REG_CTRL_SS_SCALEDOWN_MODE_MASK GENMASK(3, 2) + #define REG_CTRL_CLK_DET_RST BIT(4) + #define REG_CTRL_INTR_SEL BIT(5) + #define REG_CTRL_CLK_DETECTED BIT(8) + #define REG_CTRL_SOF_SENT_RCVD_TGL BIT(9) + #define REG_CTRL_SOF_TOGGLE_OUT BIT(10) + #define REG_CTRL_POWER_ON_RESET BIT(15) + #define REG_CTRL_SLEEPM BIT(16) + #define REG_CTRL_TX_BITSTUFF_ENN_H BIT(17) + #define REG_CTRL_TX_BITSTUFF_ENN BIT(18) + #define REG_CTRL_COMMON_ON BIT(19) + #define REG_CTRL_REF_CLK_SEL_MASK GENMASK(21, 20) + #define REG_CTRL_REF_CLK_SEL_SHIFT 20 + #define REG_CTRL_FSEL_MASK GENMASK(24, 22) + #define REG_CTRL_FSEL_SHIFT 22 + #define REG_CTRL_PORT_RESET BIT(25) + #define REG_CTRL_THREAD_ID_MASK GENMASK(31, 26) + +#define REG_ENDP_INTR 0x08 + +/* bits [31:26], [24:21] and [15:3] seem to be read-only */ +#define REG_ADP_BC 0x0c + #define REG_ADP_BC_VBUS_VLD_EXT_SEL BIT(0) + #define REG_ADP_BC_VBUS_VLD_EXT BIT(1) + #define REG_ADP_BC_OTG_DISABLE BIT(2) + #define REG_ADP_BC_ID_PULLUP BIT(3) + #define REG_ADP_BC_DRV_VBUS BIT(4) + #define REG_ADP_BC_ADP_PRB_EN BIT(5) + #define REG_ADP_BC_ADP_DISCHARGE BIT(6) + #define REG_ADP_BC_ADP_CHARGE BIT(7) + #define REG_ADP_BC_SESS_END BIT(8) + #define REG_ADP_BC_DEVICE_SESS_VLD BIT(9) + #define REG_ADP_BC_B_VALID BIT(10) + #define REG_ADP_BC_A_VALID BIT(11) + #define REG_ADP_BC_ID_DIG BIT(12) + #define REG_ADP_BC_VBUS_VALID BIT(13) + #define REG_ADP_BC_ADP_PROBE BIT(14) + #define REG_ADP_BC_ADP_SENSE BIT(15) + #define REG_ADP_BC_ACA_ENABLE BIT(16) + #define REG_ADP_BC_DCD_ENABLE BIT(17) + #define REG_ADP_BC_VDAT_DET_EN_B BIT(18) + #define REG_ADP_BC_VDAT_SRC_EN_B BIT(19) + #define REG_ADP_BC_CHARGE_SEL BIT(20) + #define REG_ADP_BC_CHARGE_DETECT BIT(21) + #define REG_ADP_BC_ACA_PIN_RANGE_C BIT(22) + #define REG_ADP_BC_ACA_PIN_RANGE_B BIT(23) + #define REG_ADP_BC_ACA_PIN_RANGE_A BIT(24) + #define REG_ADP_BC_ACA_PIN_GND BIT(25) + #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) + +#define REG_DBG_UART 0x14 + +#define REG_TEST 0x18 + #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) + #define REG_TEST_EN_MASK GENMASK(7, 4) + #define REG_TEST_ADDR_MASK GENMASK(11, 8) + #define REG_TEST_DATA_OUT_SEL BIT(12) + #define REG_TEST_CLK BIT(13) + #define REG_TEST_VA_TEST_EN_B_MASK GENMASK(15, 14) + #define REG_TEST_DATA_OUT_MASK GENMASK(19, 16) + #define REG_TEST_DISABLE_ID_PULLUP BIT(20) + +#define REG_TUNE 0x1c + #define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0) + #define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2) + #define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4) + #define REG_TUNE_TX_RISE_TUNE_MASK GENMASK(9, 8) + #define REG_TUNE_TX_PREEMP_PULSE_TUNE BIT(10) + #define REG_TUNE_TX_PREEMP_AMP_TUNE_MASK GENMASK(12, 11) + #define REG_TUNE_TX_FSLS_TUNE_MASK GENMASK(16, 13) + #define REG_TUNE_SQRX_TUNE_MASK GENMASK(19, 17) + #define REG_TUNE_OTG_TUNE GENMASK(22, 20) + #define REG_TUNE_COMP_DIS_TUNE GENMASK(25, 23) + #define REG_TUNE_HOST_DM_PULLDOWN BIT(26) + #define REG_TUNE_HOST_DP_PULLDOWN BIT(27) + +#define RESET_COMPLETE_TIME 500 +#define ACA_ENABLE_COMPLETE_TIME 50 + +struct phy_meson8b_usb2_priv { + void __iomem *regs; + enum usb_dr_mode dr_mode; + struct clk *clk_usb_general; + struct clk *clk_usb; + struct reset_control *reset; +}; + +static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, + u32 reg) +{ + return readl(phy_priv->regs + reg); +} + +static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, + u32 reg, u32 mask, u32 value) +{ + u32 data; + + data = phy_meson8b_usb2_read(phy_priv, reg); + data &= ~mask; + data |= (value & mask); + + writel(data, phy_priv->regs + reg); +} + +static int phy_meson8b_usb2_power_on(struct phy *phy) +{ + struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + int ret; + + if (!IS_ERR_OR_NULL(priv->reset)) { + ret = reset_control_reset(priv->reset); + if (ret) { + dev_err(&phy->dev, "Failed to trigger USB reset\n"); + return ret; + } + } + + ret = clk_prepare_enable(priv->clk_usb_general); + if (ret) { + dev_err(&phy->dev, "Failed to enable USB general clock\n"); + return ret; + } + + ret = clk_prepare_enable(priv->clk_usb); + if (ret) { + dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); + clk_disable_unprepare(priv->clk_usb_general); + return ret; + } + + phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, + REG_CONFIG_CLK_32k_ALTSEL); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, + 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, + 0x5 << REG_CTRL_FSEL_SHIFT); + + /* reset the PHY */ + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, + REG_CTRL_POWER_ON_RESET); + udelay(RESET_COMPLETE_TIME); + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); + udelay(RESET_COMPLETE_TIME); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, + REG_CTRL_SOF_TOGGLE_OUT); + + if (priv->dr_mode == USB_DR_MODE_HOST) { + phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, + REG_ADP_BC_ACA_ENABLE, + REG_ADP_BC_ACA_ENABLE); + + udelay(ACA_ENABLE_COMPLETE_TIME); + + if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & + REG_ADP_BC_ACA_PIN_FLOAT) { + dev_warn(&phy->dev, "USB ID detect failed!\n"); + clk_disable_unprepare(priv->clk_usb); + clk_disable_unprepare(priv->clk_usb_general); + return -EINVAL; + } + } + + return 0; +} + +static int phy_meson8b_usb2_power_off(struct phy *phy) +{ + struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + + clk_disable_unprepare(priv->clk_usb); + clk_disable_unprepare(priv->clk_usb_general); + + return 0; +} + +static const struct phy_ops phy_meson8b_usb2_ops = { + .power_on = phy_meson8b_usb2_power_on, + .power_off = phy_meson8b_usb2_power_off, + .owner = THIS_MODULE, +}; + +static int phy_meson8b_usb2_probe(struct platform_device *pdev) +{ + struct phy_meson8b_usb2_priv *priv; + struct resource *res; + struct phy *phy; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); + if (IS_ERR(priv->clk_usb_general)) + return PTR_ERR(priv->clk_usb_general); + + priv->clk_usb = devm_clk_get(&pdev->dev, "usb"); + if (IS_ERR(priv->clk_usb)) + return PTR_ERR(priv->clk_usb); + + priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); + if (PTR_ERR(priv->reset) == -EPROBE_DEFER) + return PTR_ERR(priv->reset); + + priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); + if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { + dev_err(&pdev->dev, + "missing dual role configuration of the controller\n"); + return -EINVAL; + } + + phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + phy_provider = + devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_meson8b_usb2_of_match[] = { + { .compatible = "amlogic,meson8b-usb2-phy", }, + { .compatible = "amlogic,meson-gxbb-usb2-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); + +static struct platform_driver phy_meson8b_usb2_driver = { + .probe = phy_meson8b_usb2_probe, + .driver = { + .name = "phy-meson-usb2", + .of_match_table = phy_meson8b_usb2_of_match, + }, +}; +module_platform_driver(phy_meson8b_usb2_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Meson8b and GXBB USB2 PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c deleted file mode 100644 index e661f3b36eaa..000000000000 --- a/drivers/phy/phy-miphy365x.c +++ /dev/null @@ -1,625 +0,0 @@ -/* - * Copyright (C) 2014 STMicroelectronics – All Rights Reserved - * - * STMicroelectronics PHY driver MiPHY365 (for SoC STiH416). - * - * Authors: Alexandre Torgue - * Lee Jones - * - * 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 -#include -#include -#include -#include -#include - -#include - -#define HFC_TIMEOUT 100 - -#define SYSCFG_SELECT_SATA_MASK BIT(1) -#define SYSCFG_SELECT_SATA_POS 1 - -/* MiPHY365x register definitions */ -#define RESET_REG 0x00 -#define RST_PLL BIT(1) -#define RST_PLL_CAL BIT(2) -#define RST_RX BIT(4) -#define RST_MACRO BIT(7) - -#define STATUS_REG 0x01 -#define IDLL_RDY BIT(0) -#define PLL_RDY BIT(1) -#define DES_BIT_LOCK BIT(2) -#define DES_SYMBOL_LOCK BIT(3) - -#define CTRL_REG 0x02 -#define TERM_EN BIT(0) -#define PCI_EN BIT(2) -#define DES_BIT_LOCK_EN BIT(3) -#define TX_POL BIT(5) - -#define INT_CTRL_REG 0x03 - -#define BOUNDARY1_REG 0x10 -#define SPDSEL_SEL BIT(0) - -#define BOUNDARY3_REG 0x12 -#define TX_SPDSEL_GEN1_VAL 0 -#define TX_SPDSEL_GEN2_VAL 0x01 -#define TX_SPDSEL_GEN3_VAL 0x02 -#define RX_SPDSEL_GEN1_VAL 0 -#define RX_SPDSEL_GEN2_VAL (0x01 << 3) -#define RX_SPDSEL_GEN3_VAL (0x02 << 3) - -#define PCIE_REG 0x16 - -#define BUF_SEL_REG 0x20 -#define CONF_GEN_SEL_GEN3 0x02 -#define CONF_GEN_SEL_GEN2 0x01 -#define PD_VDDTFILTER BIT(4) - -#define TXBUF1_REG 0x21 -#define SWING_VAL 0x04 -#define SWING_VAL_GEN1 0x03 -#define PREEMPH_VAL (0x3 << 5) - -#define TXBUF2_REG 0x22 -#define TXSLEW_VAL 0x2 -#define TXSLEW_VAL_GEN1 0x4 - -#define RXBUF_OFFSET_CTRL_REG 0x23 - -#define RXBUF_REG 0x25 -#define SDTHRES_VAL 0x01 -#define EQ_ON3 (0x03 << 4) -#define EQ_ON1 (0x01 << 4) - -#define COMP_CTRL1_REG 0x40 -#define START_COMSR BIT(0) -#define START_COMZC BIT(1) -#define COMSR_DONE BIT(2) -#define COMZC_DONE BIT(3) -#define COMP_AUTO_LOAD BIT(4) - -#define COMP_CTRL2_REG 0x41 -#define COMP_2MHZ_RAT_GEN1 0x1e -#define COMP_2MHZ_RAT 0xf - -#define COMP_CTRL3_REG 0x42 -#define COMSR_COMP_REF 0x33 - -#define COMP_IDLL_REG 0x47 -#define COMZC_IDLL 0x2a - -#define PLL_CTRL1_REG 0x50 -#define PLL_START_CAL BIT(0) -#define BUF_EN BIT(2) -#define SYNCHRO_TX BIT(3) -#define SSC_EN BIT(6) -#define CONFIG_PLL BIT(7) - -#define PLL_CTRL2_REG 0x51 -#define BYPASS_PLL_CAL BIT(1) - -#define PLL_RAT_REG 0x52 - -#define PLL_SSC_STEP_MSB_REG 0x56 -#define PLL_SSC_STEP_MSB_VAL 0x03 - -#define PLL_SSC_STEP_LSB_REG 0x57 -#define PLL_SSC_STEP_LSB_VAL 0x63 - -#define PLL_SSC_PER_MSB_REG 0x58 -#define PLL_SSC_PER_MSB_VAL 0 - -#define PLL_SSC_PER_LSB_REG 0x59 -#define PLL_SSC_PER_LSB_VAL 0xf1 - -#define IDLL_TEST_REG 0x72 -#define START_CLK_HF BIT(6) - -#define DES_BITLOCK_REG 0x86 -#define BIT_LOCK_LEVEL 0x01 -#define BIT_LOCK_CNT_512 (0x03 << 5) - -struct miphy365x_phy { - struct phy *phy; - void __iomem *base; - bool pcie_tx_pol_inv; - bool sata_tx_pol_inv; - u32 sata_gen; - u32 ctrlreg; - u8 type; -}; - -struct miphy365x_dev { - struct device *dev; - struct regmap *regmap; - struct mutex miphy_mutex; - struct miphy365x_phy **phys; - int nphys; -}; - -/* - * These values are represented in Device tree. They are considered to be ABI - * and although they can be extended any existing values must not change. - */ -enum miphy_sata_gen { - SATA_GEN1 = 1, - SATA_GEN2, - SATA_GEN3 -}; - -static u8 rx_tx_spd[] = { - 0, /* GEN0 doesn't exist. */ - TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL, - TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL, - TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL -}; - -/* - * This function selects the system configuration, - * either two SATA, one SATA and one PCIe, or two PCIe lanes. - */ -static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, - struct miphy365x_dev *miphy_dev) -{ - bool sata = (miphy_phy->type == PHY_TYPE_SATA); - - return regmap_update_bits(miphy_dev->regmap, - miphy_phy->ctrlreg, - SYSCFG_SELECT_SATA_MASK, - sata << SYSCFG_SELECT_SATA_POS); -} - -static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy, - struct miphy365x_dev *miphy_dev) -{ - u8 val; - - if (miphy_phy->pcie_tx_pol_inv) { - /* Invert Tx polarity and clear pci_txdetect_pol bit */ - val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL; - writeb_relaxed(val, miphy_phy->base + CTRL_REG); - writeb_relaxed(0x00, miphy_phy->base + PCIE_REG); - } - - return 0; -} - -static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy, - struct miphy365x_dev *miphy_dev) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); - u8 mask = IDLL_RDY | PLL_RDY; - u8 regval; - - do { - regval = readb_relaxed(miphy_phy->base + STATUS_REG); - if (!(regval & mask)) - return 0; - - usleep_range(2000, 2500); - } while (time_before(jiffies, timeout)); - - dev_err(miphy_dev->dev, "HFC ready timeout!\n"); - return -EBUSY; -} - -static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy, - struct miphy365x_dev *miphy_dev) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT); - u8 mask = IDLL_RDY | PLL_RDY; - u8 regval; - - do { - regval = readb_relaxed(miphy_phy->base + STATUS_REG); - if ((regval & mask) == mask) - return 0; - - usleep_range(2000, 2500); - } while (time_before(jiffies, timeout)); - - dev_err(miphy_dev->dev, "PHY not ready timeout!\n"); - return -EBUSY; -} - -static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy, - struct miphy365x_dev *miphy_dev) -{ - u8 val, mask; - - if (miphy_phy->sata_gen == SATA_GEN1) - writeb_relaxed(COMP_2MHZ_RAT_GEN1, - miphy_phy->base + COMP_CTRL2_REG); - else - writeb_relaxed(COMP_2MHZ_RAT, - miphy_phy->base + COMP_CTRL2_REG); - - if (miphy_phy->sata_gen != SATA_GEN3) { - writeb_relaxed(COMSR_COMP_REF, - miphy_phy->base + COMP_CTRL3_REG); - /* - * Force VCO current to value defined by address 0x5A - * and disable PCIe100Mref bit - * Enable auto load compensation for pll_i_bias - */ - writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG); - writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG); - } - - /* - * Force restart compensation and enable auto load - * for Comzc_Tx, Comzc_Rx and Comsr on macro - */ - val = START_COMSR | START_COMZC | COMP_AUTO_LOAD; - writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG); - - mask = COMSR_DONE | COMZC_DONE; - while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask) - cpu_relax(); -} - -static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy, - struct miphy365x_dev *miphy_dev) -{ - u8 val; - - /* - * SSC Settings. SSC will be enabled through Link - * SSC Ampl. = 0.4% - * SSC Freq = 31KHz - */ - writeb_relaxed(PLL_SSC_STEP_MSB_VAL, - miphy_phy->base + PLL_SSC_STEP_MSB_REG); - writeb_relaxed(PLL_SSC_STEP_LSB_VAL, - miphy_phy->base + PLL_SSC_STEP_LSB_REG); - writeb_relaxed(PLL_SSC_PER_MSB_VAL, - miphy_phy->base + PLL_SSC_PER_MSB_REG); - writeb_relaxed(PLL_SSC_PER_LSB_VAL, - miphy_phy->base + PLL_SSC_PER_LSB_REG); - - /* SSC Settings complete */ - if (miphy_phy->sata_gen == SATA_GEN1) { - val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; - writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); - } else { - val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL; - writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG); - } -} - -static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy, - struct miphy365x_dev *miphy_dev) -{ - int ret; - u8 val; - - /* - * Force PHY macro reset, PLL calibration reset, PLL reset - * and assert Deserializer Reset - */ - val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO; - writeb_relaxed(val, miphy_phy->base + RESET_REG); - - if (miphy_phy->sata_tx_pol_inv) - writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG); - - /* - * Force macro1 to use rx_lspd, tx_lspd - * Force Rx_Clock on first I-DLL phase - * Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3 - */ - writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG); - writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG); - val = rx_tx_spd[miphy_phy->sata_gen]; - writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG); - - /* Wait for HFC_READY = 0 */ - ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev); - if (ret) - return ret; - - /* Compensation Recalibration */ - miphy365x_set_comp(miphy_phy, miphy_dev); - - switch (miphy_phy->sata_gen) { - case SATA_GEN3: - /* - * TX Swing target 550-600mv peak to peak diff - * Tx Slew target 90-110ps rising/falling time - * Rx Eq ON3, Sigdet threshold SDTH1 - */ - val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3; - writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG); - val = SWING_VAL | PREEMPH_VAL; - writeb_relaxed(val, miphy_phy->base + TXBUF1_REG); - writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); - writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG); - val = SDTHRES_VAL | EQ_ON3; - writeb_relaxed(val, miphy_phy->base + RXBUF_REG); - break; - case SATA_GEN2: - /* - * conf gen sel=0x1 to program Gen2 banked registers - * VDDT filter ON - * Tx Swing target 550-600mV peak-to-peak diff - * Tx Slew target 90-110 ps rising/falling time - * RX Equalization ON1, Sigdet threshold SDTH1 - */ - writeb_relaxed(CONF_GEN_SEL_GEN2, - miphy_phy->base + BUF_SEL_REG); - writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG); - writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG); - val = SDTHRES_VAL | EQ_ON1; - writeb_relaxed(val, miphy_phy->base + RXBUF_REG); - break; - case SATA_GEN1: - /* - * conf gen sel = 00b to program Gen1 banked registers - * VDDT filter ON - * Tx Swing target 500-550mV peak-to-peak diff - * Tx Slew target120-140 ps rising/falling time - */ - writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG); - writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG); - writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG); - break; - default: - break; - } - - /* Force Macro1 in partial mode & release pll cal reset */ - writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); - usleep_range(100, 150); - - miphy365x_set_ssc(miphy_phy, miphy_dev); - - /* Wait for phy_ready */ - ret = miphy365x_rdy(miphy_phy, miphy_dev); - if (ret) - return ret; - - /* - * Enable macro1 to use rx_lspd & tx_lspd - * Release Rx_Clock on first I-DLL phase on macro1 - * Assert deserializer reset - * des_bit_lock_en is set - * bit lock detection strength - * Deassert deserializer reset - */ - writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG); - writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG); - writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG); - val = miphy_phy->sata_tx_pol_inv ? - (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN; - writeb_relaxed(val, miphy_phy->base + CTRL_REG); - - val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL; - writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG); - writeb_relaxed(0x00, miphy_phy->base + RESET_REG); - - return 0; -} - -static int miphy365x_init(struct phy *phy) -{ - struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy); - struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent); - int ret = 0; - - mutex_lock(&miphy_dev->miphy_mutex); - - ret = miphy365x_set_path(miphy_phy, miphy_dev); - if (ret) { - mutex_unlock(&miphy_dev->miphy_mutex); - return ret; - } - - /* Initialise Miphy for PCIe or SATA */ - if (miphy_phy->type == PHY_TYPE_PCIE) - ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev); - else - ret = miphy365x_init_sata_port(miphy_phy, miphy_dev); - - mutex_unlock(&miphy_dev->miphy_mutex); - - return ret; -} - -static int miphy365x_get_addr(struct device *dev, - struct miphy365x_phy *miphy_phy, int index) -{ - struct device_node *phynode = miphy_phy->phy->dev.of_node; - const char *name; - int type = miphy_phy->type; - int ret; - - ret = of_property_read_string_index(phynode, "reg-names", index, &name); - if (ret) { - dev_err(dev, "no reg-names property not found\n"); - return ret; - } - - if (!((!strncmp(name, "sata", 4) && type == PHY_TYPE_SATA) || - (!strncmp(name, "pcie", 4) && type == PHY_TYPE_PCIE))) - return 0; - - miphy_phy->base = of_iomap(phynode, index); - if (!miphy_phy->base) { - dev_err(dev, "Failed to map %s\n", phynode->full_name); - return -EINVAL; - } - - return 0; -} - -static struct phy *miphy365x_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev); - struct miphy365x_phy *miphy_phy = NULL; - struct device_node *phynode = args->np; - int ret, index; - - if (args->args_count != 1) { - dev_err(dev, "Invalid number of cells in 'phy' property\n"); - return ERR_PTR(-EINVAL); - } - - for (index = 0; index < miphy_dev->nphys; index++) - if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { - miphy_phy = miphy_dev->phys[index]; - break; - } - - if (!miphy_phy) { - dev_err(dev, "Failed to find appropriate phy\n"); - return ERR_PTR(-EINVAL); - } - - miphy_phy->type = args->args[0]; - - if (!(miphy_phy->type == PHY_TYPE_SATA || - miphy_phy->type == PHY_TYPE_PCIE)) { - dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type); - return ERR_PTR(-EINVAL); - } - - /* Each port handles SATA and PCIE - third entry is always sysconf. */ - for (index = 0; index < 3; index++) { - ret = miphy365x_get_addr(dev, miphy_phy, index); - if (ret < 0) - return ERR_PTR(ret); - } - - return miphy_phy->phy; -} - -static const struct phy_ops miphy365x_ops = { - .init = miphy365x_init, - .owner = THIS_MODULE, -}; - -static int miphy365x_of_probe(struct device_node *phynode, - struct miphy365x_phy *miphy_phy) -{ - of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen); - if (!miphy_phy->sata_gen) - miphy_phy->sata_gen = SATA_GEN1; - - miphy_phy->pcie_tx_pol_inv = - of_property_read_bool(phynode, "st,pcie-tx-pol-inv"); - - miphy_phy->sata_tx_pol_inv = - of_property_read_bool(phynode, "st,sata-tx-pol-inv"); - - return 0; -} - -static int miphy365x_probe(struct platform_device *pdev) -{ - struct device_node *child, *np = pdev->dev.of_node; - struct miphy365x_dev *miphy_dev; - struct phy_provider *provider; - struct phy *phy; - int ret, port = 0; - - miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); - if (!miphy_dev) - return -ENOMEM; - - miphy_dev->nphys = of_get_child_count(np); - miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, - sizeof(*miphy_dev->phys), GFP_KERNEL); - if (!miphy_dev->phys) - return -ENOMEM; - - miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); - if (IS_ERR(miphy_dev->regmap)) { - dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); - return PTR_ERR(miphy_dev->regmap); - } - - miphy_dev->dev = &pdev->dev; - - dev_set_drvdata(&pdev->dev, miphy_dev); - - mutex_init(&miphy_dev->miphy_mutex); - - for_each_child_of_node(np, child) { - struct miphy365x_phy *miphy_phy; - - miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), - GFP_KERNEL); - if (!miphy_phy) { - ret = -ENOMEM; - goto put_child; - } - - miphy_dev->phys[port] = miphy_phy; - - phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops); - if (IS_ERR(phy)) { - dev_err(&pdev->dev, "failed to create PHY\n"); - ret = PTR_ERR(phy); - goto put_child; - } - - miphy_dev->phys[port]->phy = phy; - - ret = miphy365x_of_probe(child, miphy_phy); - if (ret) - goto put_child; - - phy_set_drvdata(phy, miphy_dev->phys[port]); - - port++; - /* sysconfig offsets are indexed from 1 */ - ret = of_property_read_u32_index(np, "st,syscfg", port, - &miphy_phy->ctrlreg); - if (ret) { - dev_err(&pdev->dev, "No sysconfig offset found\n"); - goto put_child; - } - } - - provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); - return PTR_ERR_OR_ZERO(provider); -put_child: - of_node_put(child); - return ret; -} - -static const struct of_device_id miphy365x_of_match[] = { - { .compatible = "st,miphy365x-phy", }, - { }, -}; -MODULE_DEVICE_TABLE(of, miphy365x_of_match); - -static struct platform_driver miphy365x_driver = { - .probe = miphy365x_probe, - .driver = { - .name = "miphy365x-phy", - .of_match_table = miphy365x_of_match, - } -}; -module_platform_driver(miphy365x_driver); - -MODULE_AUTHOR("Alexandre Torgue "); -MODULE_DESCRIPTION("STMicroelectronics miphy365x driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c index 3d97eadd247d..c63da1b955c1 100644 --- a/drivers/phy/phy-rcar-gen3-usb2.c +++ b/drivers/phy/phy-rcar-gen3-usb2.c @@ -70,6 +70,7 @@ #define USB2_LINECTRL1_DP_RPD BIT(18) #define USB2_LINECTRL1_DMRPD_EN BIT(17) #define USB2_LINECTRL1_DM_RPD BIT(16) +#define USB2_LINECTRL1_OPMODE_NODRV BIT(6) /* ADPCTRL */ #define USB2_ADPCTRL_OTGSESSVLD BIT(20) @@ -161,6 +162,43 @@ static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) schedule_work(&ch->work); } +static void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch) +{ + void __iomem *usb2_base = ch->base; + u32 val; + + val = readl(usb2_base + USB2_LINECTRL1); + writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); + + rcar_gen3_set_linectrl(ch, 1, 1); + rcar_gen3_set_host_mode(ch, 1); + rcar_gen3_enable_vbus_ctrl(ch, 0); + + val = readl(usb2_base + USB2_LINECTRL1); + writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); +} + +static void rcar_gen3_init_for_a_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, 1); +} + +static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) +{ + void __iomem *usb2_base = ch->base; + u32 val; + + val = readl(usb2_base + USB2_OBINTEN); + writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); + + rcar_gen3_enable_vbus_ctrl(ch, 0); + rcar_gen3_init_for_host(ch); + + writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); +} + static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) { return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); @@ -174,6 +212,65 @@ static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) rcar_gen3_init_for_peri(ch); } +static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) +{ + return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); +} + +static ssize_t role_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rcar_gen3_chan *ch = dev_get_drvdata(dev); + bool is_b_device, is_host, new_mode_is_host; + + if (!ch->has_otg || !ch->phy->init_count) + return -EIO; + + /* + * is_b_device: true is B-Device. false is A-Device. + * If {new_mode_}is_host: true is Host mode. false is Peripheral mode. + */ + is_b_device = rcar_gen3_check_id(ch); + is_host = rcar_gen3_is_host(ch); + if (!strncmp(buf, "host", strlen("host"))) + new_mode_is_host = true; + else if (!strncmp(buf, "peripheral", strlen("peripheral"))) + new_mode_is_host = false; + else + return -EINVAL; + + /* If current and new mode is the same, this returns the error */ + if (is_host == new_mode_is_host) + return -EINVAL; + + if (new_mode_is_host) { /* And is_host must be false */ + if (!is_b_device) /* A-Peripheral */ + rcar_gen3_init_from_a_peri_to_a_host(ch); + else /* B-Peripheral */ + rcar_gen3_init_for_b_host(ch); + } else { /* And is_host must be true */ + if (!is_b_device) /* A-Host */ + rcar_gen3_init_for_a_peri(ch); + else /* B-Host */ + rcar_gen3_init_for_peri(ch); + } + + return count; +} + +static ssize_t role_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rcar_gen3_chan *ch = dev_get_drvdata(dev); + + if (!ch->has_otg || !ch->phy->init_count) + return -EIO; + + return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : + "peripheral"); +} +static DEVICE_ATTR_RW(role); + static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) { void __iomem *usb2_base = ch->base; @@ -351,21 +448,40 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) channel->vbus = NULL; } + platform_set_drvdata(pdev, channel); phy_set_drvdata(channel->phy, channel); provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(provider)) + if (IS_ERR(provider)) { dev_err(dev, "Failed to register PHY provider\n"); + } else if (channel->has_otg) { + int ret; + + ret = device_create_file(dev, &dev_attr_role); + if (ret < 0) + return ret; + } return PTR_ERR_OR_ZERO(provider); } +static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) +{ + struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); + + if (channel->has_otg) + device_remove_file(&pdev->dev, &dev_attr_role); + + return 0; +}; + 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, + .remove = rcar_gen3_phy_usb2_remove, }; module_platform_driver(rcar_gen3_phy_usb2_driver); diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index fd57345ffed2..f1b24f18e9b2 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -132,7 +132,7 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) default: ideal_rate = 200000000; break; - }; + } diff = (rate > ideal_rate) ? rate - ideal_rate : ideal_rate - rate; diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c index ac203107b071..2f99ec95079c 100644 --- a/drivers/phy/phy-rockchip-inno-usb2.c +++ b/drivers/phy/phy-rockchip-inno-usb2.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -30,11 +31,15 @@ #include #include #include +#include #include #include +#include +#include #define BIT_WRITEABLE_SHIFT 16 -#define SCHEDULE_DELAY (60 * HZ) +#define SCHEDULE_DELAY (60 * HZ) +#define OTG_SCHEDULE_DELAY (2 * HZ) enum rockchip_usb2phy_port_id { USB2PHY_PORT_OTG, @@ -49,6 +54,37 @@ enum rockchip_usb2phy_host_state { PHY_STATE_FS_LS_ONLINE = 4, }; +/** + * Different states involved in USB charger detection. + * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection + * process is not yet started. + * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. + * USB_CHG_STATE_DCD_DONE Data pin contact is detected. + * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects + * between SDP and DCP/CDP). + * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects + * between DCP and CDP). + * USB_CHG_STATE_DETECTED USB charger type is determined. + */ +enum usb_chg_state { + USB_CHG_STATE_UNDEFINED = 0, + USB_CHG_STATE_WAIT_FOR_DCD, + USB_CHG_STATE_DCD_DONE, + USB_CHG_STATE_PRIMARY_DONE, + USB_CHG_STATE_SECONDARY_DONE, + USB_CHG_STATE_DETECTED, +}; + +static const unsigned int rockchip_usb2phy_extcon_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_CHG_USB_SDP, + EXTCON_CHG_USB_CDP, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_SLOW, + EXTCON_NONE, +}; + struct usb2phy_reg { unsigned int offset; unsigned int bitend; @@ -57,20 +93,56 @@ struct usb2phy_reg { unsigned int enable; }; +/** + * struct rockchip_chg_det_reg: usb charger detect registers + * @cp_det: charging port detected successfully. + * @dcp_det: dedicated charging port detected successfully. + * @dp_det: assert data pin connect successfully. + * @idm_sink_en: open dm sink curren. + * @idp_sink_en: open dp sink current. + * @idp_src_en: open dm source current. + * @rdm_pdwn_en: open dm pull down resistor. + * @vdm_src_en: open dm voltage source. + * @vdp_src_en: open dp voltage source. + * @opmode: utmi operational mode. + */ +struct rockchip_chg_det_reg { + struct usb2phy_reg cp_det; + struct usb2phy_reg dcp_det; + struct usb2phy_reg dp_det; + struct usb2phy_reg idm_sink_en; + struct usb2phy_reg idp_sink_en; + struct usb2phy_reg idp_src_en; + struct usb2phy_reg rdm_pdwn_en; + struct usb2phy_reg vdm_src_en; + struct usb2phy_reg vdp_src_en; + struct usb2phy_reg opmode; +}; + /** * struct rockchip_usb2phy_port_cfg: usb-phy port configuration. * @phy_sus: phy suspend register. + * @bvalid_det_en: vbus valid rise detection enable register. + * @bvalid_det_st: vbus valid rise detection status register. + * @bvalid_det_clr: vbus valid rise detection clear register. * @ls_det_en: linestate detection enable register. * @ls_det_st: linestate detection state register. * @ls_det_clr: linestate detection clear register. + * @utmi_avalid: utmi vbus avalid status register. + * @utmi_bvalid: utmi vbus bvalid status register. * @utmi_ls: utmi linestate state register. * @utmi_hstdet: utmi host disconnect register. */ struct rockchip_usb2phy_port_cfg { struct usb2phy_reg phy_sus; + struct usb2phy_reg bvalid_det_en; + struct usb2phy_reg bvalid_det_st; + struct usb2phy_reg bvalid_det_clr; struct usb2phy_reg ls_det_en; struct usb2phy_reg ls_det_st; struct usb2phy_reg ls_det_clr; + struct usb2phy_reg utmi_avalid; + struct usb2phy_reg utmi_bvalid; struct usb2phy_reg utmi_ls; struct usb2phy_reg utmi_hstdet; }; @@ -80,31 +152,51 @@ struct rockchip_usb2phy_port_cfg { * @reg: the address offset of grf for usb-phy config. * @num_ports: specify how many ports that the phy has. * @clkout_ctl: keep on/turn off output clk of phy. + * @chg_det: charger detection registers. */ struct rockchip_usb2phy_cfg { unsigned int reg; unsigned int num_ports; struct usb2phy_reg clkout_ctl; const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; + const struct rockchip_chg_det_reg chg_det; }; /** * struct rockchip_usb2phy_port: usb-phy port data. * @port_id: flag for otg port or host port. * @suspended: phy suspended flag. + * @utmi_avalid: utmi avalid status usage flag. + * true - use avalid to get vbus status + * flase - use bvalid to get vbus status + * @vbus_attached: otg device vbus status. + * @bvalid_irq: IRQ number assigned for vbus valid rise detection. * @ls_irq: IRQ number assigned for linestate detection. * @mutex: for register updating in sm_work. - * @sm_work: OTG state machine work. + * @chg_work: charge detect work. + * @otg_sm_work: OTG state machine work. + * @sm_work: HOST state machine work. * @phy_cfg: port register configuration, assigned by driver data. + * @event_nb: hold event notification callback. + * @state: define OTG enumeration states before device reset. + * @mode: the dr_mode of the controller. */ struct rockchip_usb2phy_port { struct phy *phy; unsigned int port_id; bool suspended; + bool utmi_avalid; + bool vbus_attached; + int bvalid_irq; int ls_irq; struct mutex mutex; + struct delayed_work chg_work; + struct delayed_work otg_sm_work; struct delayed_work sm_work; const struct rockchip_usb2phy_port_cfg *port_cfg; + struct notifier_block event_nb; + enum usb_otg_state state; + enum usb_dr_mode mode; }; /** @@ -113,6 +205,11 @@ struct rockchip_usb2phy_port { * @clk: clock struct of phy input clk. * @clk480m: clock struct of phy output clk. * @clk_hw: clock struct of phy output clk management. + * @chg_state: states involved in USB charger detection. + * @chg_type: USB charger types. + * @dcd_retries: The retry count used to track Data contact + * detection process. + * @edev: extcon device for notification registration * @phy_cfg: phy register configuration, assigned by driver data. * @ports: phy port instance. */ @@ -122,6 +219,10 @@ struct rockchip_usb2phy { struct clk *clk; struct clk *clk480m; struct clk_hw clk480m_hw; + enum usb_chg_state chg_state; + enum power_supply_type chg_type; + u8 dcd_retries; + struct extcon_dev *edev; const struct rockchip_usb2phy_cfg *phy_cfg; struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; }; @@ -153,7 +254,7 @@ static inline bool property_enabled(struct rockchip_usb2phy *rphy, return tmp == reg->enable; } -static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw) +static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = container_of(hw, struct rockchip_usb2phy, clk480m_hw); @@ -165,14 +266,14 @@ static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw) if (ret) return ret; - /* waitting for the clk become stable */ - mdelay(1); + /* waiting for the clk become stable */ + usleep_range(1200, 1300); } return 0; } -static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw) +static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = container_of(hw, struct rockchip_usb2phy, clk480m_hw); @@ -181,7 +282,7 @@ static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw) property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false); } -static int rockchip_usb2phy_clk480m_enabled(struct clk_hw *hw) +static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = container_of(hw, struct rockchip_usb2phy, clk480m_hw); @@ -197,9 +298,9 @@ rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, } static const struct clk_ops rockchip_usb2phy_clkout_ops = { - .enable = rockchip_usb2phy_clk480m_enable, - .disable = rockchip_usb2phy_clk480m_disable, - .is_enabled = rockchip_usb2phy_clk480m_enabled, + .prepare = rockchip_usb2phy_clk480m_prepare, + .unprepare = rockchip_usb2phy_clk480m_unprepare, + .is_prepared = rockchip_usb2phy_clk480m_prepared, .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate, }; @@ -263,33 +364,84 @@ err_ret: return ret; } +static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) +{ + int ret; + struct device_node *node = rphy->dev->of_node; + struct extcon_dev *edev; + + if (of_property_read_bool(node, "extcon")) { + edev = extcon_get_edev_by_phandle(rphy->dev, 0); + if (IS_ERR(edev)) { + if (PTR_ERR(edev) != -EPROBE_DEFER) + dev_err(rphy->dev, "Invalid or missing extcon\n"); + return PTR_ERR(edev); + } + } else { + /* Initialize extcon device */ + edev = devm_extcon_dev_allocate(rphy->dev, + rockchip_usb2phy_extcon_cable); + + if (IS_ERR(edev)) + return -ENOMEM; + + ret = devm_extcon_dev_register(rphy->dev, edev); + if (ret) { + dev_err(rphy->dev, "failed to register extcon device\n"); + return ret; + } + } + + rphy->edev = edev; + + return 0; +} + static int rockchip_usb2phy_init(struct phy *phy) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); - int ret; + int ret = 0; - if (rport->port_id == USB2PHY_PORT_HOST) { - /* clear linestate and enable linestate detect irq */ - mutex_lock(&rport->mutex); + mutex_lock(&rport->mutex); - ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); - if (ret) { - mutex_unlock(&rport->mutex); - return ret; + if (rport->port_id == USB2PHY_PORT_OTG) { + if (rport->mode != USB_DR_MODE_HOST) { + /* clear bvalid status and enable bvalid detect irq */ + ret = property_enable(rphy, + &rport->port_cfg->bvalid_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy, + &rport->port_cfg->bvalid_det_en, + true); + if (ret) + goto out; + + schedule_delayed_work(&rport->otg_sm_work, + OTG_SCHEDULE_DELAY); + } else { + /* If OTG works in host only mode, do nothing. */ + dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); } + } else if (rport->port_id == USB2PHY_PORT_HOST) { + /* clear linestate and enable linestate detect irq */ + ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); + if (ret) + goto out; ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true); - if (ret) { - mutex_unlock(&rport->mutex); - return ret; - } + if (ret) + goto out; - mutex_unlock(&rport->mutex); schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); } - return 0; +out: + mutex_unlock(&rport->mutex); + return ret; } static int rockchip_usb2phy_power_on(struct phy *phy) @@ -340,7 +492,11 @@ static int rockchip_usb2phy_exit(struct phy *phy) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); - if (rport->port_id == USB2PHY_PORT_HOST) + if (rport->port_id == USB2PHY_PORT_OTG && + rport->mode != USB_DR_MODE_HOST) { + cancel_delayed_work_sync(&rport->otg_sm_work); + cancel_delayed_work_sync(&rport->chg_work); + } else if (rport->port_id == USB2PHY_PORT_HOST) cancel_delayed_work_sync(&rport->sm_work); return 0; @@ -354,6 +510,249 @@ static const struct phy_ops rockchip_usb2phy_ops = { .owner = THIS_MODULE, }; +static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) +{ + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, + otg_sm_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + static unsigned int cable; + unsigned long delay; + bool vbus_attach, sch_work, notify_charger; + + if (rport->utmi_avalid) + vbus_attach = + property_enabled(rphy, &rport->port_cfg->utmi_avalid); + else + vbus_attach = + property_enabled(rphy, &rport->port_cfg->utmi_bvalid); + + sch_work = false; + notify_charger = false; + delay = OTG_SCHEDULE_DELAY; + dev_dbg(&rport->phy->dev, "%s otg sm work\n", + usb_otg_state_string(rport->state)); + + switch (rport->state) { + case OTG_STATE_UNDEFINED: + rport->state = OTG_STATE_B_IDLE; + if (!vbus_attach) + rockchip_usb2phy_power_off(rport->phy); + /* fall through */ + case OTG_STATE_B_IDLE: + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) { + dev_dbg(&rport->phy->dev, "usb otg host connect\n"); + rport->state = OTG_STATE_A_HOST; + rockchip_usb2phy_power_on(rport->phy); + return; + } else if (vbus_attach) { + dev_dbg(&rport->phy->dev, "vbus_attach\n"); + switch (rphy->chg_state) { + case USB_CHG_STATE_UNDEFINED: + schedule_delayed_work(&rport->chg_work, 0); + return; + case USB_CHG_STATE_DETECTED: + switch (rphy->chg_type) { + case POWER_SUPPLY_TYPE_USB: + dev_dbg(&rport->phy->dev, + "sdp cable is connecetd\n"); + rockchip_usb2phy_power_on(rport->phy); + rport->state = OTG_STATE_B_PERIPHERAL; + notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_SDP; + break; + case POWER_SUPPLY_TYPE_USB_DCP: + dev_dbg(&rport->phy->dev, + "dcp cable is connecetd\n"); + rockchip_usb2phy_power_off(rport->phy); + notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_DCP; + break; + case POWER_SUPPLY_TYPE_USB_CDP: + dev_dbg(&rport->phy->dev, + "cdp cable is connecetd\n"); + rockchip_usb2phy_power_on(rport->phy); + rport->state = OTG_STATE_B_PERIPHERAL; + notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_CDP; + break; + default: + break; + } + break; + default: + break; + } + } else { + notify_charger = true; + rphy->chg_state = USB_CHG_STATE_UNDEFINED; + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + } + + if (rport->vbus_attached != vbus_attach) { + rport->vbus_attached = vbus_attach; + + if (notify_charger && rphy->edev) + extcon_set_cable_state_(rphy->edev, + cable, vbus_attach); + } + break; + case OTG_STATE_B_PERIPHERAL: + if (!vbus_attach) { + dev_dbg(&rport->phy->dev, "usb disconnect\n"); + rphy->chg_state = USB_CHG_STATE_UNDEFINED; + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + rport->state = OTG_STATE_B_IDLE; + delay = 0; + rockchip_usb2phy_power_off(rport->phy); + } + sch_work = true; + break; + case OTG_STATE_A_HOST: + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { + dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); + rport->state = OTG_STATE_B_IDLE; + rockchip_usb2phy_power_off(rport->phy); + } + break; + default: + break; + } + + if (sch_work) + schedule_delayed_work(&rport->otg_sm_work, delay); +} + +static const char *chg_to_string(enum power_supply_type chg_type) +{ + switch (chg_type) { + case POWER_SUPPLY_TYPE_USB: + return "USB_SDP_CHARGER"; + case POWER_SUPPLY_TYPE_USB_DCP: + return "USB_DCP_CHARGER"; + case POWER_SUPPLY_TYPE_USB_CDP: + return "USB_CDP_CHARGER"; + default: + return "INVALID_CHARGER"; + } +} + +static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, + bool en) +{ + property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en); +} + +static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, + bool en) +{ + property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en); + property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en); +} + +static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, + bool en) +{ + property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en); + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en); +} + +#define CHG_DCD_POLL_TIME (100 * HZ / 1000) +#define CHG_DCD_MAX_RETRIES 6 +#define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) +#define CHG_SECONDARY_DET_TIME (40 * HZ / 1000) +static void rockchip_chg_detect_work(struct work_struct *work) +{ + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, chg_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + bool is_dcd, tmout, vout; + unsigned long delay; + + dev_dbg(&rport->phy->dev, "chg detection work state = %d\n", + rphy->chg_state); + switch (rphy->chg_state) { + case USB_CHG_STATE_UNDEFINED: + if (!rport->suspended) + rockchip_usb2phy_power_off(rport->phy); + /* put the controller in non-driving mode */ + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false); + /* Start DCD processing stage 1 */ + rockchip_chg_enable_dcd(rphy, true); + rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; + rphy->dcd_retries = 0; + delay = CHG_DCD_POLL_TIME; + break; + case USB_CHG_STATE_WAIT_FOR_DCD: + /* get data contact detection status */ + is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det); + tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; + /* stage 2 */ + if (is_dcd || tmout) { + /* stage 4 */ + /* Turn off DCD circuitry */ + rockchip_chg_enable_dcd(rphy, false); + /* Voltage Source on DP, Probe on DM */ + rockchip_chg_enable_primary_det(rphy, true); + delay = CHG_PRIMARY_DET_TIME; + rphy->chg_state = USB_CHG_STATE_DCD_DONE; + } else { + /* stage 3 */ + delay = CHG_DCD_POLL_TIME; + } + break; + case USB_CHG_STATE_DCD_DONE: + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det); + rockchip_chg_enable_primary_det(rphy, false); + if (vout) { + /* Voltage Source on DM, Probe on DP */ + rockchip_chg_enable_secondary_det(rphy, true); + delay = CHG_SECONDARY_DET_TIME; + rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE; + } else { + if (rphy->dcd_retries == CHG_DCD_MAX_RETRIES) { + /* floating charger found */ + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; + rphy->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } else { + rphy->chg_type = POWER_SUPPLY_TYPE_USB; + rphy->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } + } + break; + case USB_CHG_STATE_PRIMARY_DONE: + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det); + /* Turn off voltage source */ + rockchip_chg_enable_secondary_det(rphy, false); + if (vout) + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; + else + rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; + /* fall through */ + case USB_CHG_STATE_SECONDARY_DONE: + rphy->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + /* fall through */ + case USB_CHG_STATE_DETECTED: + /* put the controller in normal mode */ + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true); + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + dev_info(&rport->phy->dev, "charger = %s\n", + chg_to_string(rphy->chg_type)); + return; + default: + return; + } + + schedule_delayed_work(&rport->chg_work, delay); +} + /* * The function manage host-phy port state and suspend/resume phy port * to save power. @@ -485,6 +884,26 @@ static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) +{ + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + + if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* clear bvalid detect irq pending status */ + property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true); + + mutex_unlock(&rport->mutex); + + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + + return IRQ_HANDLED; +} + static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) @@ -509,13 +928,86 @@ static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, IRQF_ONESHOT, "rockchip_usb2phy", rport); if (ret) { - dev_err(rphy->dev, "failed to request irq handle\n"); + dev_err(rphy->dev, "failed to request linestate irq handle\n"); return ret; } return 0; } +static int rockchip_otg_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct rockchip_usb2phy_port *rport = + container_of(nb, struct rockchip_usb2phy_port, event_nb); + + schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY); + + return NOTIFY_DONE; +} + +static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + struct device_node *child_np) +{ + int ret; + + rport->port_id = USB2PHY_PORT_OTG; + rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; + rport->state = OTG_STATE_UNDEFINED; + + /* + * set suspended flag to true, but actually don't + * put phy in suspend mode, it aims to enable usb + * phy and clock in power_on() called by usb controller + * driver during probe. + */ + rport->suspended = true; + rport->vbus_attached = false; + + mutex_init(&rport->mutex); + + rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); + if (rport->mode == USB_DR_MODE_HOST) { + ret = 0; + goto out; + } + + INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); + INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); + + rport->utmi_avalid = + of_property_read_bool(child_np, "rockchip,utmi-avalid"); + + rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); + if (rport->bvalid_irq < 0) { + dev_err(rphy->dev, "no vbus valid irq provided\n"); + ret = rport->bvalid_irq; + goto out; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL, + rockchip_usb2phy_bvalid_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_bvalid", rport); + if (ret) { + dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n"); + goto out; + } + + if (!IS_ERR(rphy->edev)) { + rport->event_nb.notifier_call = rockchip_otg_event; + + ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, + &rport->event_nb); + if (ret) + dev_err(rphy->dev, "register USB HOST notifier failed\n"); + } + +out: + return ret; +} + static int rockchip_usb2phy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -553,8 +1045,14 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) rphy->dev = dev; phy_cfgs = match->data; + rphy->chg_state = USB_CHG_STATE_UNDEFINED; + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; platform_set_drvdata(pdev, rphy); + ret = rockchip_usb2phy_extcon_register(rphy); + if (ret) + return ret; + /* find out a proper config which can be matched with dt. */ index = 0; while (phy_cfgs[index].reg) { @@ -591,13 +1089,9 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) struct rockchip_usb2phy_port *rport = &rphy->ports[index]; struct phy *phy; - /* - * This driver aim to support both otg-port and host-port, - * but unfortunately, the otg part is not ready in current, - * so this comments and below codes are interim, which should - * be changed after otg-port is supplied soon. - */ - if (of_node_cmp(child_np->name, "host-port")) + /* This driver aims to support both otg-port and host-port */ + if (of_node_cmp(child_np->name, "host-port") && + of_node_cmp(child_np->name, "otg-port")) goto next_child; phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); @@ -610,9 +1104,18 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) rport->phy = phy; phy_set_drvdata(rport->phy, rport); - ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np); - if (ret) - goto put_child; + /* initialize otg/host port separately */ + if (!of_node_cmp(child_np->name, "host-port")) { + ret = rockchip_usb2phy_host_port_init(rphy, rport, + child_np); + if (ret) + goto put_child; + } else { + ret = rockchip_usb2phy_otg_port_init(rphy, rport, + child_np); + if (ret) + goto put_child; + } next_child: /* to prevent out of boundary */ @@ -654,10 +1157,18 @@ static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { { - .reg = 0xe450, + .reg = 0xe450, .num_ports = 2, .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0xe454, 1, 0, 2, 1 }, + .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, + .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, + .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, + .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, + .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, + }, [USB2PHY_PORT_HOST] = { .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, .ls_det_en = { 0xe3c0, 6, 6, 0, 1 }, @@ -667,12 +1178,32 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 } } }, + .chg_det = { + .opmode = { 0xe454, 3, 0, 5, 1 }, + .cp_det = { 0xe2ac, 2, 2, 0, 1 }, + .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, + .dp_det = { 0xe2ac, 0, 0, 0, 1 }, + .idm_sink_en = { 0xe450, 8, 8, 0, 1 }, + .idp_sink_en = { 0xe450, 7, 7, 0, 1 }, + .idp_src_en = { 0xe450, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 }, + .vdm_src_en = { 0xe450, 12, 12, 0, 1 }, + .vdp_src_en = { 0xe450, 11, 11, 0, 1 }, + }, }, { - .reg = 0xe460, + .reg = 0xe460, .num_ports = 2, .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0xe464, 1, 0, 2, 1 }, + .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, + .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, + .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, + .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, + .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, + }, [USB2PHY_PORT_HOST] = { .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, .ls_det_en = { 0xe3c0, 11, 11, 0, 1 }, diff --git a/drivers/phy/phy-s5pv210-usb2.c b/drivers/phy/phy-s5pv210-usb2.c index 004d320767e4..f6f72339bbc3 100644 --- a/drivers/phy/phy-s5pv210-usb2.c +++ b/drivers/phy/phy-s5pv210-usb2.c @@ -103,7 +103,7 @@ static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) break; default: return; - }; + } regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, mask, on ? 0 : mask); @@ -127,7 +127,7 @@ static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) rstbits = S5PV210_URSTCON_PHY1_ALL | S5PV210_URSTCON_HOST_LINK_ALL; break; - }; + } if (on) { writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c deleted file mode 100644 index 0ac74639ad02..000000000000 --- a/drivers/phy/phy-stih41x-usb.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2014 STMicroelectronics - * - * STMicroelectronics PHY driver for STiH41x USB. - * - * Author: Maxime Coquelin - * - * 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 -#include -#include -#include - -#define SYSCFG332 0x80 -#define SYSCFG2520 0x820 - -/** - * struct stih41x_usb_cfg - SoC specific PHY register mapping - * @syscfg: Offset in syscfg registers bank - * @cfg_mask: Bits mask for PHY configuration - * @cfg: Static configuration value for PHY - * @oscok: Notify the PHY oscillator clock is ready - * Setting this bit enable the PHY - */ -struct stih41x_usb_cfg { - u32 syscfg; - u32 cfg_mask; - u32 cfg; - u32 oscok; -}; - -/** - * struct stih41x_usb_phy - Private data for the PHY - * @dev: device for this controller - * @regmap: Syscfg registers bank in which PHY is configured - * @cfg: SoC specific PHY register mapping - * @clk: Oscillator used by the PHY - */ -struct stih41x_usb_phy { - struct device *dev; - struct regmap *regmap; - const struct stih41x_usb_cfg *cfg; - struct clk *clk; -}; - -static struct stih41x_usb_cfg stih415_usb_phy_cfg = { - .syscfg = SYSCFG332, - .cfg_mask = 0x3f, - .cfg = 0x38, - .oscok = BIT(6), -}; - -static struct stih41x_usb_cfg stih416_usb_phy_cfg = { - .syscfg = SYSCFG2520, - .cfg_mask = 0x33f, - .cfg = 0x238, - .oscok = BIT(6), -}; - -static int stih41x_usb_phy_init(struct phy *phy) -{ - struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy); - - return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg, - phy_dev->cfg->cfg_mask, phy_dev->cfg->cfg); -} - -static int stih41x_usb_phy_power_on(struct phy *phy) -{ - struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy); - int ret; - - ret = clk_prepare_enable(phy_dev->clk); - if (ret) { - dev_err(phy_dev->dev, "Failed to enable osc_phy clock\n"); - return ret; - } - - ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg, - phy_dev->cfg->oscok, phy_dev->cfg->oscok); - if (ret) - clk_disable_unprepare(phy_dev->clk); - - return ret; -} - -static int stih41x_usb_phy_power_off(struct phy *phy) -{ - struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy); - int ret; - - ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg, - phy_dev->cfg->oscok, 0); - if (ret) { - dev_err(phy_dev->dev, "Failed to clear oscok bit\n"); - return ret; - } - - clk_disable_unprepare(phy_dev->clk); - - return 0; -} - -static const struct phy_ops stih41x_usb_phy_ops = { - .init = stih41x_usb_phy_init, - .power_on = stih41x_usb_phy_power_on, - .power_off = stih41x_usb_phy_power_off, - .owner = THIS_MODULE, -}; - -static const struct of_device_id stih41x_usb_phy_of_match[]; - -static int stih41x_usb_phy_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - const struct of_device_id *match; - struct stih41x_usb_phy *phy_dev; - struct device *dev = &pdev->dev; - struct phy_provider *phy_provider; - struct phy *phy; - - phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); - if (!phy_dev) - return -ENOMEM; - - match = of_match_device(stih41x_usb_phy_of_match, &pdev->dev); - if (!match) - return -ENODEV; - - phy_dev->cfg = match->data; - - phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); - if (IS_ERR(phy_dev->regmap)) { - dev_err(dev, "No syscfg phandle specified\n"); - return PTR_ERR(phy_dev->regmap); - } - - phy_dev->clk = devm_clk_get(dev, "osc_phy"); - if (IS_ERR(phy_dev->clk)) { - dev_err(dev, "osc_phy clk not found\n"); - return PTR_ERR(phy_dev->clk); - } - - phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops); - - if (IS_ERR(phy)) { - dev_err(dev, "failed to create phy\n"); - return PTR_ERR(phy); - } - - phy_dev->dev = dev; - - phy_set_drvdata(phy, phy_dev); - - 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 stih41x_usb_phy_of_match[] = { - { .compatible = "st,stih415-usb-phy", .data = &stih415_usb_phy_cfg }, - { .compatible = "st,stih416-usb-phy", .data = &stih416_usb_phy_cfg }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, stih41x_usb_phy_of_match); - -static struct platform_driver stih41x_usb_phy_driver = { - .probe = stih41x_usb_phy_probe, - .driver = { - .name = "stih41x-usb-phy", - .of_match_table = stih41x_usb_phy_of_match, - } -}; -module_platform_driver(stih41x_usb_phy_driver); - -MODULE_AUTHOR("Maxime Coquelin "); -MODULE_DESCRIPTION("STMicroelectronics USB PHY driver for STiH41x series"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index fec34f5213c4..bf28a0fdd569 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -436,25 +436,31 @@ static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode) { struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + int new_mode; if (phy->index != 0) return -EINVAL; switch (mode) { case PHY_MODE_USB_HOST: - data->dr_mode = USB_DR_MODE_HOST; + new_mode = USB_DR_MODE_HOST; break; case PHY_MODE_USB_DEVICE: - data->dr_mode = USB_DR_MODE_PERIPHERAL; + new_mode = USB_DR_MODE_PERIPHERAL; break; case PHY_MODE_USB_OTG: - data->dr_mode = USB_DR_MODE_OTG; + new_mode = USB_DR_MODE_OTG; break; default: return -EINVAL; } - dev_info(&_phy->dev, "Changing dr_mode to %d\n", (int)data->dr_mode); + if (new_mode != data->dr_mode) { + dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); + data->dr_mode = new_mode; + } + + data->id_det = -1; /* Force reprocessing of id */ data->force_session_end = true; queue_delayed_work(system_wq, &data->detect, 0); diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index bf46844dc387..9c84d32c6f60 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -537,10 +537,7 @@ static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) 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); - - return 0; + return PTR_ERR_OR_ZERO(phy->pll_ctrl_base); } static int ti_pipe3_probe(struct platform_device *pdev) @@ -592,10 +589,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) 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); - - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static int ti_pipe3_remove(struct platform_device *pdev) diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 547ca7b3f098..2990b3965460 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -317,6 +317,9 @@ static enum musb_vbus_id_status linkstat = MUSB_VBUS_OFF; } + kobject_uevent(&twl->dev->kobj, linkstat == MUSB_VBUS_VALID + ? KOBJ_ONLINE : KOBJ_OFFLINE); + dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", status, status, linkstat); diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c index 119957249a51..c45cbedc6634 100644 --- a/drivers/phy/tegra/xusb-tegra124.c +++ b/drivers/phy/tegra/xusb-tegra124.c @@ -1483,7 +1483,6 @@ static int tegra124_usb3_port_enable(struct tegra_xusb_port *port) struct tegra_xusb_padctl *padctl = port->padctl; struct tegra_xusb_lane *lane = usb3->base.lane; unsigned int index = port->index, offset; - int ret = 0; u32 value; value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP); @@ -1612,7 +1611,7 @@ static int tegra124_usb3_port_enable(struct tegra_xusb_port *port) value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(index); padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM); - return ret; + return 0; } static void tegra124_usb3_port_disable(struct tegra_xusb_port *port) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 873424ab0e32..3cbcb2537657 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -561,10 +561,7 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2) usb2->internal = of_property_read_bool(np, "nvidia,internal"); usb2->supply = devm_regulator_get(&port->dev, "vbus"); - if (IS_ERR(usb2->supply)) - return PTR_ERR(usb2->supply); - - return 0; + return PTR_ERR_OR_ZERO(usb2->supply); } static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl, @@ -731,10 +728,7 @@ static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) usb3->internal = of_property_read_bool(np, "nvidia,internal"); usb3->supply = devm_regulator_get(&port->dev, "vbus"); - if (IS_ERR(usb3->supply)) - return PTR_ERR(usb3->supply); - - return 0; + return PTR_ERR_OR_ZERO(usb3->supply); } static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,