diff --git a/drivers/usb/udc/Kconfig.stm32 b/drivers/usb/udc/Kconfig.stm32 index bf580957cc630..11f885f0dfcc3 100644 --- a/drivers/usb/udc/Kconfig.stm32 +++ b/drivers/usb/udc/Kconfig.stm32 @@ -36,6 +36,26 @@ config UDC_STM32_MAX_QMESSAGES help Maximum number of messages for handling of STM32 USBD ISR events. +config UDC_STM32_OTG_RXFIFO_BASELINE_SIZE + int "Baseline RxFIFO size (in bytes)" + default 600 + depends on DT_HAS_ST_STM32_OTGFS_ENABLED \ + || DT_HAS_ST_STM32_OTGHS_ENABLED + help + Baseline value for RXFIFO size computation + + The OTG_FS and OTG_HS USB controllers use a single "RxFIFO" to hold + data received by all OUT endpoints. The RxFIFO's size is influenced + by various parameters: the optimal value depends on the exact USB + configuration that will be used, which the driver does not know by + the time it has to configure the RxFIFO size. + + The total RxFIFO size will be equal to the value of this option + plus a fixed overhead that the driver can derive from the hardware + configuration (e.g., number of endpoints implemented). + + Refer to STM32 Reference Manuals for more details about the RxFIFO. + config UDC_STM32_CLOCK_CHECK bool "Runtime USB 48MHz clock check" default y if !(SOC_SERIES_STM32F1X || SOC_SERIES_STM32F3X || SOC_SERIES_STM32U5X) diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index 479eb54c2672f..19c7062759241 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -121,6 +121,17 @@ LOG_MODULE_REGISTER(udc_stm32, CONFIG_UDC_DRIVER_LOG_LEVEL); (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 @@ -136,6 +147,13 @@ static const int syscfg_otg_hs_phy_clk[] = { }; #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; @@ -150,14 +168,13 @@ struct udc_stm32_data { struct udc_stm32_config { uint32_t num_endpoints; - uint32_t pma_offset; uint32_t dram_size; - uint16_t ep0_mps; - uint16_t ep_mps; /* 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 { @@ -188,19 +205,20 @@ void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd) { struct udc_stm32_data *priv = hpcd2data(hpcd); const struct device *dev = priv->dev; - const struct udc_stm32_config *cfg = dev->config; 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, cfg->ep0_mps, + 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, cfg->ep0_mps, + HAL_PCD_EP_Open(&priv->pcd, USB_CONTROL_EP_IN, + UDC_STM32_EP0_MAX_PACKET_SIZE, EP_TYPE_CTRL); } @@ -288,7 +306,6 @@ 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); - const struct udc_stm32_config *cfg = dev->config; uint8_t *data; uint32_t len; HAL_StatusTypeDef status; @@ -302,7 +319,7 @@ static int udc_stm32_tx(const struct device *dev, struct udc_ep_config *epcfg, len = buf->len; if (epcfg->addr == USB_CONTROL_EP_IN) { - len = MIN(cfg->ep0_mps, buf->len); + len = MIN(UDC_STM32_EP0_MAX_PACKET_SIZE, buf->len); } buf->data += len; @@ -444,8 +461,7 @@ static void handle_msg_data_in(struct udc_stm32_data *priv, uint8_t epnum) } if (ep == USB_CONTROL_EP_IN && buf->len) { - const struct udc_stm32_config *cfg = dev->config; - uint32_t len = MIN(cfg->ep0_mps, buf->len); + uint32_t len = MIN(UDC_STM32_EP0_MAX_PACKET_SIZE, buf->len); HAL_PCD_EP_Transmit(&priv->pcd, ep, buf->data, len); @@ -603,7 +619,12 @@ 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; - priv->occupied_mem = cfg->pma_offset; + /** + * 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, @@ -639,26 +660,31 @@ 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; - int words; + uint32_t rxfifo_size; /* in words */ - LOG_DBG("DRAM size: %ub", cfg->dram_size); + LOG_DBG("DRAM size: %uB", cfg->dram_size); - if (cfg->ep_mps % 4 || cfg->ep0_mps % 4) { - LOG_ERR("Not a 32-bit word multiple: ep0(%u)|ep(%u)", - cfg->ep0_mps, cfg->ep_mps); - return; - } - - /* The documentation is not clear at all about RX FiFo size requirement, - * 160 has been selected through trial and error. + /* + * 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. */ - words = MAX(160, cfg->ep_mps / 4); - HAL_PCDEx_SetRxFiFo(&priv->pcd, words); - priv->occupied_mem = words * 4; + 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, cfg->ep0_mps / 4); - priv->occupied_mem += cfg->ep0_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++) { @@ -678,7 +704,7 @@ static int udc_stm32_ep_mem_config(const struct device *dev, return 0; } - words = MIN(udc_mps_ep_size(ep), cfg->ep_mps) / 4; + words = DIV_ROUND_UP(MIN(udc_mps_ep_size(ep), cfg->ep_mps), 4U); words = (words <= 64) ? words * 2 : words; if (!enable) { @@ -705,7 +731,6 @@ static int udc_stm32_ep_mem_config(const struct device *dev, static int udc_stm32_enable(const struct device *dev) { struct udc_stm32_data *priv = udc_get_private(dev); - const struct udc_stm32_config *cfg = dev->config; HAL_StatusTypeDef status; int ret; @@ -720,14 +745,16 @@ static int udc_stm32_enable(const struct device *dev) } ret = udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, - USB_EP_TYPE_CONTROL, cfg->ep0_mps, 0); + 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, cfg->ep0_mps, 0); + 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; @@ -1066,22 +1093,7 @@ static const struct udc_api udc_stm32_api = { * Kconfig system. */ #define USB_NUM_BIDIR_ENDPOINTS DT_INST_PROP(0, num_bidir_endpoints) - -#if defined(USB) || defined(USB_DRD_FS) -#define EP0_MPS 64U -#define EP_MPS 64U -#define USB_BTABLE_SIZE (8 * USB_NUM_BIDIR_ENDPOINTS) -#define USB_RAM_SIZE DT_INST_PROP(0, ram_size) -#else /* USB_OTG_FS */ -#define EP0_MPS USB_OTG_MAX_EP0_SIZE -#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) -#define EP_MPS USB_OTG_HS_MAX_PACKET_SIZE -#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs) || DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb) -#define EP_MPS USB_OTG_FS_MAX_PACKET_SIZE -#endif -#define USB_RAM_SIZE DT_INST_PROP(0, ram_size) -#define USB_BTABLE_SIZE 0 -#endif /* USB */ +#define USB_RAM_SIZE DT_INST_PROP(0, ram_size) static struct udc_stm32_data udc0_priv; @@ -1093,9 +1105,7 @@ static struct udc_data udc0_data = { static const struct udc_stm32_config udc0_cfg = { .num_endpoints = USB_NUM_BIDIR_ENDPOINTS, .dram_size = USB_RAM_SIZE, - .pma_offset = USB_BTABLE_SIZE, - .ep0_mps = EP0_MPS, - .ep_mps = EP_MPS, + .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)), }; @@ -1109,7 +1119,7 @@ static void priv_pcd_prepare(const struct device *dev) /* Default values */ priv->pcd.Init.dev_endpoints = cfg->num_endpoints; - priv->pcd.Init.ep0_mps = cfg->ep0_mps; + priv->pcd.Init.ep0_mps = UDC_STM32_EP0_MAX_PACKET_SIZE; priv->pcd.Init.speed = cfg->selected_speed; /* Per controller/Phy values */ @@ -1332,7 +1342,7 @@ static int udc_stm32_driver_init0(const struct device *dev) ep_cfg_out[i].caps.out = 1; if (i == 0) { ep_cfg_out[i].caps.control = 1; - ep_cfg_out[i].caps.mps = cfg->ep0_mps; + 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; @@ -1352,7 +1362,7 @@ static int udc_stm32_driver_init0(const struct device *dev) ep_cfg_in[i].caps.in = 1; if (i == 0) { ep_cfg_in[i].caps.control = 1; - ep_cfg_in[i].caps.mps = cfg->ep0_mps; + 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;