diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 0f744ca4397..f533490da25 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -54,12 +54,6 @@ K_MSGQ_DEFINE(drv_msgq, sizeof(struct dwc2_drv_event), /* TX FIFO0 depth in 32-bit words (used by control IN endpoint) */ #define UDC_DWC2_FIFO0_DEPTH 16U -/* Number of endpoints supported by the driver. - * This must be equal to or greater than the number supported by the hardware. - * (FIXME) - */ -#define UDC_DWC2_DRV_EP_NUM 8 - /* Get Data FIFO access register */ #define UDC_DWC2_EP_FIFO(base, idx) ((mem_addr_t)base + 0x1000 * (idx + 1)) @@ -1613,6 +1607,8 @@ static int dwc2_driver_preinit(const struct device *dev) const struct udc_dwc2_config *config = dev->config; struct udc_data *data = dev->data; uint16_t mps = 1023; + uint32_t numdeveps; + uint32_t ineps; int err; k_mutex_init(&data->mutex); @@ -1620,49 +1616,93 @@ static int dwc2_driver_preinit(const struct device *dev) data->caps.rwup = true; data->caps.addr_before_status = true; data->caps.mps0 = UDC_MPS0_64; - if (config->speed_idx == 2) { - data->caps.hs = true; + + (void)dwc2_quirk_caps(dev); + if (data->caps.hs) { mps = 1024; } - for (int i = 0; i < config->num_of_eps; i++) { - config->ep_cfg_out[i].caps.out = 1; - if (i == 0) { - config->ep_cfg_out[i].caps.control = 1; - config->ep_cfg_out[i].caps.mps = 64; - } else { - config->ep_cfg_out[i].caps.bulk = 1; - config->ep_cfg_out[i].caps.interrupt = 1; - config->ep_cfg_out[i].caps.iso = 1; - config->ep_cfg_out[i].caps.mps = mps; + /* + * At this point, we cannot or do not want to access the hardware + * registers to get GHWCFGn values. For now, we will use devicetree to + * get GHWCFGn values and use them to determine the number and type of + * configured endpoints in the hardware. This can be considered a + * workaround, and we may change the upper layer internals to avoid it + * in the future. + */ + ineps = usb_dwc2_get_ghwcfg4_ineps(config->ghwcfg4) + 1U; + numdeveps = usb_dwc2_get_ghwcfg2_numdeveps(config->ghwcfg2) + 1U; + LOG_DBG("Number of endpoints (NUMDEVEPS + 1) %u", numdeveps); + LOG_DBG("Number of IN endpoints (INEPS + 1) %u", ineps); + + for (uint32_t i = 0, n = 0; i < numdeveps; i++) { + uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(config->ghwcfg1, i); + + if (epdir != USB_DWC2_GHWCFG1_EPDIR_OUT && + epdir != USB_DWC2_GHWCFG1_EPDIR_BDIR) { + continue; } - config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; - err = udc_register_ep(dev, &config->ep_cfg_out[i]); + if (i == 0) { + config->ep_cfg_out[n].caps.control = 1; + config->ep_cfg_out[n].caps.mps = 64; + } else { + config->ep_cfg_out[n].caps.bulk = 1; + config->ep_cfg_out[n].caps.interrupt = 1; + config->ep_cfg_out[n].caps.iso = 1; + config->ep_cfg_out[n].caps.mps = mps; + } + + config->ep_cfg_out[n].caps.out = 1; + config->ep_cfg_out[n].addr = USB_EP_DIR_OUT | i; + + LOG_DBG("Register ep 0x%02x (%u)", i, n); + err = udc_register_ep(dev, &config->ep_cfg_out[n]); if (err != 0) { LOG_ERR("Failed to register endpoint"); return err; } + + n++; + /* Also check the number of desired OUT endpoints in devicetree. */ + if (n >= config->num_out_eps) { + break; + } } - for (int i = 0; i < config->num_of_eps; i++) { - config->ep_cfg_in[i].caps.in = 1; - if (i == 0) { - config->ep_cfg_in[i].caps.control = 1; - config->ep_cfg_in[i].caps.mps = 64; - } else { - config->ep_cfg_in[i].caps.bulk = 1; - config->ep_cfg_in[i].caps.interrupt = 1; - config->ep_cfg_in[i].caps.iso = 1; - config->ep_cfg_in[i].caps.mps = mps; + for (uint32_t i = 0, n = 0; i < numdeveps; i++) { + uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(config->ghwcfg1, i); + + if (epdir != USB_DWC2_GHWCFG1_EPDIR_IN && + epdir != USB_DWC2_GHWCFG1_EPDIR_BDIR) { + continue; } - config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; - err = udc_register_ep(dev, &config->ep_cfg_in[i]); + if (i == 0) { + config->ep_cfg_in[n].caps.control = 1; + config->ep_cfg_in[n].caps.mps = 64; + } else { + config->ep_cfg_in[n].caps.bulk = 1; + config->ep_cfg_in[n].caps.interrupt = 1; + config->ep_cfg_in[n].caps.iso = 1; + config->ep_cfg_in[n].caps.mps = mps; + } + + config->ep_cfg_in[n].caps.in = 1; + config->ep_cfg_in[n].addr = USB_EP_DIR_IN | i; + + LOG_DBG("Register ep 0x%02x (%u)", USB_EP_DIR_IN | i, n); + err = udc_register_ep(dev, &config->ep_cfg_in[n]); if (err != 0) { LOG_ERR("Failed to register endpoint"); return err; } + + n++; + /* Also check the number of desired IN endpoints in devicetree. */ + if (n >= MIN(ineps, config->num_in_eps)) { + break; + } } config->make_thread(dev); @@ -1770,19 +1810,23 @@ static const struct udc_api udc_dwc2_api = { irq_disable(DT_INST_IRQN(n)); \ } \ \ - static struct udc_ep_config ep_cfg_out[UDC_DWC2_DRV_EP_NUM]; \ - static struct udc_ep_config ep_cfg_in[UDC_DWC2_DRV_EP_NUM]; \ + static struct udc_ep_config ep_cfg_out[DT_INST_PROP(n, num_out_eps)]; \ + static struct udc_ep_config ep_cfg_in[DT_INST_PROP(n, num_in_eps)]; \ \ static const struct udc_dwc2_config udc_dwc2_config_##n = { \ - .num_of_eps = UDC_DWC2_DRV_EP_NUM, \ - .ep_cfg_in = ep_cfg_out, \ - .ep_cfg_out = ep_cfg_in, \ + .num_out_eps = DT_INST_PROP(n, num_out_eps), \ + .num_in_eps = DT_INST_PROP(n, num_in_eps), \ + .ep_cfg_in = ep_cfg_in, \ + .ep_cfg_out = ep_cfg_out, \ .make_thread = udc_dwc2_make_thread_##n, \ .base = (struct usb_dwc2_reg *)UDC_DWC2_DT_INST_REG_ADDR(n), \ .pcfg = UDC_DWC2_PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ .irq_enable_func = udc_dwc2_irq_enable_func_##n, \ .irq_disable_func = udc_dwc2_irq_disable_func_##n, \ .quirks = UDC_DWC2_VENDOR_QUIRK_GET(n), \ + .ghwcfg1 = DT_INST_PROP(n, ghwcfg1), \ + .ghwcfg2 = DT_INST_PROP(n, ghwcfg2), \ + .ghwcfg4 = DT_INST_PROP(n, ghwcfg4), \ }; \ \ static struct udc_dwc2_data udc_priv_##n = { \ diff --git a/drivers/usb/udc/udc_dwc2.h b/drivers/usb/udc/udc_dwc2.h index 37c9a1eca38..b54cb82fd2a 100644 --- a/drivers/usb/udc/udc_dwc2.h +++ b/drivers/usb/udc/udc_dwc2.h @@ -26,14 +26,16 @@ struct dwc2_vendor_quirks { int (*shutdown)(const struct device *dev); /* Called at the end of IRQ handling */ int (*irq_clear)(const struct device *dev); + /* Called on driver pre-init */ + int (*caps)(const struct device *dev); }; /* Driver configuration per instance */ struct udc_dwc2_config { - size_t num_of_eps; + size_t num_in_eps; + size_t num_out_eps; struct udc_ep_config *ep_cfg_in; struct udc_ep_config *ep_cfg_out; - int speed_idx; struct usb_dwc2_reg *const base; /* Pointer to pin control configuration or NULL */ struct pinctrl_dev_config *const pcfg; @@ -42,6 +44,9 @@ struct udc_dwc2_config { void (*make_thread)(const struct device *dev); void (*irq_enable_func)(const struct device *dev); void (*irq_disable_func)(const struct device *dev); + uint32_t ghwcfg1; + uint32_t ghwcfg2; + uint32_t ghwcfg4; }; #define DWC2_QUIRK_FUNC_DEFINE(fname) \ @@ -63,5 +68,6 @@ DWC2_QUIRK_FUNC_DEFINE(post_enable) DWC2_QUIRK_FUNC_DEFINE(disable) DWC2_QUIRK_FUNC_DEFINE(shutdown) DWC2_QUIRK_FUNC_DEFINE(irq_clear) +DWC2_QUIRK_FUNC_DEFINE(caps) #endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_H */ diff --git a/dts/arm/intel_socfpga_std/socfpga.dtsi b/dts/arm/intel_socfpga_std/socfpga.dtsi index 7e1f37267d5..ad36609bfa1 100644 --- a/dts/arm/intel_socfpga_std/socfpga.dtsi +++ b/dts/arm/intel_socfpga_std/socfpga.dtsi @@ -227,6 +227,11 @@ reg = <0xffb30000 0xffff>; interrupts = <0 127 4 IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&intc>; + num-out-eps = <16>; + num-in-eps = <16>; + ghwcfg1 = <0x00000000>; + ghwcfg2 = <0x208ffc90>; + ghwcfg4 = <0xfe0f0020>; status = "disabled"; }; @@ -235,6 +240,11 @@ reg = <0xffb40000 0xffff>; interrupts = <0 128 4 IRQ_DEFAULT_PRIORITY>; interrupt-parent = <&intc>; + num-out-eps = <16>; + num-in-eps = <16>; + ghwcfg1 = <0x00000000>; + ghwcfg2 = <0x208ffc90>; + ghwcfg4 = <0xfe0f0020>; status = "okay"; }; diff --git a/dts/bindings/usb/snps,dwc2.yaml b/dts/bindings/usb/snps,dwc2.yaml index 8af55448780..d5b0167f26b 100644 --- a/dts/bindings/usb/snps,dwc2.yaml +++ b/dts/bindings/usb/snps,dwc2.yaml @@ -18,3 +18,36 @@ properties: phys: type: phandle + + num-in-eps: + type: int + required: true + description: | + Number of configured OUT endpoints including control endpoint. + + num-out-eps: + type: int + required: true + description: | + Number of configured IN endpoints including control endpoint. + + ghwcfg1: + type: int + required: true + description: | + Value of the GHWCFG1 register. It is used to determine available endpoint + types during driver pre-initialization. + + ghwcfg2: + type: int + required: true + description: | + Value of the GHWCFG2 register. It is used to determine available endpoint + types during driver pre-initialization. + + ghwcfg4: + type: int + required: true + description: | + Value of the GHWCFG4 register. It is used to determine available endpoint + types during driver pre-initialization. diff --git a/samples/subsys/usb/cdc_acm/nucleo_f413zh_dwc2.overlay b/samples/subsys/usb/cdc_acm/nucleo_f413zh_dwc2.overlay index 8cd2525699c..d1e6b29edbd 100644 --- a/samples/subsys/usb/cdc_acm/nucleo_f413zh_dwc2.overlay +++ b/samples/subsys/usb/cdc_acm/nucleo_f413zh_dwc2.overlay @@ -15,6 +15,11 @@ interrupt-names = "fsotg"; clocks = <&rcc STM32_CLOCK_BUS_AHB2 0x00000080>, <&rcc STM32_SRC_PLL_Q NO_SEL>; + num-out-eps = <6>; + num-in-eps = <6>; + ghwcfg1 = <0x00000000>; + ghwcfg2 = <0x229ed520>; + ghwcfg4 = <0x17f08030>; status = "disabled"; }; }; diff --git a/samples/subsys/usb/shell/nucleo_f413zh_dwc2.overlay b/samples/subsys/usb/shell/nucleo_f413zh_dwc2.overlay index ff51b08078e..2e954626ded 100644 --- a/samples/subsys/usb/shell/nucleo_f413zh_dwc2.overlay +++ b/samples/subsys/usb/shell/nucleo_f413zh_dwc2.overlay @@ -15,6 +15,11 @@ interrupt-names = "fsotg"; clocks = <&rcc STM32_CLOCK_BUS_AHB2 0x00000080>, <&rcc STM32_SRC_PLL_Q NO_SEL>; + num-out-eps = <6>; + num-in-eps = <6>; + ghwcfg1 = <0x00000000>; + ghwcfg2 = <0x229ed520>; + ghwcfg4 = <0x17f08030>; }; }; };