blob: 4d51730e29f26b22d8539c971c68a15764b617dc [file] [log] [blame]
/*
* Copyright (c) 2023 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file udc_stm32.c
* @brief STM32 USB device controller (UDC) driver
*/
#include <soc.h>
#include <stm32_ll_bus.h>
#include <stm32_ll_pwr.h>
#include <stm32_ll_rcc.h>
#include <stm32_ll_system.h>
#include <string.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include <zephyr/sys/util.h>
#include "udc_common.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(udc_stm32, CONFIG_UDC_DRIVER_LOG_LEVEL);
/*
* The STM32 HAL does not provide PCD_SPEED_HIGH and PCD_SPEED_HIGH_IN_FULL
* on series which lack HS-capable hardware. Provide dummy definitions for
* these series to remove checks elsewhere in the driver. The exact value
* of the dummy definitions in insignificant, as long as they are not equal
* to PCD_SPEED_FULL (which is always provided).
*/
#if !defined(PCD_SPEED_HIGH)
#define PCD_SPEED_HIGH (PCD_SPEED_FULL + 1)
#define PCD_SPEED_HIGH_IN_FULL (PCD_SPEED_HIGH + 1)
#endif
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
#define DT_DRV_COMPAT st_stm32_otghs
#define UDC_STM32_IRQ_NAME otghs
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs)
#define DT_DRV_COMPAT st_stm32_otgfs
#define UDC_STM32_IRQ_NAME otgfs
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb)
#define DT_DRV_COMPAT st_stm32_usb
#define UDC_STM32_IRQ_NAME usb
#endif
#define UDC_STM32_BASE_ADDRESS DT_INST_REG_ADDR(0)
#define UDC_STM32_IRQ DT_INST_IRQ_BY_NAME(0, UDC_STM32_IRQ_NAME, irq)
#define UDC_STM32_IRQ_PRI DT_INST_IRQ_BY_NAME(0, UDC_STM32_IRQ_NAME, priority)
/* Shorthand to obtain PHY node for an instance */
#define UDC_STM32_PHY(usb_node) DT_PROP_BY_IDX(usb_node, phys, 0)
/* Evaluates to 1 if PHY of 'usb_node' is an embedded HS PHY, 0 otherwise */
#define UDC_STM32_PHY_HAS_EMBEDDED_HS_COMPAT(usb_node) \
UTIL_OR(DT_NODE_HAS_COMPAT(UDC_STM32_PHY(usb_node), st_stm32_usbphyc), \
DT_NODE_HAS_COMPAT(UDC_STM32_PHY(usb_node), st_stm32u5_otghs_phy))
/* Evaluates to 1 if 'usb_node' is HS-capable, 0 otherwise. */
#define UDC_STM32_NODE_IS_HS_CAPABLE(usb_node) DT_NODE_HAS_COMPAT(usb_node, st_stm32_otghs)
/*
* Returns the 'PCD_PHY_Module' value for 'usb_node', which
* corresponds to the PHY interface that should be used by
* the USB controller.
*
* This value may be one of:
* - PCD_PHY_EMBEDDED: embedded Full-Speed PHY
* - PCD_PHY_UTMI: embedded High-Speed PHY over UTMI+
* - PCD_PHY_ULPI: external High-Speed PHY over ULPI
*
* The correct value is always PCD_PHY_EMBEDDED for nodes
* that are not HS-capable: these instances are always
* hardwired to an embedded FS PHY.
*
* The correct value for HS-capable nodes is determined from
* the 'compatible' list on the PHY DT node, which is referenced
* by the USB controller's 'phys' property:
* - External HS PHYs must have 'usb-ulpi-phy' compatible
* - Embedded HS PHYs must have one of the ST-specific compatibles
* - Others ('usb-nop-xceiv') are assumed to be embedded FS PHYs
*/
#define UDC_STM32_NODE_PHY_ITFACE(usb_node) \
COND_CODE_0(UDC_STM32_NODE_IS_HS_CAPABLE(usb_node), \
(PCD_PHY_EMBEDDED), \
(COND_CODE_1(DT_NODE_HAS_COMPAT(UDC_STM32_PHY(usb_node), usb_ulpi_phy), \
(PCD_PHY_ULPI), \
(COND_CODE_1(UDC_STM32_PHY_HAS_EMBEDDED_HS_COMPAT(usb_node), \
(PCD_PHY_UTMI), \
(PCD_PHY_EMBEDDED)) \
))))
/*
* Evaluates to 1 if 'usb_node' uses an embedded FS PHY or has
* the 'maximum-speed' property set to 'full-speed', 0 otherwise.
*
* N.B.: enum index 1 corresponds to 'full-speed'
*/
#define UDC_STM32_NODE_LIMITED_TO_FS(usb_node) \
UTIL_OR(IS_EQ(UDC_STM32_NODE_PHY_ITFACE(usb_node), PHY_PCD_EMBEDDED), \
UTIL_AND(DT_NODE_HAS_PROP(usb_node, maximum_speed), \
IS_EQ(DT_ENUM_IDX(usb_node, maximum_speed), 1)))
/*
* Returns the 'PCD_Speed' value for 'usb_node', which indicates
* the operation mode in which controller should be configured.
*
* The 'maximum-speed' property is taken into account but only when
* present on an HS-capable instance to force 'full-speed' mode; all
* other uses are invalid and silently ignored.
*/
#define UDC_STM32_NODE_SPEED(usb_node) \
COND_CODE_0(UDC_STM32_NODE_IS_HS_CAPABLE(usb_node), \
(PCD_SPEED_FULL), \
(COND_CODE_1(UDC_STM32_NODE_LIMITED_TO_FS(usb_node), \
(PCD_SPEED_HIGH_IN_FULL), \
(PCD_SPEED_HIGH))))
/*
* Returns max packet size allowed for endpoints of 'usb_node'
*
* Hardware always supports the maximal value allowed
* by the USB Specification at a given operating speed:
* 1024 bytes in High-Speed, 1023 bytes in Full-Speed
*/
#define UDC_STM32_NODE_EP_MPS(node_id) \
((UDC_STM32_NODE_SPEED(node_id) == PCD_SPEED_HIGH) ? 1024U : 1023U)
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32n6_otghs)
#define USB_USBPHYC_CR_FSEL_24MHZ USB_USBPHYC_CR_FSEL_1
#endif
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) && defined(CONFIG_SOC_SERIES_STM32U5X)
static const int syscfg_otg_hs_phy_clk[] = {
SYSCFG_OTG_HS_PHY_CLK_SELECT_1, /* 16Mhz */
SYSCFG_OTG_HS_PHY_CLK_SELECT_2, /* 19.2Mhz */
SYSCFG_OTG_HS_PHY_CLK_SELECT_3, /* 20Mhz */
SYSCFG_OTG_HS_PHY_CLK_SELECT_4, /* 24Mhz */
SYSCFG_OTG_HS_PHY_CLK_SELECT_5, /* 26Mhz */
SYSCFG_OTG_HS_PHY_CLK_SELECT_6, /* 32Mhz */
};
#endif
/*
* Hardcode EP0 max packet size (bMaxPacketSize0) to 64,
* which is the maximum allowed by the USB Specification
* and supported by all STM32 USB controllers.
*/
#define UDC_STM32_EP0_MAX_PACKET_SIZE 64U
struct udc_stm32_data {
PCD_HandleTypeDef pcd;
const struct device *dev;
uint32_t irq;
uint32_t occupied_mem;
void (*pcd_prepare)(const struct device *dev);
int (*clk_enable)(void);
int (*clk_disable)(void);
struct k_thread thread_data;
struct k_msgq msgq_data;
};
struct udc_stm32_config {
uint32_t num_endpoints;
uint32_t dram_size;
/* PHY selected for use by instance */
uint32_t selected_phy;
/* Speed selected for use by instance */
uint32_t selected_speed;
/* Maximal packet size allowed for endpoints */
uint16_t ep_mps;
};
enum udc_stm32_msg_type {
UDC_STM32_MSG_SETUP,
UDC_STM32_MSG_DATA_OUT,
UDC_STM32_MSG_DATA_IN,
};
struct udc_stm32_msg {
uint8_t type;
uint8_t ep;
uint16_t rx_count;
};
static void udc_stm32_lock(const struct device *dev)
{
udc_lock_internal(dev, K_FOREVER);
}
static void udc_stm32_unlock(const struct device *dev)
{
udc_unlock_internal(dev);
}
#define hpcd2data(hpcd) CONTAINER_OF(hpcd, struct udc_stm32_data, pcd);
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
const struct device *dev = priv->dev;
struct udc_ep_config *ep;
/* Re-Enable control endpoints */
ep = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
if (ep && ep->stat.enabled) {
HAL_PCD_EP_Open(&priv->pcd, USB_CONTROL_EP_OUT,
UDC_STM32_EP0_MAX_PACKET_SIZE,
EP_TYPE_CTRL);
}
ep = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN);
if (ep && ep->stat.enabled) {
HAL_PCD_EP_Open(&priv->pcd, USB_CONTROL_EP_IN,
UDC_STM32_EP0_MAX_PACKET_SIZE,
EP_TYPE_CTRL);
}
udc_set_suspended(dev, false);
udc_submit_event(priv->dev, UDC_EVT_RESET, 0);
}
void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
udc_submit_event(priv->dev, UDC_EVT_VBUS_READY, 0);
}
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
udc_submit_event(priv->dev, UDC_EVT_VBUS_REMOVED, 0);
}
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
udc_set_suspended(priv->dev, true);
udc_submit_event(priv->dev, UDC_EVT_SUSPEND, 0);
}
void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
udc_set_suspended(priv->dev, false);
udc_submit_event(priv->dev, UDC_EVT_RESUME, 0);
}
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
struct udc_stm32_msg msg = {.type = UDC_STM32_MSG_SETUP};
int err;
err = k_msgq_put(&priv->msgq_data, &msg, K_NO_WAIT);
if (err < 0) {
LOG_ERR("UDC Message queue overrun");
}
}
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
udc_submit_sof_event(priv->dev);
}
static int usbd_ctrl_feed_dout(const struct device *dev, const size_t length)
{
struct udc_stm32_data *priv = udc_get_private(dev);
struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
struct net_buf *buf;
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length);
if (buf == NULL) {
return -ENOMEM;
}
k_fifo_put(&cfg->fifo, buf);
HAL_PCD_EP_Receive(&priv->pcd, cfg->addr, buf->data, buf->size);
return 0;
}
static void udc_stm32_flush_tx_fifo(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT);
HAL_PCD_EP_Receive(&priv->pcd, cfg->addr, NULL, 0);
}
static int udc_stm32_tx(const struct device *dev, struct udc_ep_config *epcfg,
struct net_buf *buf)
{
struct udc_stm32_data *priv = udc_get_private(dev);
uint8_t *data; uint32_t len;
HAL_StatusTypeDef status;
LOG_DBG("TX ep 0x%02x len %u", epcfg->addr, buf->len);
if (udc_ep_is_busy(epcfg)) {
return 0;
}
data = buf->data;
len = buf->len;
if (epcfg->addr == USB_CONTROL_EP_IN) {
len = MIN(UDC_STM32_EP0_MAX_PACKET_SIZE, buf->len);
}
buf->data += len;
buf->len -= len;
status = HAL_PCD_EP_Transmit(&priv->pcd, epcfg->addr, data, len);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_EP_Transmit failed(0x%02x), %d", epcfg->addr, (int)status);
return -EIO;
}
udc_ep_set_busy(epcfg, true);
if (epcfg->addr == USB_CONTROL_EP_IN && len > 0) {
/* Wait for an empty package from the host.
* This also flushes the TX FIFO to the host.
*/
if (DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb)) {
udc_stm32_flush_tx_fifo(dev);
} else {
usbd_ctrl_feed_dout(dev, 0);
}
}
return 0;
}
static int udc_stm32_rx(const struct device *dev, struct udc_ep_config *epcfg,
struct net_buf *buf)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
LOG_DBG("RX ep 0x%02x len %u", epcfg->addr, buf->size);
if (udc_ep_is_busy(epcfg)) {
return 0;
}
status = HAL_PCD_EP_Receive(&priv->pcd, epcfg->addr, buf->data, buf->size);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_EP_Receive failed(0x%02x), %d", epcfg->addr, (int)status);
return -EIO;
}
udc_ep_set_busy(epcfg, true);
return 0;
}
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
uint32_t rx_count = HAL_PCD_EP_GetRxCount(hpcd, epnum);
struct udc_stm32_data *priv = hpcd2data(hpcd);
struct udc_stm32_msg msg = {
.type = UDC_STM32_MSG_DATA_OUT,
.ep = epnum,
.rx_count = rx_count,
};
int err;
err = k_msgq_put(&priv->msgq_data, &msg, K_NO_WAIT);
if (err != 0) {
LOG_ERR("UDC Message queue overrun");
}
}
void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
struct udc_stm32_msg msg = {
.type = UDC_STM32_MSG_DATA_IN,
.ep = epnum,
};
int err;
err = k_msgq_put(&priv->msgq_data, &msg, K_NO_WAIT);
if (err != 0) {
LOG_ERR("UDC Message queue overrun");
}
}
static void handle_msg_data_out(struct udc_stm32_data *priv, uint8_t epnum, uint16_t rx_count)
{
const struct device *dev = priv->dev;
struct udc_ep_config *epcfg;
uint8_t ep = epnum | USB_EP_DIR_OUT;
struct net_buf *buf;
LOG_DBG("DataOut ep 0x%02x", ep);
epcfg = udc_get_ep_cfg(dev, ep);
udc_ep_set_busy(epcfg, false);
buf = udc_buf_get(epcfg);
if (unlikely(buf == NULL)) {
LOG_ERR("ep 0x%02x queue is empty", ep);
return;
}
net_buf_add(buf, rx_count);
if (ep == USB_CONTROL_EP_OUT) {
if (udc_ctrl_stage_is_status_out(dev)) {
udc_ctrl_update_stage(dev, buf);
udc_ctrl_submit_status(dev, buf);
} else {
udc_ctrl_update_stage(dev, buf);
}
if (udc_ctrl_stage_is_status_in(dev)) {
udc_ctrl_submit_s_out_status(dev, buf);
}
} else {
udc_submit_ep_event(dev, buf, 0);
}
buf = udc_buf_peek(epcfg);
if (buf) {
udc_stm32_rx(dev, epcfg, buf);
}
}
static void handle_msg_data_in(struct udc_stm32_data *priv, uint8_t epnum)
{
const struct device *dev = priv->dev;
struct udc_ep_config *epcfg;
uint8_t ep = epnum | USB_EP_DIR_IN;
struct net_buf *buf;
LOG_DBG("DataIn ep 0x%02x", ep);
epcfg = udc_get_ep_cfg(dev, ep);
udc_ep_set_busy(epcfg, false);
buf = udc_buf_peek(epcfg);
if (unlikely(buf == NULL)) {
return;
}
if (ep == USB_CONTROL_EP_IN && buf->len) {
uint32_t len = MIN(UDC_STM32_EP0_MAX_PACKET_SIZE, buf->len);
HAL_PCD_EP_Transmit(&priv->pcd, ep, buf->data, len);
buf->len -= len;
buf->data += len;
return;
}
if (udc_ep_buf_has_zlp(buf)) {
udc_ep_buf_clear_zlp(buf);
HAL_PCD_EP_Transmit(&priv->pcd, ep, buf->data, 0);
return;
}
udc_buf_get(epcfg);
if (ep == USB_CONTROL_EP_IN) {
if (udc_ctrl_stage_is_status_in(dev) ||
udc_ctrl_stage_is_no_data(dev)) {
/* Status stage finished, notify upper layer */
udc_ctrl_submit_status(dev, buf);
}
/* Update to next stage of control transfer */
udc_ctrl_update_stage(dev, buf);
if (udc_ctrl_stage_is_status_out(dev)) {
/*
* IN transfer finished, release buffer,
* control OUT buffer should be already fed.
*/
net_buf_unref(buf);
}
return;
}
udc_submit_ep_event(dev, buf, 0);
buf = udc_buf_peek(epcfg);
if (buf) {
udc_stm32_tx(dev, epcfg, buf);
}
}
static void handle_msg_setup(struct udc_stm32_data *priv)
{
struct usb_setup_packet *setup = (void *)priv->pcd.Setup;
const struct device *dev = priv->dev;
struct net_buf *buf;
int err;
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet));
if (buf == NULL) {
LOG_ERR("Failed to allocate for setup");
return;
}
udc_ep_buf_set_setup(buf);
memcpy(buf->data, setup, 8);
net_buf_add(buf, 8);
udc_ctrl_update_stage(dev, buf);
if (!buf->len) {
return;
}
if ((setup->bmRequestType == 0) && (setup->bRequest == USB_SREQ_SET_ADDRESS)) {
/* HAL requires we set the address before submitting status */
HAL_PCD_SetAddress(&priv->pcd, setup->wValue);
}
if (udc_ctrl_stage_is_data_out(dev)) {
/* Allocate and feed buffer for data OUT stage */
err = usbd_ctrl_feed_dout(dev, udc_data_stage_length(buf));
if (err == -ENOMEM) {
udc_submit_ep_event(dev, buf, err);
}
} else if (udc_ctrl_stage_is_data_in(dev)) {
udc_ctrl_submit_s_in_status(dev);
} else {
udc_ctrl_submit_s_status(dev);
}
}
static void udc_stm32_thread_handler(void *arg1, void *arg2, void *arg3)
{
const struct device *dev = arg1;
struct udc_stm32_data *priv = udc_get_private(dev);
struct udc_stm32_msg msg;
while (true) {
k_msgq_get(&priv->msgq_data, &msg, K_FOREVER);
switch (msg.type) {
case UDC_STM32_MSG_SETUP:
handle_msg_setup(priv);
break;
case UDC_STM32_MSG_DATA_IN:
handle_msg_data_in(priv, msg.ep);
break;
case UDC_STM32_MSG_DATA_OUT:
handle_msg_data_out(priv, msg.ep, msg.rx_count);
break;
}
}
}
#if DT_INST_NODE_HAS_PROP(0, disconnect_gpios)
void HAL_PCDEx_SetConnectionState(PCD_HandleTypeDef *hpcd, uint8_t state)
{
struct gpio_dt_spec usb_disconnect = GPIO_DT_SPEC_INST_GET(0, disconnect_gpios);
gpio_pin_configure_dt(&usb_disconnect,
state ? GPIO_OUTPUT_ACTIVE : GPIO_OUTPUT_INACTIVE);
}
#endif
static void udc_stm32_irq(const struct device *dev)
{
const struct udc_stm32_data *priv = udc_get_private(dev);
/* HAL irq handler will call the related above callback */
HAL_PCD_IRQHandler((PCD_HandleTypeDef *)&priv->pcd);
}
int udc_stm32_init(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
if (priv->clk_enable && priv->clk_enable()) {
LOG_ERR("Error enabling clock(s)");
return -EIO;
}
priv->pcd_prepare(dev);
status = HAL_PCD_Init(&priv->pcd);
if (status != HAL_OK) {
LOG_ERR("PCD_Init failed, %d", (int)status);
return -EIO;
}
HAL_PCD_Stop(&priv->pcd);
return 0;
}
#if defined(USB) || defined(USB_DRD_FS)
static inline void udc_stm32_mem_init(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
const struct udc_stm32_config *cfg = dev->config;
/**
* Endpoint configuration table is placed at the
* beginning of Private Memory Area and consumes
* 8 bytes for each endpoint.
*/
priv->occupied_mem = 8 * cfg->num_endpoints;
}
static int udc_stm32_ep_mem_config(const struct device *dev,
struct udc_ep_config *ep,
bool enable)
{
struct udc_stm32_data *priv = udc_get_private(dev);
const struct udc_stm32_config *cfg = dev->config;
uint32_t size;
size = MIN(udc_mps_ep_size(ep), cfg->ep_mps);
if (!enable) {
priv->occupied_mem -= size;
return 0;
}
if (priv->occupied_mem + size >= cfg->dram_size) {
LOG_ERR("Unable to allocate FIFO for 0x%02x", ep->addr);
return -ENOMEM;
}
/* Configure PMA offset for the endpoint */
HAL_PCDEx_PMAConfig(&priv->pcd, ep->addr, PCD_SNG_BUF,
priv->occupied_mem);
priv->occupied_mem += size;
return 0;
}
#else
static void udc_stm32_mem_init(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
const struct udc_stm32_config *cfg = dev->config;
uint32_t rxfifo_size; /* in words */
LOG_DBG("DRAM size: %uB", cfg->dram_size);
/*
* In addition to the user-provided baseline, RxFIFO should fit:
* - Global OUT NAK (1 word)
* - Received packet information (1 word)
* - Transfer complete status information (2 words per OUT endpoint)
*
* Align user-provided baseline up to 32-bit word size then
* add this "fixed" overhead to obtain the final RxFIFO size.
*/
rxfifo_size = DIV_ROUND_UP(CONFIG_UDC_STM32_OTG_RXFIFO_BASELINE_SIZE, 4U);
rxfifo_size += 2U; /* Global OUT NAK and Rx packet info */
rxfifo_size += 2U * cfg->num_endpoints;
LOG_DBG("RxFIFO size: %uB", rxfifo_size * 4U);
HAL_PCDEx_SetRxFiFo(&priv->pcd, rxfifo_size);
priv->occupied_mem = rxfifo_size * 4U;
/* For EP0 TX, reserve only one MPS */
HAL_PCDEx_SetTxFiFo(&priv->pcd, 0, DIV_ROUND_UP(UDC_STM32_EP0_MAX_PACKET_SIZE, 4U));
priv->occupied_mem += UDC_STM32_EP0_MAX_PACKET_SIZE;
/* Reset TX allocs */
for (unsigned int i = 1U; i < cfg->num_endpoints; i++) {
HAL_PCDEx_SetTxFiFo(&priv->pcd, i, 0);
}
}
static int udc_stm32_ep_mem_config(const struct device *dev,
struct udc_ep_config *ep,
bool enable)
{
struct udc_stm32_data *priv = udc_get_private(dev);
const struct udc_stm32_config *cfg = dev->config;
unsigned int words;
if (!(ep->addr & USB_EP_DIR_IN) || !USB_EP_GET_IDX(ep->addr)) {
return 0;
}
words = DIV_ROUND_UP(MIN(udc_mps_ep_size(ep), cfg->ep_mps), 4U);
words = (words <= 64) ? words * 2 : words;
if (!enable) {
if (priv->occupied_mem >= (words * 4)) {
priv->occupied_mem -= (words * 4);
}
HAL_PCDEx_SetTxFiFo(&priv->pcd, USB_EP_GET_IDX(ep->addr), 0);
return 0;
}
if (cfg->dram_size - priv->occupied_mem < words * 4) {
LOG_ERR("Unable to allocate FIFO for 0x%02x", ep->addr);
return -ENOMEM;
}
HAL_PCDEx_SetTxFiFo(&priv->pcd, USB_EP_GET_IDX(ep->addr), words);
priv->occupied_mem += words * 4;
return 0;
}
#endif
static int udc_stm32_enable(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
int ret;
LOG_DBG("Enable UDC");
udc_stm32_mem_init(dev);
status = HAL_PCD_Start(&priv->pcd);
if (status != HAL_OK) {
LOG_ERR("PCD_Start failed, %d", (int)status);
return -EIO;
}
ret = udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT,
USB_EP_TYPE_CONTROL,
UDC_STM32_EP0_MAX_PACKET_SIZE, 0);
if (ret) {
LOG_ERR("Failed enabling ep 0x%02x", USB_CONTROL_EP_OUT);
return ret;
}
ret |= udc_ep_enable_internal(dev, USB_CONTROL_EP_IN,
USB_EP_TYPE_CONTROL,
UDC_STM32_EP0_MAX_PACKET_SIZE, 0);
if (ret) {
LOG_ERR("Failed enabling ep 0x%02x", USB_CONTROL_EP_IN);
return ret;
}
irq_enable(priv->irq);
return 0;
}
static int udc_stm32_disable(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
irq_disable(UDC_STM32_IRQ);
if (udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT)) {
LOG_ERR("Failed to disable control endpoint");
return -EIO;
}
if (udc_ep_disable_internal(dev, USB_CONTROL_EP_IN)) {
LOG_ERR("Failed to disable control endpoint");
return -EIO;
}
status = HAL_PCD_Stop(&priv->pcd);
if (status != HAL_OK) {
LOG_ERR("PCD_Stop failed, %d", (int)status);
return -EIO;
}
return 0;
}
static int udc_stm32_shutdown(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
status = HAL_PCD_DeInit(&priv->pcd);
if (status != HAL_OK) {
LOG_ERR("PCD_DeInit failed, %d", (int)status);
/* continue anyway */
}
if (priv->clk_disable && priv->clk_disable()) {
LOG_ERR("Error disabling clock(s)");
/* continue anyway */
}
if (irq_is_enabled(priv->irq)) {
irq_disable(priv->irq);
}
return 0;
}
static int udc_stm32_set_address(const struct device *dev, const uint8_t addr)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
LOG_DBG("Set Address %u", addr);
status = HAL_PCD_SetAddress(&priv->pcd, addr);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_SetAddress failed(0x%02x), %d",
addr, (int)status);
return -EIO;
}
return 0;
}
static int udc_stm32_host_wakeup(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
status = HAL_PCD_ActivateRemoteWakeup(&priv->pcd);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_ActivateRemoteWakeup, %d", (int)status);
return -EIO;
}
/* Must be active from 1ms to 15ms as per reference manual. */
k_sleep(K_MSEC(2));
status = HAL_PCD_DeActivateRemoteWakeup(&priv->pcd);
if (status != HAL_OK) {
return -EIO;
}
udc_set_suspended(dev, false);
udc_submit_event(dev, UDC_EVT_RESUME, 0);
return 0;
}
static int udc_stm32_ep_enable(const struct device *dev,
struct udc_ep_config *ep_cfg)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
uint8_t ep_type;
int ret;
LOG_DBG("Enable ep 0x%02x", ep_cfg->addr);
switch (ep_cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) {
case USB_EP_TYPE_CONTROL:
ep_type = EP_TYPE_CTRL;
break;
case USB_EP_TYPE_BULK:
ep_type = EP_TYPE_BULK;
break;
case USB_EP_TYPE_INTERRUPT:
ep_type = EP_TYPE_INTR;
break;
case USB_EP_TYPE_ISO:
ep_type = EP_TYPE_ISOC;
break;
default:
return -EINVAL;
}
ret = udc_stm32_ep_mem_config(dev, ep_cfg, true);
if (ret) {
return ret;
}
status = HAL_PCD_EP_Open(&priv->pcd, ep_cfg->addr,
udc_mps_ep_size(ep_cfg), ep_type);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_EP_Open failed(0x%02x), %d",
ep_cfg->addr, (int)status);
return -EIO;
}
return 0;
}
static int udc_stm32_ep_disable(const struct device *dev,
struct udc_ep_config *ep)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
LOG_DBG("Disable ep 0x%02x", ep->addr);
status = HAL_PCD_EP_Close(&priv->pcd, ep->addr);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_EP_Close failed(0x%02x), %d",
ep->addr, (int)status);
return -EIO;
}
return udc_stm32_ep_mem_config(dev, ep, false);
}
static int udc_stm32_ep_set_halt(const struct device *dev,
struct udc_ep_config *cfg)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
LOG_DBG("Halt ep 0x%02x", cfg->addr);
status = HAL_PCD_EP_SetStall(&priv->pcd, cfg->addr);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_EP_SetStall failed(0x%02x), %d",
cfg->addr, (int)status);
return -EIO;
}
/* Mark endpoint as halted if not control EP */
if (USB_EP_GET_IDX(cfg->addr) != 0U) {
cfg->stat.halted = true;
}
return 0;
}
static int udc_stm32_ep_clear_halt(const struct device *dev,
struct udc_ep_config *cfg)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
struct net_buf *buf;
LOG_DBG("Clear halt for ep 0x%02x", cfg->addr);
status = HAL_PCD_EP_ClrStall(&priv->pcd, cfg->addr);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_EP_ClrStall failed(0x%02x), %d",
cfg->addr, (int)status);
return -EIO;
}
/* Clear halt bit from endpoint status */
cfg->stat.halted = false;
/* Check if there are transfers queued for EP */
buf = udc_buf_peek(cfg);
if (buf != NULL) {
/*
* There is at least one transfer pending.
* IN EP transfer can be started only if not busy;
* OUT EP transfer should be prepared only if busy.
*/
const bool busy = udc_ep_is_busy(cfg);
if (USB_EP_DIR_IS_IN(cfg->addr) && !busy) {
udc_stm32_tx(dev, cfg, buf);
} else if (USB_EP_DIR_IS_OUT(cfg->addr) && busy) {
udc_stm32_rx(dev, cfg, buf);
}
}
return 0;
}
static int udc_stm32_ep_flush(const struct device *dev,
struct udc_ep_config *cfg)
{
struct udc_stm32_data *priv = udc_get_private(dev);
HAL_StatusTypeDef status;
LOG_DBG("Flush ep 0x%02x", cfg->addr);
status = HAL_PCD_EP_Flush(&priv->pcd, cfg->addr);
if (status != HAL_OK) {
LOG_ERR("HAL_PCD_EP_Flush failed(0x%02x), %d",
cfg->addr, (int)status);
return -EIO;
}
return 0;
}
static int udc_stm32_ep_enqueue(const struct device *dev,
struct udc_ep_config *epcfg,
struct net_buf *buf)
{
unsigned int lock_key;
int ret = 0;
udc_buf_put(epcfg, buf);
lock_key = irq_lock();
if (USB_EP_DIR_IS_IN(epcfg->addr)) {
if (epcfg->stat.halted) {
LOG_DBG("skip enqueue for halted ep 0x%02x", epcfg->addr);
} else {
ret = udc_stm32_tx(dev, epcfg, buf);
}
} else {
ret = udc_stm32_rx(dev, epcfg, buf);
}
irq_unlock(lock_key);
return ret;
}
static int udc_stm32_ep_dequeue(const struct device *dev,
struct udc_ep_config *epcfg)
{
struct net_buf *buf;
udc_stm32_ep_flush(dev, epcfg);
buf = udc_buf_get_all(epcfg);
if (buf) {
udc_submit_ep_event(dev, buf, -ECONNABORTED);
}
udc_ep_set_busy(epcfg, false);
return 0;
}
static enum udc_bus_speed udc_stm32_device_speed(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
/*
* N.B.: pcd.Init.speed is used here on purpose instead
* of cfg->selected_speed because HAL updates this field
* after USB enumeration to reflect actual bus speed.
*/
if (priv->pcd.Init.speed == PCD_SPEED_HIGH) {
return UDC_BUS_SPEED_HS;
}
if (priv->pcd.Init.speed == PCD_SPEED_HIGH_IN_FULL ||
priv->pcd.Init.speed == PCD_SPEED_FULL) {
return UDC_BUS_SPEED_FS;
}
return UDC_BUS_UNKNOWN;
}
static const struct udc_api udc_stm32_api = {
.lock = udc_stm32_lock,
.unlock = udc_stm32_unlock,
.init = udc_stm32_init,
.enable = udc_stm32_enable,
.disable = udc_stm32_disable,
.shutdown = udc_stm32_shutdown,
.set_address = udc_stm32_set_address,
.host_wakeup = udc_stm32_host_wakeup,
.ep_try_config = NULL,
.ep_enable = udc_stm32_ep_enable,
.ep_disable = udc_stm32_ep_disable,
.ep_set_halt = udc_stm32_ep_set_halt,
.ep_clear_halt = udc_stm32_ep_clear_halt,
.ep_enqueue = udc_stm32_ep_enqueue,
.ep_dequeue = udc_stm32_ep_dequeue,
.device_speed = udc_stm32_device_speed,
};
/* ----------------- Instance/Device specific data ----------------- */
/*
* USB, USB_OTG_FS and USB_DRD_FS are defined in STM32Cube HAL and allows to
* distinguish between two kind of USB DC. STM32 F0, F3, L0 and G4 series
* support USB device controller. STM32 F4 and F7 series support USB_OTG_FS
* device controller. STM32 F1 and L4 series support either USB or USB_OTG_FS
* device controller.STM32 G0 series supports USB_DRD_FS device controller.
*
* WARNING: Don't mix USB defined in STM32Cube HAL and CONFIG_USB_* from Zephyr
* Kconfig system.
*/
#define USB_NUM_BIDIR_ENDPOINTS DT_INST_PROP(0, num_bidir_endpoints)
#define USB_RAM_SIZE DT_INST_PROP(0, ram_size)
static struct udc_stm32_data udc0_priv;
static struct udc_data udc0_data = {
.mutex = Z_MUTEX_INITIALIZER(udc0_data.mutex),
.priv = &udc0_priv,
};
static const struct udc_stm32_config udc0_cfg = {
.num_endpoints = USB_NUM_BIDIR_ENDPOINTS,
.dram_size = USB_RAM_SIZE,
.ep_mps = UDC_STM32_NODE_EP_MPS(DT_DRV_INST(0)),
.selected_phy = UDC_STM32_NODE_PHY_ITFACE(DT_DRV_INST(0)),
.selected_speed = UDC_STM32_NODE_SPEED(DT_DRV_INST(0)),
};
static void priv_pcd_prepare(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
const struct udc_stm32_config *cfg = dev->config;
memset(&priv->pcd, 0, sizeof(priv->pcd));
/* Default values */
priv->pcd.Init.dev_endpoints = cfg->num_endpoints;
priv->pcd.Init.ep0_mps = UDC_STM32_EP0_MAX_PACKET_SIZE;
priv->pcd.Init.speed = cfg->selected_speed;
/* Per controller/Phy values */
#if defined(USB)
priv->pcd.Instance = USB;
#elif defined(USB_DRD_FS)
priv->pcd.Instance = USB_DRD_FS;
#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs) || DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
priv->pcd.Instance = (USB_OTG_GlobalTypeDef *)UDC_STM32_BASE_ADDRESS;
#endif /* USB */
priv->pcd.Init.phy_itface = cfg->selected_phy;
}
static struct stm32_pclken pclken[] = STM32_DT_INST_CLOCKS(0);
static int priv_clock_enable(void)
{
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
if (!device_is_ready(clk)) {
LOG_ERR("clock control device not ready");
return -ENODEV;
}
/* Power configuration */
#if defined(CONFIG_SOC_SERIES_STM32H7X)
LL_PWR_EnableUSBVoltageDetector();
/* Per AN2606: USBREGEN not supported when running in FS mode. */
LL_PWR_DisableUSBReg();
while (!LL_PWR_IsActiveFlag_USB()) {
LOG_INF("PWR not active yet");
k_msleep(100);
}
#elif defined(CONFIG_SOC_SERIES_STM32U5X)
/* Sequence to enable the power of the OTG HS on a stm32U5 serie : Enable VDDUSB */
__ASSERT_NO_MSG(LL_AHB3_GRP1_IsEnabledClock(LL_AHB3_GRP1_PERIPH_PWR));
/* Check that power range is 1 or 2 */
if (LL_PWR_GetRegulVoltageScaling() < LL_PWR_REGU_VOLTAGE_SCALE2) {
LOG_ERR("Wrong Power range to use USB OTG HS");
return -EIO;
}
LL_PWR_EnableVddUSB();
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
/* Configure VOSR register of USB HSTransceiverSupply(); */
LL_PWR_EnableUSBPowerSupply();
LL_PWR_EnableUSBEPODBooster();
while (LL_PWR_IsActiveFlag_USBBOOST() != 1) {
/* Wait for USB EPOD BOOST ready */
}
#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) */
#elif defined(CONFIG_SOC_SERIES_STM32N6X)
/* Enable Vdd USB voltage monitoring */
LL_PWR_EnableVddUSBMonitoring();
while (__HAL_PWR_GET_FLAG(PWR_FLAG_USB33RDY)) {
/* Wait FOR VDD33USB ready */
}
/* Enable VDDUSB */
LL_PWR_EnableVddUSB();
#elif defined(PWR_USBSCR_USB33SV) || defined(PWR_SVMCR_USV)
/*
* VDDUSB independent USB supply (PWR clock is on)
* with LL_PWR_EnableVDDUSB function (higher case)
*/
LL_PWR_EnableVDDUSB();
#endif
if (DT_INST_NUM_CLOCKS(0) > 1) {
if (clock_control_configure(clk, &pclken[1], NULL) != 0) {
LOG_ERR("Could not select USB domain clock");
return -EIO;
}
}
if (clock_control_on(clk, &pclken[0]) != 0) {
LOG_ERR("Unable to enable USB clock");
return -EIO;
}
if (IS_ENABLED(CONFIG_UDC_STM32_CLOCK_CHECK)) {
uint32_t usb_clock_rate;
if (clock_control_get_rate(clk, &pclken[1], &usb_clock_rate) != 0) {
LOG_ERR("Failed to get USB domain clock rate");
return -EIO;
}
if (usb_clock_rate != MHZ(48)) {
LOG_ERR("USB Clock is not 48MHz (%d)", usb_clock_rate);
return -ENOTSUP;
}
}
/* Previous check won't work in case of F1/F3. Add build time check */
#if defined(RCC_CFGR_OTGFSPRE) || defined(RCC_CFGR_USBPRE)
#if (MHZ(48) == CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) && !defined(STM32_PLL_USBPRE)
/* PLL output clock is set to 48MHz, it should not be divided */
#warning USBPRE/OTGFSPRE should be set in rcc node
#endif
#endif /* RCC_CFGR_OTGFSPRE / RCC_CFGR_USBPRE */
/* PHY configuration */
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
#if defined(CONFIG_SOC_SERIES_STM32N6X)
/*
* Note that the USBPHYC is clocked only when
* the OTG_HS instance is also clocked, so this
* must come after clock_control_on() or the
* SoC will deadlock.
*/
/* Reset specific configuration bits before setting new values */
USB1_HS_PHYC->USBPHYC_CR &= ~USB_USBPHYC_CR_FSEL_Msk;
/* Configure the USB PHY Control Register to operate in the High frequency "24 MHz"
* by setting the Frequency Selection (FSEL) bits 4 and 5 to 10,
* which ensures proper communication.
*/
USB1_HS_PHYC->USBPHYC_CR |= USB_USBPHYC_CR_FSEL_24MHZ;
/* Peripheral OTGPHY clock enable */
LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1);
#elif defined(CONFIG_SOC_SERIES_STM32U5X)
/* Configure OTG PHY reference clock through SYSCFG */
LL_APB3_GRP1_EnableClock(LL_APB3_GRP1_PERIPH_SYSCFG);
HAL_SYSCFG_SetOTGPHYReferenceClockSelection(
syscfg_otg_hs_phy_clk[DT_ENUM_IDX(DT_NODELABEL(otghs_phy), clock_reference)]
);
/* De-assert reset and enable clock of OTG PHY */
HAL_SYSCFG_EnableOTGPHY(SYSCFG_OTG_HS_PHY_ENABLE);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_USBPHY);
#elif defined(CONFIG_SOC_SERIES_STM32H7X)
/*
* If HS PHY (over ULPI) is used, enable ULPI interface clock.
* Otherwise, disable ULPI clock in sleep/low-power mode.
* (No need to disable Run mode clock, it is off by default)
*/
if (UDC_STM32_NODE_PHY_ITFACE(DT_DRV_INST(0)) == PCD_PHY_ULPI) {
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_USB1OTGHSULPI);
} else {
LL_AHB1_GRP1_DisableClockSleep(LL_AHB1_GRP1_PERIPH_USB1OTGHSULPI);
}
#elif defined(CONFIG_SOC_SERIES_STM32F7X)
/*
* Preprocessor check is required here because
* the OTGPHYC defines are not provided if it
* doesn't exist on SoC.
*/
#if UDC_STM32_NODE_PHY_ITFACE(DT_DRV_INST(0)) == PCD_PHY_ULPI
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_OTGHSULPI);
#elif UDC_STM32_NODE_PHY_ITFACE(DT_DRV_INST(0)) == PCD_PHY_UTMI
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_OTGPHYC);
#endif
#else /* CONFIG_SOC_SERIES_STM32F2X || CONFIG_SOC_SERIES_STM32F4X */
if (UDC_STM32_NODE_PHY_ITFACE(DT_DRV_INST(0)) == PCD_PHY_ULPI) {
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_OTGHSULPI);
}
#endif /* CONFIG_SOC_SERIES_* */
#elif defined(CONFIG_SOC_SERIES_STM32H7X) && DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs)
/* The USB2 controller only works in FS mode, but the ULPI clock needs
* to be disabled in sleep mode for it to work.
*/
LL_AHB1_GRP1_DisableClockSleep(LL_AHB1_GRP1_PERIPH_USB2OTGHSULPI);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) */
return 0;
}
static int priv_clock_disable(void)
{
const struct device *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
if (clock_control_off(clk, &pclken[0]) != 0) {
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);
#endif
return 0;
}
static struct udc_ep_config ep_cfg_in[DT_INST_PROP(0, num_bidir_endpoints)];
static struct udc_ep_config ep_cfg_out[DT_INST_PROP(0, num_bidir_endpoints)];
#if !DT_HAS_COMPAT_STATUS_OKAY(st_stm32n6_otghs)
PINCTRL_DT_INST_DEFINE(0);
static const struct pinctrl_dev_config *usb_pcfg =
PINCTRL_DT_INST_DEV_CONFIG_GET(0);
#endif
#if UDC_STM32_NODE_PHY_ITFACE(DT_DRV_INST(0)) == PCD_PHY_ULPI
static const struct gpio_dt_spec ulpi_reset =
GPIO_DT_SPEC_GET_OR(DT_PHANDLE(DT_INST(0, st_stm32_otghs), phys), reset_gpios, {0});
#endif
static char udc_msgq_buf_0[CONFIG_UDC_STM32_MAX_QMESSAGES * sizeof(struct udc_stm32_msg)];
K_THREAD_STACK_DEFINE(udc_stm32_stack_0, CONFIG_UDC_STM32_STACK_SIZE);
static int udc_stm32_driver_init0(const struct device *dev)
{
struct udc_stm32_data *priv = udc_get_private(dev);
const struct udc_stm32_config *cfg = dev->config;
struct udc_data *data = dev->data;
int err;
for (unsigned int i = 0; i < ARRAY_SIZE(ep_cfg_out); i++) {
ep_cfg_out[i].caps.out = 1;
if (i == 0) {
ep_cfg_out[i].caps.control = 1;
ep_cfg_out[i].caps.mps = UDC_STM32_EP0_MAX_PACKET_SIZE;
} else {
ep_cfg_out[i].caps.bulk = 1;
ep_cfg_out[i].caps.interrupt = 1;
ep_cfg_out[i].caps.iso = 1;
ep_cfg_out[i].caps.mps = cfg->ep_mps;
}
ep_cfg_out[i].addr = USB_EP_DIR_OUT | i;
err = udc_register_ep(dev, &ep_cfg_out[i]);
if (err != 0) {
LOG_ERR("Failed to register endpoint");
return err;
}
}
for (unsigned int i = 0; i < ARRAY_SIZE(ep_cfg_in); i++) {
ep_cfg_in[i].caps.in = 1;
if (i == 0) {
ep_cfg_in[i].caps.control = 1;
ep_cfg_in[i].caps.mps = UDC_STM32_EP0_MAX_PACKET_SIZE;
} else {
ep_cfg_in[i].caps.bulk = 1;
ep_cfg_in[i].caps.interrupt = 1;
ep_cfg_in[i].caps.iso = 1;
ep_cfg_in[i].caps.mps = 1023;
}
ep_cfg_in[i].addr = USB_EP_DIR_IN | i;
err = udc_register_ep(dev, &ep_cfg_in[i]);
if (err != 0) {
LOG_ERR("Failed to register endpoint");
return err;
}
}
data->caps.rwup = true;
data->caps.out_ack = false;
data->caps.mps0 = UDC_MPS0_64;
if (cfg->selected_speed == PCD_SPEED_HIGH) {
data->caps.hs = true;
}
priv->dev = dev;
priv->irq = UDC_STM32_IRQ;
priv->clk_enable = priv_clock_enable;
priv->clk_disable = priv_clock_disable;
priv->pcd_prepare = priv_pcd_prepare;
k_msgq_init(&priv->msgq_data, udc_msgq_buf_0, sizeof(struct udc_stm32_msg),
CONFIG_UDC_STM32_MAX_QMESSAGES);
k_thread_create(&priv->thread_data, udc_stm32_stack_0,
K_THREAD_STACK_SIZEOF(udc_stm32_stack_0), udc_stm32_thread_handler,
(void *)dev, NULL, NULL, K_PRIO_COOP(CONFIG_UDC_STM32_THREAD_PRIORITY),
K_ESSENTIAL, K_NO_WAIT);
k_thread_name_set(&priv->thread_data, dev->name);
IRQ_CONNECT(UDC_STM32_IRQ, UDC_STM32_IRQ_PRI, udc_stm32_irq,
DEVICE_DT_INST_GET(0), 0);
#if !DT_HAS_COMPAT_STATUS_OKAY(st_stm32n6_otghs)
err = pinctrl_apply_state(usb_pcfg, PINCTRL_STATE_DEFAULT);
if (err < 0) {
LOG_ERR("USB pinctrl setup failed (%d)", err);
return err;
}
#endif
#ifdef SYSCFG_CFGR1_USB_IT_RMP
/*
* STM32F302/F303: USB IRQ collides with CAN_1 IRQ (ยง14.1.3, RM0316)
* Remap IRQ by default to enable use of both IPs simultaneoulsy
* This should be done before calling any HAL function
*/
if (LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_SYSCFG)) {
LL_SYSCFG_EnableRemapIT_USB();
} else {
LOG_ERR("System Configuration Controller clock is "
"disabled. Unable to enable IRQ remapping.");
}
#endif
#if UDC_STM32_NODE_PHY_ITFACE(DT_DRV_INST(0)) == PCD_PHY_ULPI
if (ulpi_reset.port != NULL) {
if (!gpio_is_ready_dt(&ulpi_reset)) {
LOG_ERR("Reset GPIO device not ready");
return -EINVAL;
}
if (gpio_pin_configure_dt(&ulpi_reset, GPIO_OUTPUT_INACTIVE)) {
LOG_ERR("Couldn't configure reset pin");
return -EIO;
}
}
#endif
/*cd
* Required for at least STM32L4 devices as they electrically
* isolate USB features from VDDUSB. It must be enabled before
* USB can function. Refer to section 5.1.3 in DM00083560 or
* DM00310109.
*/
#ifdef PWR_CR2_USV
#if defined(LL_APB1_GRP1_PERIPH_PWR)
if (LL_APB1_GRP1_IsEnabledClock(LL_APB1_GRP1_PERIPH_PWR)) {
LL_PWR_EnableVddUSB();
} else {
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
LL_PWR_EnableVddUSB();
LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_PWR);
}
#else
LL_PWR_EnableVddUSB();
#endif /* defined(LL_APB1_GRP1_PERIPH_PWR) */
#endif /* PWR_CR2_USV */
return 0;
}
DEVICE_DT_INST_DEFINE(0, udc_stm32_driver_init0, NULL, &udc0_data, &udc0_cfg,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&udc_stm32_api);