drivers: usb: stm32: fix support of STM32U5 OTG_HS with embedded PHY

Introduce new binding "st,stm32u5-otghs-phy" for OTG_HS PHY. This allows to
configure clock source and handle STM32U5 specific OTG_HS PHY behavior in
driver implementation in a more readable way.

Move OTG_HS PHY clock selection (previously <&rcc STM32_SRC_HSI48
ICKLK_SEL(0)>) from OTG_HS node to OTG_HS PHY node.

Rename USBPHYC_SEL -> OTGHS_SEL which matches the definition in the stm32u5
CCIPR2 register (RM0456 Rev 5, Section 11.8.47).

Support enabling OTG_HS PHY clock, which is bit 15 (OTGHSPHYEN) in
RCC_AHB2ENR1. Change OTG_HS clock to be bit 14 (OTGEN).

Calculate in runtime OTG_HS PHY clock source frequency. Try to match that
to supported (16, 19.2, 20, 24, 26, 32 MHz) frequencies and select proper
option with HAL_SYSCFG_SetOTGPHYReferenceClockSelection() API (instead of
hardcoded 16 MHz selection).

Co-authored-by: Adrian Chadd <adrian.chadd@meta.com>
Signed-off-by: Adrian Chadd <adrian.chadd@meta.com>
Signed-off-by: Marcin Niestroj <m.niestroj@emb.dev>
This commit is contained in:
Marcin Niestroj 2024-12-04 12:36:19 +01:00 committed by Benjamin Cabé
parent fafaa58240
commit f72ef5c237
5 changed files with 142 additions and 17 deletions

View file

@ -62,7 +62,7 @@ config USB_DC_STM32
config USB_DC_STM32_CLOCK_CHECK config USB_DC_STM32_CLOCK_CHECK
bool "Runtime USB 48MHz clock check" bool "Runtime USB 48MHz clock check"
depends on USB_DC_STM32 depends on USB_DC_STM32
default y if !(SOC_SERIES_STM32F1X || SOC_SERIES_STM32F3X) default y if !(SOC_SERIES_STM32F1X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32U5X)
help help
Enable USB clock 48MHz configuration runtime check. Enable USB clock 48MHz configuration runtime check.
In specific cases, this check might provide wrong verdict and should In specific cases, this check might provide wrong verdict and should

View file

@ -208,16 +208,67 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
} }
#endif #endif
static int usb_dc_stm32_clock_enable(void) #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_otghs_phy)
{
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
if (!device_is_ready(clk)) { static const struct stm32_pclken phy_pclken[] = STM32_DT_CLOCKS(DT_INST_PHANDLE(0, phys));
LOG_ERR("clock control device not ready");
return -ENODEV; static int usb_dc_stm32u5_phy_clock_select(const struct device *const clk)
{
static const struct {
uint32_t freq;
uint32_t ref_clk;
} clk_select[] = {
{ MHZ(16), SYSCFG_OTG_HS_PHY_CLK_SELECT_1 },
{ KHZ(19200), SYSCFG_OTG_HS_PHY_CLK_SELECT_2 },
{ MHZ(20), SYSCFG_OTG_HS_PHY_CLK_SELECT_3 },
{ MHZ(24), SYSCFG_OTG_HS_PHY_CLK_SELECT_4 },
{ MHZ(26), SYSCFG_OTG_HS_PHY_CLK_SELECT_5 },
{ MHZ(32), SYSCFG_OTG_HS_PHY_CLK_SELECT_6 },
};
uint32_t freq;
if (clock_control_get_rate(clk,
(clock_control_subsys_t)&phy_pclken[1],
&freq) != 0) {
LOG_ERR("Failed to get USB_PHY clock source rate");
return -EIO;
} }
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) && defined(CONFIG_SOC_SERIES_STM32U5X) for (size_t i = 0; ARRAY_SIZE(clk_select); i++) {
if (clk_select[i].freq == freq) {
HAL_SYSCFG_SetOTGPHYReferenceClockSelection(clk_select[i].ref_clk);
return 0;
}
}
LOG_ERR("Unsupported PHY clock source frequency (%"PRIu32")", freq);
return -EINVAL;
}
static int usb_dc_stm32u5_phy_clock_enable(const struct device *const clk)
{
int err;
err = clock_control_configure(clk, (clock_control_subsys_t)&phy_pclken[1], NULL);
if (err) {
LOG_ERR("Could not select USB_PHY clock source");
return -EIO;
}
err = clock_control_on(clk, (clock_control_subsys_t)&phy_pclken[0]);
if (err) {
LOG_ERR("Unable to enable USB_PHY clock");
return -EIO;
}
return usb_dc_stm32u5_phy_clock_select(clk);
}
static int usb_dc_stm32_phy_specific_clock_enable(const struct device *const clk)
{
int err;
/* Sequence to enable the power of the OTG HS on a stm32U5 serie : Enable VDDUSB */ /* Sequence to enable the power of the OTG HS on a stm32U5 serie : Enable VDDUSB */
bool pwr_clk = LL_AHB3_GRP1_IsEnabledClock(LL_AHB3_GRP1_PERIPH_PWR); bool pwr_clk = LL_AHB3_GRP1_IsEnabledClock(LL_AHB3_GRP1_PERIPH_PWR);
@ -246,10 +297,28 @@ static int usb_dc_stm32_clock_enable(void)
/* Set the OTG PHY reference clock selection (through SYSCFG) block */ /* Set the OTG PHY reference clock selection (through SYSCFG) block */
LL_APB3_GRP1_EnableClock(LL_APB3_GRP1_PERIPH_SYSCFG); LL_APB3_GRP1_EnableClock(LL_APB3_GRP1_PERIPH_SYSCFG);
HAL_SYSCFG_SetOTGPHYReferenceClockSelection(SYSCFG_OTG_HS_PHY_CLK_SELECT_1);
err = usb_dc_stm32u5_phy_clock_enable(clk);
if (err) {
return err;
}
/* Configuring the SYSCFG registers OTG_HS PHY : OTG_HS PHY enable*/ /* Configuring the SYSCFG registers OTG_HS PHY : OTG_HS PHY enable*/
HAL_SYSCFG_EnableOTGPHY(SYSCFG_OTG_HS_PHY_ENABLE); HAL_SYSCFG_EnableOTGPHY(SYSCFG_OTG_HS_PHY_ENABLE);
#elif defined(PWR_USBSCR_USB33SV) || defined(PWR_SVMCR_USV)
if (clock_control_on(clk, (clock_control_subsys_t)&pclken[0]) != 0) {
LOG_ERR("Unable to enable USB clock");
return -EIO;
}
return 0;
}
#else /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_otghs_phy) */
static int usb_dc_stm32_phy_specific_clock_enable(const struct device *const clk)
{
#if defined(PWR_USBSCR_USB33SV) || defined(PWR_SVMCR_USV)
/* /*
* VDDUSB independent USB supply (PWR clock is on) * VDDUSB independent USB supply (PWR clock is on)
* with LL_PWR_EnableVDDUSB function (higher case) * with LL_PWR_EnableVDDUSB function (higher case)
@ -286,6 +355,26 @@ static int usb_dc_stm32_clock_enable(void)
} }
} }
return 0;
}
#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_otghs_phy) */
static int usb_dc_stm32_clock_enable(void)
{
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
int err;
if (!device_is_ready(clk)) {
LOG_ERR("clock control device not ready");
return -ENODEV;
}
err = usb_dc_stm32_phy_specific_clock_enable(clk);
if (err) {
return err;
}
/* Previous check won't work in case of F1/F3. Add build time check */ /* Previous check won't work in case of F1/F3. Add build time check */
#if defined(RCC_CFGR_OTGFSPRE) || defined(RCC_CFGR_USBPRE) #if defined(RCC_CFGR_OTGFSPRE) || defined(RCC_CFGR_USBPRE)
@ -333,8 +422,8 @@ static int usb_dc_stm32_clock_disable(void)
LOG_ERR("Unable to disable USB clock"); LOG_ERR("Unable to disable USB clock");
return -EIO; return -EIO;
} }
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) && defined(CONFIG_SOC_SERIES_STM32U5X) #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_otghs_phy)
LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_USBPHY); clock_control_off(clk, (clock_control_subsys_t)&phy_pclken[0]);
#endif #endif
return 0; return 0;

View file

@ -106,16 +106,16 @@
num-bidir-endpoints = <9>; num-bidir-endpoints = <9>;
ram-size = <4096>; ram-size = <4096>;
maximum-speed = "high-speed"; maximum-speed = "high-speed";
clocks = <&rcc STM32_CLOCK(AHB2, 15U)>, clocks = <&rcc STM32_CLOCK(AHB2, 14U)>;
<&rcc STM32_SRC_HSI48 ICKLK_SEL(0)>;
phys = <&otghs_phy>; phys = <&otghs_phy>;
status = "disabled"; status = "disabled";
}; };
}; };
otghs_phy: otghs_phy { otghs_phy: otghs_phy {
/* Clock source defined by USBPHYC_SEL in */ compatible = "st,stm32u5-otghs-phy";
compatible = "usb-nop-xceiv"; clocks = <&rcc STM32_CLOCK(AHB2, 15U)>,
<&rcc STM32_SRC_HSE OTGHS_SEL(0)>;
#phy-cells = <0>; #phy-cells = <0>;
}; };

View file

@ -0,0 +1,36 @@
# Copyright (c) 2024 Marcin Niestroj
# Copyright (c) 2024 Meta
# SPDX-License-Identifier: Apache-2.0
description: |
This binding is to be used by the STM32U5xx transceivers which are built-in
with USB HS PHY IP and a configurable HSE clock source.
compatible: "st,stm32u5-otghs-phy"
include: phy-controller.yaml
properties:
"#phy-cells":
const: 0
clocks:
required: true
description: |
Supported configurations:
/* HSE */
clocks = <&rcc STM32_CLOCK(AHB2, 15U)>,
<&rcc STM32_SRC_HSE OTGHS_SEL(0)>;
/* HSE/2 */
clocks = <&rcc STM32_CLOCK(AHB2, 15U)>,
<&rcc (STM32_SRC_HSE | STM32_CLOCK_DIV(2)) OTGHS_SEL(2)>;
/* PLL1_P_CK */
clocks = <&rcc STM32_CLOCK(AHB2, 15U)>,
<&rcc STM32_SRC_PLL1_P OTGHS_SEL(1)>;
/* PLL1_P_CK/2 */
clocks = <&rcc STM32_CLOCK(AHB2, 15U)>,
<&rcc (STM32_SRC_PLL1_P | STM32_CLOCK_DIV(2)) OTGHS_SEL(3)>;

View file

@ -122,7 +122,7 @@
#define HSPI_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 22, CCIPR2_REG) #define HSPI_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 22, CCIPR2_REG)
#define I2C5_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 24, CCIPR2_REG) #define I2C5_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 24, CCIPR2_REG)
#define I2C6_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 26, CCIPR2_REG) #define I2C6_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 26, CCIPR2_REG)
#define USBPHYC_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 30, CCIPR2_REG) #define OTGHS_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 30, CCIPR2_REG)
/** CCIPR3 devices */ /** CCIPR3 devices */
#define LPUART1_SEL(val) STM32_DOMAIN_CLOCK(val, 7, 0, CCIPR3_REG) #define LPUART1_SEL(val) STM32_DOMAIN_CLOCK(val, 7, 0, CCIPR3_REG)
#define SPI3_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 3, CCIPR3_REG) #define SPI3_SEL(val) STM32_DOMAIN_CLOCK(val, 3, 3, CCIPR3_REG)