From 731c37c7d36e13cac98e1c872657c03613fc5259 Mon Sep 17 00:00:00 2001 From: Romuald JEANNE Date: Tue, 16 Mar 2021 09:14:10 +0100 Subject: [PATCH 15/22] ARM 5.10.10-stm32mp1-r1 PHY-USB Signed-off-by: Romuald JEANNE --- drivers/phy/st/phy-stm32-usbphyc.c | 499 +++++++++++++++++++++++++---- drivers/usb/core/hcd.c | 9 +- drivers/usb/core/phy.c | 22 +- drivers/usb/core/phy.h | 6 +- drivers/usb/dwc2/core.c | 123 ++++--- drivers/usb/dwc2/core.h | 4 + drivers/usb/dwc2/drd.c | 21 +- drivers/usb/dwc2/gadget.c | 5 +- drivers/usb/dwc2/hcd.c | 6 +- drivers/usb/dwc2/params.c | 8 + drivers/usb/dwc2/platform.c | 47 ++- drivers/usb/host/ehci-platform.c | 16 +- drivers/usb/typec/stusb160x.c | 11 +- 13 files changed, 615 insertions(+), 162 deletions(-) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 2b3639cba51a..5f169d5c669e 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -7,8 +7,9 @@ */ #include #include +#include #include -#include +#include #include #include #include @@ -17,6 +18,8 @@ #define STM32_USBPHYC_PLL 0x0 #define STM32_USBPHYC_MISC 0x8 +#define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100)) +#define STM32_USBPHYC_TUNE(X) (0x10C + ((X) * 0x100)) #define STM32_USBPHYC_VERSION 0x3F4 /* STM32_USBPHYC_PLL bit fields */ @@ -32,19 +35,93 @@ /* STM32_USBPHYC_MISC bit fields */ #define SWITHOST BIT(0) -/* STM32_USBPHYC_VERSION bit fields */ -#define MINREV GENMASK(3, 0) -#define MAJREV GENMASK(7, 4) +/* STM32_USBPHYC_MONITOR bit fields */ +#define STM32_USBPHYC_MON_OUT GENMASK(3, 0) +#define STM32_USBPHYC_MON_SEL GENMASK(8, 4) +#define STM32_USBPHYC_MON_SEL_LOCKP 0x1F +#define STM32_USBPHYC_MON_OUT_LOCKP BIT(3) + +/* STM32_USBPHYC_TUNE bit fields */ +#define INCURREN BIT(0) +#define INCURRINT BIT(1) +#define LFSCAPEN BIT(2) +#define HSDRVSLEW BIT(3) +#define HSDRVDCCUR BIT(4) +#define HSDRVDCLEV BIT(5) +#define HSDRVCURINCR BIT(6) +#define FSDRVRFADJ BIT(7) +#define HSDRVRFRED BIT(8) +#define HSDRVCHKITRM GENMASK(12, 9) +#define HSDRVCHKZTRM GENMASK(14, 13) +#define OTPCOMP GENMASK(19, 15) +#define SQLCHCTL GENMASK(21, 20) +#define HDRXGNEQEN BIT(22) +#define HSRXOFF GENMASK(24, 23) +#define HSFALLPREEM BIT(25) +#define SHTCCTCTLPROT BIT(26) +#define STAGSEL BIT(27) + +enum boosting_vals { + BOOST_1_MA = 1, + BOOST_2_MA, + BOOST_MAX, +}; + +enum dc_level_vals { + DC_MINUS_5_TO_7_MV, + DC_PLUS_5_TO_7_MV, + DC_PLUS_10_TO_14_MV, + DC_MAX, +}; + +enum current_trim { + CUR_NOMINAL, + CUR_PLUS_1_56_PCT, + CUR_PLUS_3_12_PCT, + CUR_PLUS_4_68_PCT, + CUR_PLUS_6_24_PCT, + CUR_PLUS_7_8_PCT, + CUR_PLUS_9_36_PCT, + CUR_PLUS_10_92_PCT, + CUR_PLUS_12_48_PCT, + CUR_PLUS_14_04_PCT, + CUR_PLUS_15_6_PCT, + CUR_PLUS_17_16_PCT, + CUR_PLUS_19_01_PCT, + CUR_PLUS_20_58_PCT, + CUR_PLUS_22_16_PCT, + CUR_PLUS_23_73_PCT, + CUR_MAX, +}; + +enum impedance_trim { + IMP_NOMINAL, + IMP_MINUS_2_OHMS, + IMP_MINUS_4_OMHS, + IMP_MINUS_6_OHMS, + IMP_MAX, +}; + +enum squelch_level { + SQLCH_NOMINAL, + SQLCH_PLUS_7_MV, + SQLCH_MINUS_5_MV, + SQLCH_PLUS_14_MV, + SQLCH_MAX, +}; -static const char * const supplies_names[] = { - "vdda1v1", /* 1V1 */ - "vdda1v8", /* 1V8 */ +enum rx_offset { + NO_RX_OFFSET, + RX_OFFSET_PLUS_5_MV, + RX_OFFSET_PLUS_10_MV, + RX_OFFSET_MINUS_5_MV, + RX_OFFSET_MAX, }; -#define NUM_SUPPLIES ARRAY_SIZE(supplies_names) +/* STM32_USBPHYC_VERSION bit fields */ +#define MINREV GENMASK(3, 0) +#define MAJREV GENMASK(7, 4) -#define PLL_LOCK_TIME_US 100 -#define PLL_PWR_DOWN_TIME_US 5 #define PLL_FVCO_MHZ 2880 #define PLL_INFF_MIN_RATE_HZ 19200000 #define PLL_INFF_MAX_RATE_HZ 38400000 @@ -58,7 +135,7 @@ struct pll_params { struct stm32_usbphyc_phy { struct phy *phy; struct stm32_usbphyc *usbphyc; - struct regulator_bulk_data supplies[NUM_SUPPLIES]; + struct regulator *vbus; u32 index; bool active; }; @@ -70,6 +147,10 @@ struct stm32_usbphyc { struct reset_control *rst; struct stm32_usbphyc_phy **phys; int nphys; + struct regulator *vdda1v1; + struct regulator *vdda1v8; + atomic_t n_pll_cons; + struct clk_hw clk48_hw; int switch_setup; }; @@ -83,6 +164,41 @@ static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits) writel_relaxed(readl_relaxed(reg) & ~bits, reg); } +static int stm32_usbphyc_regulators_enable(struct stm32_usbphyc *usbphyc) +{ + int ret; + + ret = regulator_enable(usbphyc->vdda1v1); + if (ret) + return ret; + + ret = regulator_enable(usbphyc->vdda1v8); + if (ret) + goto vdda1v1_disable; + + return 0; + +vdda1v1_disable: + regulator_disable(usbphyc->vdda1v1); + + return ret; +} + +static int stm32_usbphyc_regulators_disable(struct stm32_usbphyc *usbphyc) +{ + int ret; + + ret = regulator_disable(usbphyc->vdda1v8); + if (ret) + return ret; + + ret = regulator_disable(usbphyc->vdda1v1); + if (ret) + return ret; + + return 0; +} + static void stm32_usbphyc_get_pll_params(u32 clk_rate, struct pll_params *pll_params) { @@ -142,83 +258,106 @@ static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc) return 0; } -static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc) +static int __stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) { - int i; + void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; + u32 pllen; + + stm32_usbphyc_clr_bits(pll_reg, PLLEN); - for (i = 0; i < usbphyc->nphys; i++) - if (usbphyc->phys[i]->active) - return true; + /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ + if (readl_relaxed_poll_timeout(pll_reg, pllen, !(pllen & PLLEN), 5, 50)) + dev_err(usbphyc->dev, "PLL not reset\n"); - return false; + return stm32_usbphyc_regulators_disable(usbphyc); +} + +static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) +{ + /* Check if a phy port is still active or clk48 in use */ + if (atomic_dec_return(&usbphyc->n_pll_cons) > 0) + return 0; + + return __stm32_usbphyc_pll_disable(usbphyc); } static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc) { void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; - bool pllen = (readl_relaxed(pll_reg) & PLLEN); + bool pllen = readl_relaxed(pll_reg) & PLLEN; int ret; - /* Check if one phy port has already configured the pll */ - if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc)) + /* + * Check if a phy port or clk48 prepare has configured the pll + * and ensure the PLL is enabled + */ + if (atomic_inc_return(&usbphyc->n_pll_cons) > 1 && pllen) return 0; if (pllen) { - stm32_usbphyc_clr_bits(pll_reg, PLLEN); - /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ - udelay(PLL_PWR_DOWN_TIME_US); + /* + * PLL shouldn't be enabled without known consumer, + * disable it and reinit n_pll_cons + */ + dev_warn(usbphyc->dev, "PLL enabled without known consumers\n"); + + ret = __stm32_usbphyc_pll_disable(usbphyc); + if (ret) + return ret; } + ret = stm32_usbphyc_regulators_enable(usbphyc); + if (ret) + goto dec_n_pll_cons; + ret = stm32_usbphyc_pll_init(usbphyc); if (ret) - return ret; + goto reg_disable; stm32_usbphyc_set_bits(pll_reg, PLLEN); - /* Wait for maximum lock time */ - udelay(PLL_LOCK_TIME_US); - - if (!(readl_relaxed(pll_reg) & PLLEN)) { - dev_err(usbphyc->dev, "PLLEN not set\n"); - return -EIO; - } - return 0; -} -static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) -{ - void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; +reg_disable: + stm32_usbphyc_regulators_disable(usbphyc); - /* Check if other phy port active */ - if (stm32_usbphyc_has_one_phy_active(usbphyc)) - return 0; +dec_n_pll_cons: + atomic_dec(&usbphyc->n_pll_cons); - stm32_usbphyc_clr_bits(pll_reg, PLLEN); - /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ - udelay(PLL_PWR_DOWN_TIME_US); - - if (readl_relaxed(pll_reg) & PLLEN) { - dev_err(usbphyc->dev, "PLL not reset\n"); - return -EIO; - } - - return 0; + return ret; } static int stm32_usbphyc_phy_init(struct phy *phy) { struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc; + u32 reg_mon = STM32_USBPHYC_MONITOR(usbphyc_phy->index); + u32 monsel = FIELD_PREP(STM32_USBPHYC_MON_SEL, + STM32_USBPHYC_MON_SEL_LOCKP); + u32 monout; int ret; ret = stm32_usbphyc_pll_enable(usbphyc); if (ret) return ret; + /* Check that PLL Lock input to PHY is High */ + writel_relaxed(monsel, usbphyc->base + reg_mon); + ret = readl_relaxed_poll_timeout(usbphyc->base + reg_mon, monout, + (monout & STM32_USBPHYC_MON_OUT_LOCKP), + 100, 1000); + if (ret) { + dev_err(usbphyc->dev, "PLL Lock input to PHY is Low (val=%x)\n", + (u32)(monout & STM32_USBPHYC_MON_OUT)); + goto pll_disable; + } + usbphyc_phy->active = true; return 0; + +pll_disable: + return stm32_usbphyc_pll_disable(usbphyc); } static int stm32_usbphyc_phy_exit(struct phy *phy) @@ -235,14 +374,20 @@ static int stm32_usbphyc_phy_power_on(struct phy *phy) { struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); - return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies); + if (usbphyc_phy->vbus) + return regulator_enable(usbphyc_phy->vbus); + + return 0; } static int stm32_usbphyc_phy_power_off(struct phy *phy) { struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); - return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies); + if (usbphyc_phy->vbus) + return regulator_disable(usbphyc_phy->vbus); + + return 0; } static const struct phy_ops stm32_usbphyc_phy_ops = { @@ -253,6 +398,162 @@ static const struct phy_ops stm32_usbphyc_phy_ops = { .owner = THIS_MODULE, }; +static int stm32_usbphyc_clk48_prepare(struct clk_hw *hw) +{ + struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, + clk48_hw); + + return stm32_usbphyc_pll_enable(usbphyc); +} + +static void stm32_usbphyc_clk48_unprepare(struct clk_hw *hw) +{ + struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, + clk48_hw); + + stm32_usbphyc_pll_disable(usbphyc); +} + +static unsigned long stm32_usbphyc_clk48_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 48000000; +} + +static const struct clk_ops usbphyc_clk48_ops = { + .prepare = stm32_usbphyc_clk48_prepare, + .unprepare = stm32_usbphyc_clk48_unprepare, + .recalc_rate = stm32_usbphyc_clk48_recalc_rate, +}; + +static void stm32_usbphyc_clk48_unregister(void *data) +{ + struct stm32_usbphyc *usbphyc = data; + + of_clk_del_provider(usbphyc->dev->of_node); + clk_hw_unregister(&usbphyc->clk48_hw); +} + +static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc) +{ + struct device_node *node = usbphyc->dev->of_node; + struct clk_init_data init = { }; + int ret = 0; + + init.name = "ck_usbo_48m"; + init.ops = &usbphyc_clk48_ops; + + usbphyc->clk48_hw.init = &init; + + ret = clk_hw_register(usbphyc->dev, &usbphyc->clk48_hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, + &usbphyc->clk48_hw); + if (ret) + clk_hw_unregister(&usbphyc->clk48_hw); + + return ret; +} + +static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc, + struct device_node *np, u32 index) +{ + struct device_node *tune_np; + u32 reg = STM32_USBPHYC_TUNE(index); + u32 otpcomp, val, tune = 0; + int ret; + + tune_np = of_parse_phandle(np, "st,phy-tuning", 0); + if (!tune_np) + return; + + /* Backup OTP compensation code */ + otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg)); + + ret = of_property_read_u32(tune_np, "st,current-boost", &val); + if (!ret && val < BOOST_MAX) { + val = (val == BOOST_2_MA) ? 1 : 0; + tune |= INCURREN | FIELD_PREP(INCURRINT, val); + } else if (ret != -EINVAL) { + dev_warn(usbphyc->dev, + "phy%d: invalid st,current-boost value\n", index); + } + + if (!of_property_read_bool(tune_np, "st,no-lsfs-fb-cap")) + tune |= LFSCAPEN; + + if (of_property_read_bool(tune_np, "st,hs-slew-ctrl")) + tune |= HSDRVSLEW; + + ret = of_property_read_u32(tune_np, "st,hs-dc-level", &val); + if (!ret && val < DC_MAX) { + if (val == DC_MINUS_5_TO_7_MV) { + tune |= HSDRVDCCUR; + } else { + val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0; + tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val); + } + } else if (ret != -EINVAL) { + dev_warn(usbphyc->dev, + "phy%d: invalid st,hs-dc-level value\n", index); + } + + if (of_property_read_bool(tune_np, "st,fs-rftime-tuning")) + tune |= FSDRVRFADJ; + + if (of_property_read_bool(tune_np, "st,hs-rftime-reduction")) + tune |= HSDRVRFRED; + + ret = of_property_read_u32(tune_np, "st,hs-current-trim", &val); + if (!ret && val < CUR_MAX) + tune |= FIELD_PREP(HSDRVCHKITRM, val); + else if (ret != -EINVAL) + dev_warn(usbphyc->dev, + "phy%d: invalid st,hs-current-trim value\n", index); + + ret = of_property_read_u32(tune_np, "st,hs-impedance-trim", &val); + if (!ret && val < IMP_MAX) + tune |= FIELD_PREP(HSDRVCHKZTRM, val); + else if (ret != -EINVAL) + dev_warn(usbphyc->dev, + "phy%d: invalid hs-impedance-trim value\n", index); + + ret = of_property_read_u32(tune_np, "st,squelch-level", &val); + if (!ret && val < SQLCH_MAX) + tune |= FIELD_PREP(SQLCHCTL, val); + else if (ret != -EINVAL) + dev_warn(usbphyc->dev, + "phy%d: invalid st,squelch-level value\n", index); + + if (of_property_read_bool(tune_np, "st,hs-rx-gain-eq")) + tune |= HDRXGNEQEN; + + ret = of_property_read_u32(tune_np, "st,hs-rx-offset", &val); + if (!ret && val < RX_OFFSET_MAX) + tune |= FIELD_PREP(HSRXOFF, val); + else if (ret != -EINVAL) + dev_warn(usbphyc->dev, + "phy%d: invalid st,hs-rx-offset value\n", index); + + if (of_property_read_bool(tune_np, "st,no-hs-ftime-ctrl")) + tune |= HSFALLPREEM; + + if (!of_property_read_bool(tune_np, "st,no-lsfs-sc")) + tune |= SHTCCTCTLPROT; + + if (of_property_read_bool(tune_np, "st,hs-tx-staggering")) + tune |= STAGSEL; + + of_node_put(tune_np); + + /* Restore OTP compensation code */ + tune |= FIELD_PREP(OTPCOMP, otpcomp); + + writel_relaxed(tune, usbphyc->base + reg); +} + static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc, u32 utmi_switch) { @@ -313,7 +614,7 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) struct device_node *child, *np = dev->of_node; struct resource *res; struct phy_provider *phy_provider; - u32 version; + u32 pllen, version; int ret, port = 0; usbphyc = devm_kzalloc(dev, sizeof(*usbphyc), GFP_KERNEL); @@ -328,11 +629,8 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) return PTR_ERR(usbphyc->base); usbphyc->clk = devm_clk_get(dev, NULL); - if (IS_ERR(usbphyc->clk)) { - ret = PTR_ERR(usbphyc->clk); - dev_err(dev, "clk get failed: %d\n", ret); - return ret; - } + if (IS_ERR(usbphyc->clk)) + return dev_err_probe(dev, PTR_ERR(usbphyc->clk), "clk get failed\n"); ret = clk_prepare_enable(usbphyc->clk); if (ret) { @@ -345,6 +643,23 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) reset_control_assert(usbphyc->rst); udelay(2); reset_control_deassert(usbphyc->rst); + } else { + ret = PTR_ERR(usbphyc->rst); + if (ret == -EPROBE_DEFER) + goto clk_disable; + + stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_PLL, PLLEN); + } + + /* + * Wait for minimum width of powerdown pulse (ENABLE = Low): + * we have to ensure the PLL is disabled before phys initialization. + */ + if (readl_relaxed_poll_timeout(usbphyc->base + STM32_USBPHYC_PLL, + pllen, !(pllen & PLLEN), 5, 50)) { + dev_warn(usbphyc->dev, "PLL not reset\n"); + ret = -EPROBE_DEFER; + goto clk_disable; } usbphyc->switch_setup = -EINVAL; @@ -356,11 +671,26 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) goto clk_disable; } + usbphyc->vdda1v1 = devm_regulator_get(dev, "vdda1v1"); + if (IS_ERR(usbphyc->vdda1v1)) { + ret = PTR_ERR(usbphyc->vdda1v1); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get vdda1v1 supply: %d\n", ret); + goto clk_disable; + } + + usbphyc->vdda1v8 = devm_regulator_get(dev, "vdda1v8"); + if (IS_ERR(usbphyc->vdda1v8)) { + ret = PTR_ERR(usbphyc->vdda1v8); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get vdda1v8 supply: %d\n", ret); + goto clk_disable; + } + for_each_child_of_node(np, child) { struct stm32_usbphyc_phy *usbphyc_phy; struct phy *phy; u32 index; - int i; phy = devm_phy_create(dev, child, &stm32_usbphyc_phy_ops); if (IS_ERR(phy)) { @@ -378,24 +708,15 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) goto put_child; } - for (i = 0; i < NUM_SUPPLIES; i++) - usbphyc_phy->supplies[i].supply = supplies_names[i]; - - ret = devm_regulator_bulk_get(&phy->dev, NUM_SUPPLIES, - usbphyc_phy->supplies); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&phy->dev, - "failed to get regulators: %d\n", ret); - goto put_child; - } - ret = of_property_read_u32(child, "reg", &index); if (ret || index > usbphyc->nphys) { dev_err(&phy->dev, "invalid reg property: %d\n", ret); goto put_child; } + /* Configure phy tuning */ + stm32_usbphyc_phy_tuning(usbphyc, child, index); + usbphyc->phys[port] = usbphyc_phy; phy_set_bus_width(phy, 8); phy_set_drvdata(phy, usbphyc_phy); @@ -405,6 +726,14 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) usbphyc->phys[port]->index = index; usbphyc->phys[port]->active = false; + usbphyc->phys[port]->vbus = devm_regulator_get_optional(&phy->dev, "vbus"); + if (IS_ERR(usbphyc->phys[port]->vbus)) { + ret = PTR_ERR(usbphyc->phys[port]->vbus); + if (ret == -EPROBE_DEFER) + goto put_child; + usbphyc->phys[port]->vbus = NULL; + } + port++; } @@ -416,6 +745,13 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) goto clk_disable; } + ret = stm32_usbphyc_clk48_register(usbphyc); + if (ret) { + dev_err(dev, + "failed to register ck_usbo_48m clock: %d\n", ret); + goto clk_disable; + } + version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION); dev_info(dev, "registered rev:%lu.%lu\n", FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version)); @@ -433,12 +769,34 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) static int stm32_usbphyc_remove(struct platform_device *pdev) { struct stm32_usbphyc *usbphyc = dev_get_drvdata(&pdev->dev); + int port; + + /* Ensure PHYs are not active, to allow PLL disabling */ + for (port = 0; port < usbphyc->nphys; port++) + if (usbphyc->phys[port]->active) + stm32_usbphyc_phy_exit(usbphyc->phys[port]->phy); + + stm32_usbphyc_clk48_unregister(usbphyc); clk_disable_unprepare(usbphyc->clk); return 0; } +#ifdef CONFIG_PM_SLEEP +static int stm32_usbphyc_resume(struct device *dev) +{ + struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev); + + if (usbphyc->switch_setup >= 0) + stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(stm32_usbphyc_pm_ops, NULL, stm32_usbphyc_resume); + static const struct of_device_id stm32_usbphyc_of_match[] = { { .compatible = "st,stm32mp1-usbphyc", }, { }, @@ -451,6 +809,7 @@ static struct platform_driver stm32_usbphyc_driver = { .driver = { .of_match_table = stm32_usbphyc_of_match, .name = "stm32-usbphyc", + .pm = &stm32_usbphyc_pm_ops, } }; module_platform_driver(stm32_usbphyc_driver); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 2c6b9578a7d3..7765fb216128 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2131,7 +2131,8 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) if (!PMSG_IS_AUTO(msg)) usb_phy_roothub_suspend(hcd->self.sysdev, - hcd->phy_roothub); + hcd->phy_roothub, + usb_wakeup_enabled_descendants(rhdev)); /* Did we race with a root-hub wakeup event? */ if (rhdev->do_remote_wakeup) { @@ -2172,7 +2173,8 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) if (!PMSG_IS_AUTO(msg)) { status = usb_phy_roothub_resume(hcd->self.sysdev, - hcd->phy_roothub); + hcd->phy_roothub, + usb_wakeup_enabled_descendants(rhdev)); if (status) return status; } @@ -2217,7 +2219,8 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) } } else { hcd->state = old_state; - usb_phy_roothub_suspend(hcd->self.sysdev, hcd->phy_roothub); + usb_phy_roothub_suspend(hcd->self.sysdev, hcd->phy_roothub, + usb_wakeup_enabled_descendants(rhdev)); dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "resume", status); if (status != -ESHUTDOWN) diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c index fb1588e7c282..746615aa1b2d 100644 --- a/drivers/usb/core/phy.c +++ b/drivers/usb/core/phy.c @@ -212,34 +212,36 @@ void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub) EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off); int usb_phy_roothub_suspend(struct device *controller_dev, - struct usb_phy_roothub *phy_roothub) + struct usb_phy_roothub *phy_roothub, + unsigned wakeup_enabled_descendants) { - usb_phy_roothub_power_off(phy_roothub); - /* keep the PHYs initialized so the device can wake up the system */ - if (device_may_wakeup(controller_dev)) + if (device_may_wakeup(controller_dev) || wakeup_enabled_descendants) return 0; + usb_phy_roothub_power_off(phy_roothub); + return usb_phy_roothub_exit(phy_roothub); } EXPORT_SYMBOL_GPL(usb_phy_roothub_suspend); int usb_phy_roothub_resume(struct device *controller_dev, - struct usb_phy_roothub *phy_roothub) + struct usb_phy_roothub *phy_roothub, + unsigned wakeup_enabled_descendants) { - int err; + int err = 0; /* if the device can't wake up the system _exit was called */ - if (!device_may_wakeup(controller_dev)) { + if (!device_may_wakeup(controller_dev) && !wakeup_enabled_descendants) { err = usb_phy_roothub_init(phy_roothub); if (err) return err; - } - err = usb_phy_roothub_power_on(phy_roothub); + err = usb_phy_roothub_power_on(phy_roothub); + } /* undo _init if _power_on failed */ - if (err && !device_may_wakeup(controller_dev)) + if (err && !device_may_wakeup(controller_dev) && !wakeup_enabled_descendants) usb_phy_roothub_exit(phy_roothub); return err; diff --git a/drivers/usb/core/phy.h b/drivers/usb/core/phy.h index 20a267cd986b..3df4ddbb6046 100644 --- a/drivers/usb/core/phy.h +++ b/drivers/usb/core/phy.h @@ -23,8 +23,10 @@ int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub); void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub); int usb_phy_roothub_suspend(struct device *controller_dev, - struct usb_phy_roothub *phy_roothub); + struct usb_phy_roothub *phy_roothub, + unsigned wakeup_enabled_descendants); int usb_phy_roothub_resume(struct device *controller_dev, - struct usb_phy_roothub *phy_roothub); + struct usb_phy_roothub *phy_roothub, + unsigned wakeup_enabled_descendants); #endif /* __USB_CORE_PHY_H_ */ diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index fec17a2d2447..6f9236d4dd38 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -83,6 +83,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) gr->pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1); gr->glpmcfg = dwc2_readl(hsotg, GLPMCFG); gr->gi2cctl = dwc2_readl(hsotg, GI2CCTL); + gr->ggpio = dwc2_readl(hsotg, GGPIO); gr->pcgcctl = dwc2_readl(hsotg, PCGCTL); gr->valid = true; @@ -112,21 +113,82 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) gr->valid = false; dwc2_writel(hsotg, 0xffffffff, GINTSTS); + dwc2_writel(hsotg, gr->gahbcfg, GAHBCFG); + dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); dwc2_writel(hsotg, gr->gotgctl, GOTGCTL); dwc2_writel(hsotg, gr->gintmsk, GINTMSK); - dwc2_writel(hsotg, gr->gusbcfg, GUSBCFG); - dwc2_writel(hsotg, gr->gahbcfg, GAHBCFG); dwc2_writel(hsotg, gr->grxfsiz, GRXFSIZ); dwc2_writel(hsotg, gr->gnptxfsiz, GNPTXFSIZ); dwc2_writel(hsotg, gr->gdfifocfg, GDFIFOCFG); dwc2_writel(hsotg, gr->pcgcctl1, PCGCCTL1); dwc2_writel(hsotg, gr->glpmcfg, GLPMCFG); dwc2_writel(hsotg, gr->pcgcctl, PCGCTL); + dwc2_writel(hsotg, gr->ggpio, GGPIO); dwc2_writel(hsotg, gr->gi2cctl, GI2CCTL); return 0; } +int dwc2_backup_registers(struct dwc2_hsotg *hsotg) +{ + int ret; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + } + + return 0; +} + +int dwc2_restore_registers(struct dwc2_hsotg *hsotg) +{ + int ret; + + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_restore_device_registers(hsotg, 0); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + } + + return 0; +} + /** * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down. * @@ -136,7 +198,6 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) { u32 pcgcctl; - int ret = 0; if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) return -ENOTSUPP; @@ -154,31 +215,11 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) dwc2_writel(hsotg, pcgcctl, PCGCTL); udelay(100); - if (restore) { - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_restore_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_restore_device_registers(hsotg, 0); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore device registers\n", - __func__); - return ret; - } - } - } - return ret; + if (restore) + return dwc2_restore_registers(hsotg); + + return 0; } /** @@ -189,34 +230,14 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) { u32 pcgcctl; - int ret = 0; + int ret; if (!hsotg->params.power_down) return -ENOTSUPP; - /* Backup all registers */ - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); + ret = dwc2_backup_registers(hsotg); + if (ret) return ret; - } - - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_backup_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_backup_device_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup device registers\n", - __func__); - return ret; - } - } /* * Clear any pending interrupts since dwc2 will not be able to @@ -238,7 +259,7 @@ int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) pcgcctl |= PCGCTL_STOPPCLK; dwc2_writel(hsotg, pcgcctl, PCGCTL); - return ret; + return 0; } /** diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 7161344c6522..80bd2900e20a 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -685,6 +685,7 @@ struct dwc2_hw_params { * @grxfsiz: Backup of GRXFSIZ register * @gnptxfsiz: Backup of GNPTXFSIZ register * @gi2cctl: Backup of GI2CCTL register + * @ggpio: Backup of GGPIO register * @glpmcfg: Backup of GLPMCFG register * @gdfifocfg: Backup of GDFIFOCFG register * @pcgcctl: Backup of PCGCCTL register @@ -701,6 +702,7 @@ struct dwc2_gregs_backup { u32 grxfsiz; u32 gnptxfsiz; u32 gi2cctl; + u32 ggpio; u32 glpmcfg; u32 pcgcctl; u32 pcgcctl1; @@ -1329,6 +1331,8 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd); void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, int is_host); +int dwc2_backup_registers(struct dwc2_hsotg *hsotg); +int dwc2_restore_registers(struct dwc2_hsotg *hsotg); int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg); diff --git a/drivers/usb/dwc2/drd.c b/drivers/usb/dwc2/drd.c index 2d4176f5788e..27202eb05bde 100644 --- a/drivers/usb/dwc2/drd.c +++ b/drivers/usb/dwc2/drd.c @@ -7,6 +7,7 @@ * Author(s): Amelie Delaunay */ +#include #include #include #include @@ -25,9 +26,9 @@ static void dwc2_ovr_init(struct dwc2_hsotg *hsotg) gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL); dwc2_writel(hsotg, gotgctl, GOTGCTL); - dwc2_force_mode(hsotg, false); - spin_unlock_irqrestore(&hsotg->lock, flags); + + dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST)); } static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid) @@ -86,6 +87,19 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) } #endif + /* + * In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of + * the probe and enabled on udc_start. + * If role-switch set is called before the udc_start, we need to enable + * the clock to read/write GOTGCTL and GUSBCFG registers to override + * mode and sessions. It is the case if cable is plugged at boot. + */ + if (!hsotg->ll_hw_enabled && hsotg->clk) { + int ret = clk_prepare_enable(hsotg->clk); + if (ret) + return ret; + } + spin_lock_irqsave(&hsotg->lock, flags); if (role == USB_ROLE_HOST) { @@ -110,6 +124,9 @@ static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role) /* This will raise a Connector ID Status Change Interrupt */ dwc2_force_mode(hsotg, role == USB_ROLE_HOST); + if (!hsotg->ll_hw_enabled && hsotg->clk) + clk_disable_unprepare(hsotg->clk); + dev_dbg(hsotg->dev, "%s-session valid\n", role == USB_ROLE_NONE ? "No" : role == USB_ROLE_HOST ? "A" : "B"); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 0a0d11151cfb..780187454251 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3565,7 +3565,8 @@ void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) { /* remove the soft-disconnect and let's go */ - dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); + if (!hsotg->role_sw || (dwc2_readl(hsotg, GOTGCTL) & GOTGCTL_BSESVLD)) + dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); } /** @@ -4982,7 +4983,7 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) hsotg->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock_irqrestore(&hsotg->lock, flags); - for (ep = 0; ep < hsotg->num_of_eps; ep++) { + for (ep = 1; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); if (hsotg->eps_out[ep]) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index e9ac215b9663..a953f7b2e3a3 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1735,7 +1735,8 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) * release_channel_ddma(), which is called from ep_disable when * device disconnects */ - channel->qh = NULL; + if (hsotg->params.host_dma && hsotg->params.dma_desc_enable) + channel->qh = NULL; } /* All channels have been freed, mark them available */ if (hsotg->params.uframe_sched) { @@ -3611,7 +3612,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, if (wvalue != USB_PORT_FEAT_TEST && (!windex || windex > 1)) goto error; - if (!hsotg->flags.b.port_connect_status) { + if (!hsotg->flags.b.port_connect_status && + !dwc2_is_host_mode(hsotg)) { /* * The port is disconnected, which means the core is * either in device mode or it soon will be. Just diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 267543c3dc38..92df3d620f7d 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -177,7 +177,10 @@ static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg) p->i2c_enable = false; p->activate_stm_fs_transceiver = true; p->activate_stm_id_vb_detection = true; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + p->host_support_fs_ls_low_power = true; + p->host_ls_low_power_phy_clk = true; } static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg) @@ -189,7 +192,12 @@ static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg) p->host_rx_fifo_size = 440; p->host_nperio_tx_fifo_size = 256; p->host_perio_tx_fifo_size = 256; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT; p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + p->lpm = false; + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; } const struct of_device_id dwc2_of_match_table[] = { diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 5f18acac7406..f0964babf260 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -224,8 +224,7 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) hsotg->reset = devm_reset_control_get_optional(hsotg->dev, "dwc2"); if (IS_ERR(hsotg->reset)) { ret = PTR_ERR(hsotg->reset); - dev_err(hsotg->dev, "error getting reset control %d\n", ret); - return ret; + return dev_err_probe(hsotg->dev, ret, "error getting reset control\n"); } reset_control_deassert(hsotg->reset); @@ -233,8 +232,7 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc"); if (IS_ERR(hsotg->reset_ecc)) { ret = PTR_ERR(hsotg->reset_ecc); - dev_err(hsotg->dev, "error getting reset control for ecc %d\n", ret); - return ret; + return dev_err_probe(hsotg->dev, ret, "error getting reset control for ecc\n"); } reset_control_deassert(hsotg->reset_ecc); @@ -283,8 +281,8 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) /* Clock */ hsotg->clk = devm_clk_get_optional(hsotg->dev, "otg"); if (IS_ERR(hsotg->clk)) { - dev_err(hsotg->dev, "cannot get otg clock\n"); - return PTR_ERR(hsotg->clk); + ret = PTR_ERR(hsotg->clk); + return dev_err_probe(hsotg->dev, ret, "cannot get otg clock\n"); } /* Regulators */ @@ -293,12 +291,9 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies), hsotg->supplies); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(hsotg->dev, "failed to request supplies: %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(hsotg->dev, ret, "failed to request supplies\n"); + return 0; } @@ -638,6 +633,19 @@ static int __maybe_unused dwc2_suspend(struct device *dev) dwc2_drd_suspend(dwc2); + if (dwc2->params.power_down == DWC2_POWER_DOWN_PARAM_NONE) { + /* + * Backup host registers when power_down param is 'none', if + * controller power is disabled. + * This shouldn't be needed, when using other power_down modes. + */ + ret = dwc2_backup_registers(dwc2); + if (ret) { + dev_err(dwc2->dev, "backup regs failed %d\n", ret); + return ret; + } + } + if (dwc2->params.activate_stm_id_vb_detection) { unsigned long flags; u32 ggpio, gotgctl; @@ -675,6 +683,9 @@ static int __maybe_unused dwc2_suspend(struct device *dev) dwc2->phy_off_for_suspend = true; } + if (device_may_wakeup(dev) || device_wakeup_path(dev)) + enable_irq_wake(dwc2->irq); + return ret; } @@ -683,6 +694,9 @@ static int __maybe_unused dwc2_resume(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; + if (device_may_wakeup(dev) || device_wakeup_path(dev)) + disable_irq_wake(dwc2->irq); + if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { ret = __dwc2_lowlevel_hw_enable(dwc2); if (ret) @@ -715,8 +729,13 @@ static int __maybe_unused dwc2_resume(struct device *dev) spin_unlock_irqrestore(&dwc2->lock, flags); } - /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ - dwc2_force_dr_mode(dwc2); + if (dwc2->params.power_down == DWC2_POWER_DOWN_PARAM_NONE) { + ret = dwc2_restore_registers(dwc2); + if (ret) { + dev_err(dwc2->dev, "restore regs failed %d\n", ret); + return ret; + } + } dwc2_drd_resume(dwc2); diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index a48dd3fac153..5017913ce6cb 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "ehci.h" @@ -364,7 +365,9 @@ static int ehci_platform_probe(struct platform_device *dev) if (err) goto err_power; - device_wakeup_enable(hcd->self.controller); + if (of_property_read_bool(dev->dev.of_node, "wakeup-source")) + device_set_wakeup_capable(hcd->self.controller, true); + device_enable_async_suspend(hcd->self.controller); platform_set_drvdata(dev, hcd); @@ -400,6 +403,9 @@ static int ehci_platform_remove(struct platform_device *dev) if (priv->quirk_poll) quirk_poll_end(priv); + if (of_property_read_bool(dev->dev.of_node, "wakeup-source")) + device_set_wakeup_capable(hcd->self.controller, false); + usb_remove_hcd(hcd); if (pdata->power_off) @@ -424,7 +430,7 @@ static int __maybe_unused ehci_platform_suspend(struct device *dev) struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = to_platform_device(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); - bool do_wakeup = device_may_wakeup(dev); + bool do_wakeup = device_may_wakeup(dev) || device_wakeup_path(dev); int ret; if (priv->quirk_poll) @@ -437,6 +443,9 @@ static int __maybe_unused ehci_platform_suspend(struct device *dev) if (pdata->power_suspend) pdata->power_suspend(pdev); + if (do_wakeup) + enable_irq_wake(hcd->irq); + return ret; } @@ -448,6 +457,9 @@ static int __maybe_unused ehci_platform_resume(struct device *dev) struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); struct device *companion_dev; + if (device_may_wakeup(dev) || device_wakeup_path(dev)) + disable_irq_wake(hcd->irq); + if (pdata->power_on) { int err = pdata->power_on(pdev); if (err < 0) diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index d21750bbbb44..a7b51bf4af18 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -739,10 +739,6 @@ static int stusb160x_probe(struct i2c_client *client) typec_set_pwr_opmode(chip->port, chip->pwr_opmode); if (client->irq) { - ret = stusb160x_irq_init(chip, client->irq); - if (ret) - goto port_unregister; - chip->role_sw = fwnode_usb_role_switch_get(fwnode); if (IS_ERR(chip->role_sw)) { ret = PTR_ERR(chip->role_sw); @@ -752,6 +748,10 @@ static int stusb160x_probe(struct i2c_client *client) ret); goto port_unregister; } + + ret = stusb160x_irq_init(chip, client->irq); + if (ret) + goto role_sw_put; } else { /* * If Source or Dual power role, need to enable VDD supply @@ -775,6 +775,9 @@ static int stusb160x_probe(struct i2c_client *client) return 0; +role_sw_put: + if (chip->role_sw) + usb_role_switch_put(chip->role_sw); port_unregister: typec_unregister_port(chip->port); all_reg_disable: -- 2.17.1