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é
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
bool "Runtime USB 48MHz clock check"
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
Enable USB clock 48MHz configuration runtime check.
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
static int usb_dc_stm32_clock_enable(void)
{
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_otghs_phy)
if (!device_is_ready(clk)) {
LOG_ERR("clock control device not ready");
return -ENODEV;
static const struct stm32_pclken phy_pclken[] = STM32_DT_CLOCKS(DT_INST_PHANDLE(0, phys));
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 */
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 */
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*/
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)
* 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 */
#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");
return -EIO;
}
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) && defined(CONFIG_SOC_SERIES_STM32U5X)
LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_USBPHY);
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_otghs_phy)
clock_control_off(clk, (clock_control_subsys_t)&phy_pclken[0]);
#endif
return 0;

View file

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