1374 lines
40 KiB
Diff
1374 lines
40 KiB
Diff
From 731c37c7d36e13cac98e1c872657c03613fc5259 Mon Sep 17 00:00:00 2001
|
|
From: Romuald JEANNE <romuald.jeanne@st.com>
|
|
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 <romuald.jeanne@st.com>
|
|
---
|
|
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 <linux/bitfield.h>
|
|
#include <linux/clk.h>
|
|
+#include <linux/clk-provider.h>
|
|
#include <linux/delay.h>
|
|
-#include <linux/io.h>
|
|
+#include <linux/iopoll.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_platform.h>
|
|
@@ -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 <amelie.delaunay@st.com>
|
|
*/
|
|
|
|
+#include <linux/clk.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/usb/role.h>
|
|
@@ -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 <linux/usb/hcd.h>
|
|
#include <linux/usb/ehci_pdriver.h>
|
|
#include <linux/usb/of.h>
|
|
+#include <linux/pm_wakeirq.h>
|
|
|
|
#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
|
|
|