From: Amelie Delaunay <amelie.delaunay@foss.st.com> To: Kishon Vijay Abraham I <kishon@ti.com>, Vinod Koul <vkoul@kernel.org>, Rob Herring <robh+dt@kernel.org>, Alexandre Torgue <alexandre.torgue@foss.st.com>, Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: <linux-kernel@vger.kernel.org>, <devicetree@vger.kernel.org>, <linux-arm-kernel@lists.infradead.org>, <linux-stm32@st-md-mailman.stormreply.com>, Amelie Delaunay <amelie.delaunay@foss.st.com> Subject: [PATCH v2 6/6] phy: stm32: rework PLL Lock detection Date: Tue, 5 Jan 2021 10:05:25 +0100 [thread overview] Message-ID: <20210105090525.23164-7-amelie.delaunay@foss.st.com> (raw) In-Reply-To: <20210105090525.23164-1-amelie.delaunay@foss.st.com> USBPHYC has a register per phy to control and monitor the debug interface of the HS PHY through a digital debug access. With this register, it is possible to know if PLL Lock input to phy is high. That means the PLL is ready for HS operation. Instead of using an hard-coded delay after PLL enable and PLL disable, use this bit to ensure good operating of the HS PHY. Also use an atomic counter (n_pll_cons) to count the actual number of PLL consumers and get rid of stm32_usbphyc_has_one_phy_active. The boolean active in the usbphyc_phy structure is kept, because we need to know in remove if a phy_exit is required to properly disable the PLL. Signed-off-by: Amelie Delaunay <amelie.delaunay@foss.st.com> --- drivers/phy/st/phy-stm32-usbphyc.c | 88 ++++++++++++++++++------------ 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 8b11d95b2c20..d08fbb180e43 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -17,6 +17,7 @@ #define STM32_USBPHYC_PLL 0x0 #define STM32_USBPHYC_MISC 0x8 +#define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100)) #define STM32_USBPHYC_VERSION 0x3F4 /* STM32_USBPHYC_PLL bit fields */ @@ -32,12 +33,16 @@ /* STM32_USBPHYC_MISC bit fields */ #define SWITHOST BIT(0) +/* 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_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 @@ -64,6 +69,7 @@ struct stm32_usbphyc { int nphys; struct regulator *vdda1v1; struct regulator *vdda1v8; + atomic_t n_pll_cons; int switch_setup; }; @@ -171,35 +177,27 @@ 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) { - void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; - - /* Check if other phy port active */ - if (stm32_usbphyc_has_one_phy_active(usbphyc)) + /* Check if a phy port is still active or clk48 in use */ + if (atomic_dec_return(&usbphyc->n_pll_cons) > 0) return 0; - 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 stm32_usbphyc_regulators_disable(usbphyc); + return __stm32_usbphyc_pll_disable(usbphyc); } static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc) @@ -208,39 +206,43 @@ static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc) 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) { - ret = stm32_usbphyc_pll_disable(usbphyc); + /* + * 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) - return ret; + goto dec_n_pll_cons; ret = stm32_usbphyc_pll_init(usbphyc); if (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"); - ret = -EIO; - goto reg_disable; - } return 0; reg_disable: stm32_usbphyc_regulators_disable(usbphyc); +dec_n_pll_cons: + atomic_dec(&usbphyc->n_pll_cons); + return ret; } @@ -248,15 +250,33 @@ 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) -- 2.17.1
WARNING: multiple messages have this Message-ID (diff)
From: Amelie Delaunay <amelie.delaunay@foss.st.com> To: Kishon Vijay Abraham I <kishon@ti.com>, Vinod Koul <vkoul@kernel.org>, Rob Herring <robh+dt@kernel.org>, Alexandre Torgue <alexandre.torgue@foss.st.com>, Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Amelie Delaunay <amelie.delaunay@foss.st.com>, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-stm32@st-md-mailman.stormreply.com Subject: [PATCH v2 6/6] phy: stm32: rework PLL Lock detection Date: Tue, 5 Jan 2021 10:05:25 +0100 [thread overview] Message-ID: <20210105090525.23164-7-amelie.delaunay@foss.st.com> (raw) In-Reply-To: <20210105090525.23164-1-amelie.delaunay@foss.st.com> USBPHYC has a register per phy to control and monitor the debug interface of the HS PHY through a digital debug access. With this register, it is possible to know if PLL Lock input to phy is high. That means the PLL is ready for HS operation. Instead of using an hard-coded delay after PLL enable and PLL disable, use this bit to ensure good operating of the HS PHY. Also use an atomic counter (n_pll_cons) to count the actual number of PLL consumers and get rid of stm32_usbphyc_has_one_phy_active. The boolean active in the usbphyc_phy structure is kept, because we need to know in remove if a phy_exit is required to properly disable the PLL. Signed-off-by: Amelie Delaunay <amelie.delaunay@foss.st.com> --- drivers/phy/st/phy-stm32-usbphyc.c | 88 ++++++++++++++++++------------ 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 8b11d95b2c20..d08fbb180e43 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -17,6 +17,7 @@ #define STM32_USBPHYC_PLL 0x0 #define STM32_USBPHYC_MISC 0x8 +#define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100)) #define STM32_USBPHYC_VERSION 0x3F4 /* STM32_USBPHYC_PLL bit fields */ @@ -32,12 +33,16 @@ /* STM32_USBPHYC_MISC bit fields */ #define SWITHOST BIT(0) +/* 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_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 @@ -64,6 +69,7 @@ struct stm32_usbphyc { int nphys; struct regulator *vdda1v1; struct regulator *vdda1v8; + atomic_t n_pll_cons; int switch_setup; }; @@ -171,35 +177,27 @@ 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) { - void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; - - /* Check if other phy port active */ - if (stm32_usbphyc_has_one_phy_active(usbphyc)) + /* Check if a phy port is still active or clk48 in use */ + if (atomic_dec_return(&usbphyc->n_pll_cons) > 0) return 0; - 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 stm32_usbphyc_regulators_disable(usbphyc); + return __stm32_usbphyc_pll_disable(usbphyc); } static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc) @@ -208,39 +206,43 @@ static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc) 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) { - ret = stm32_usbphyc_pll_disable(usbphyc); + /* + * 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) - return ret; + goto dec_n_pll_cons; ret = stm32_usbphyc_pll_init(usbphyc); if (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"); - ret = -EIO; - goto reg_disable; - } return 0; reg_disable: stm32_usbphyc_regulators_disable(usbphyc); +dec_n_pll_cons: + atomic_dec(&usbphyc->n_pll_cons); + return ret; } @@ -248,15 +250,33 @@ 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) -- 2.17.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next prev parent reply other threads:[~2021-01-05 9:06 UTC|newest] Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-01-05 9:05 [PATCH v2 0/6] STM32 USBPHYC PLL management rework Amelie Delaunay 2021-01-05 9:05 ` Amelie Delaunay 2021-01-05 9:05 ` [PATCH v2 1/6] dt-bindings: phy: phy-stm32-usbphyc: move PLL supplies to parent node Amelie Delaunay 2021-01-05 9:05 ` Amelie Delaunay 2021-01-05 9:05 ` [PATCH v2 2/6] phy: stm32: manage 1v1 and 1v8 supplies at pll activation/deactivation Amelie Delaunay 2021-01-05 9:05 ` Amelie Delaunay 2021-01-05 9:05 ` [PATCH v2 3/6] phy: stm32: replace regulator_bulk* by multiple regulator_* Amelie Delaunay 2021-01-05 9:05 ` Amelie Delaunay 2021-01-05 9:05 ` [PATCH v2 4/6] phy: stm32: ensure pll is disabled before phys creation Amelie Delaunay 2021-01-05 9:05 ` Amelie Delaunay 2021-01-05 9:05 ` [PATCH v2 5/6] phy: stm32: ensure phy are no more active when removing the driver Amelie Delaunay 2021-01-05 9:05 ` Amelie Delaunay 2021-01-05 9:05 ` Amelie Delaunay [this message] 2021-01-05 9:05 ` [PATCH v2 6/6] phy: stm32: rework PLL Lock detection Amelie Delaunay 2021-01-13 15:10 ` [PATCH v2 0/6] STM32 USBPHYC PLL management rework Vinod Koul 2021-01-13 15:10 ` Vinod Koul
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20210105090525.23164-7-amelie.delaunay@foss.st.com \ --to=amelie.delaunay@foss.st.com \ --cc=alexandre.torgue@foss.st.com \ --cc=devicetree@vger.kernel.org \ --cc=kishon@ti.com \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-stm32@st-md-mailman.stormreply.com \ --cc=mcoquelin.stm32@gmail.com \ --cc=robh+dt@kernel.org \ --cc=vkoul@kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.