Commit f72ef5c2 authored by Marcin Niestroj's avatar Marcin Niestroj Committed by Benjamin Cabé
Browse files

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: default avatarAdrian Chadd <adrian.chadd@meta.com>
Signed-off-by: default avatarAdrian Chadd <adrian.chadd@meta.com>
Signed-off-by: default avatarMarcin Niestroj <m.niestroj@emb.dev>
parent fafaa582
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+99 −10
Original line number Diff line number Diff line
@@ -208,16 +208,67 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
}
#endif

static int usb_dc_stm32_clock_enable(void)
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32u5_otghs_phy)

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)
{
	const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
	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 (!device_is_ready(clk)) {
		LOG_ERR("clock control device not ready");
		return -ENODEV;
	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;
	}

	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;
	}

#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) && defined(CONFIG_SOC_SERIES_STM32U5X)
	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;
+4 −4
Original line number Diff line number Diff line
@@ -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>;
	};

+36 −0
Original line number Diff line number Diff line
# 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)>;
+1 −1
Original line number Diff line number Diff line
@@ -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)