Skip to content

Commit 6d06a8c

Browse files
jfischer-nofabiobaltieri
authored andcommitted
drivers: udc_dwc2: use devicetree to configure endpoint capabilities
Although we can get the number of configured OUT and IN endpoints and endpoint capabilities from the DWC GHWCFGn registers, we need to configure the number of endpoint configuration structs at build time. On some platforms, we cannot access the hardware register at pre-init, so we use the GHWCFGn values from the devicetree to provide endpoint capabilities. This can be considered a workaround, and we may change the upper layer internals to avoid it in the future. Also, add a new vendor quirk to fill in platform-specific controller capabilities. Signed-off-by: Johann Fischer <[email protected]>
1 parent efb286d commit 6d06a8c

File tree

6 files changed

+138
-35
lines changed

6 files changed

+138
-35
lines changed

drivers/usb/udc/udc_dwc2.c

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,6 @@ K_MSGQ_DEFINE(drv_msgq, sizeof(struct dwc2_drv_event),
5454
/* TX FIFO0 depth in 32-bit words (used by control IN endpoint) */
5555
#define UDC_DWC2_FIFO0_DEPTH 16U
5656

57-
/* Number of endpoints supported by the driver.
58-
* This must be equal to or greater than the number supported by the hardware.
59-
* (FIXME)
60-
*/
61-
#define UDC_DWC2_DRV_EP_NUM 8
62-
6357
/* Get Data FIFO access register */
6458
#define UDC_DWC2_EP_FIFO(base, idx) ((mem_addr_t)base + 0x1000 * (idx + 1))
6559

@@ -1613,56 +1607,102 @@ static int dwc2_driver_preinit(const struct device *dev)
16131607
const struct udc_dwc2_config *config = dev->config;
16141608
struct udc_data *data = dev->data;
16151609
uint16_t mps = 1023;
1610+
uint32_t numdeveps;
1611+
uint32_t ineps;
16161612
int err;
16171613

16181614
k_mutex_init(&data->mutex);
16191615

16201616
data->caps.rwup = true;
16211617
data->caps.addr_before_status = true;
16221618
data->caps.mps0 = UDC_MPS0_64;
1623-
if (config->speed_idx == 2) {
1624-
data->caps.hs = true;
1619+
1620+
(void)dwc2_quirk_caps(dev);
1621+
if (data->caps.hs) {
16251622
mps = 1024;
16261623
}
16271624

1628-
for (int i = 0; i < config->num_of_eps; i++) {
1629-
config->ep_cfg_out[i].caps.out = 1;
1625+
/*
1626+
* At this point, we cannot or do not want to access the hardware
1627+
* registers to get GHWCFGn values. For now, we will use devicetree to
1628+
* get GHWCFGn values and use them to determine the number and type of
1629+
* configured endpoints in the hardware. This can be considered a
1630+
* workaround, and we may change the upper layer internals to avoid it
1631+
* in the future.
1632+
*/
1633+
ineps = usb_dwc2_get_ghwcfg4_ineps(config->ghwcfg4) + 1U;
1634+
numdeveps = usb_dwc2_get_ghwcfg2_numdeveps(config->ghwcfg2) + 1U;
1635+
LOG_DBG("Number of endpoints (NUMDEVEPS + 1) %u", numdeveps);
1636+
LOG_DBG("Number of IN endpoints (INEPS + 1) %u", ineps);
1637+
1638+
for (uint32_t i = 0, n = 0; i < numdeveps; i++) {
1639+
uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(config->ghwcfg1, i);
1640+
1641+
if (epdir != USB_DWC2_GHWCFG1_EPDIR_OUT &&
1642+
epdir != USB_DWC2_GHWCFG1_EPDIR_BDIR) {
1643+
continue;
1644+
}
1645+
16301646
if (i == 0) {
1631-
config->ep_cfg_out[i].caps.control = 1;
1632-
config->ep_cfg_out[i].caps.mps = 64;
1647+
config->ep_cfg_out[n].caps.control = 1;
1648+
config->ep_cfg_out[n].caps.mps = 64;
16331649
} else {
1634-
config->ep_cfg_out[i].caps.bulk = 1;
1635-
config->ep_cfg_out[i].caps.interrupt = 1;
1636-
config->ep_cfg_out[i].caps.iso = 1;
1637-
config->ep_cfg_out[i].caps.mps = mps;
1650+
config->ep_cfg_out[n].caps.bulk = 1;
1651+
config->ep_cfg_out[n].caps.interrupt = 1;
1652+
config->ep_cfg_out[n].caps.iso = 1;
1653+
config->ep_cfg_out[n].caps.mps = mps;
16381654
}
16391655

1640-
config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i;
1641-
err = udc_register_ep(dev, &config->ep_cfg_out[i]);
1656+
config->ep_cfg_out[n].caps.out = 1;
1657+
config->ep_cfg_out[n].addr = USB_EP_DIR_OUT | i;
1658+
1659+
LOG_DBG("Register ep 0x%02x (%u)", i, n);
1660+
err = udc_register_ep(dev, &config->ep_cfg_out[n]);
16421661
if (err != 0) {
16431662
LOG_ERR("Failed to register endpoint");
16441663
return err;
16451664
}
1665+
1666+
n++;
1667+
/* Also check the number of desired OUT endpoints in devicetree. */
1668+
if (n >= config->num_out_eps) {
1669+
break;
1670+
}
16461671
}
16471672

1648-
for (int i = 0; i < config->num_of_eps; i++) {
1649-
config->ep_cfg_in[i].caps.in = 1;
1673+
for (uint32_t i = 0, n = 0; i < numdeveps; i++) {
1674+
uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(config->ghwcfg1, i);
1675+
1676+
if (epdir != USB_DWC2_GHWCFG1_EPDIR_IN &&
1677+
epdir != USB_DWC2_GHWCFG1_EPDIR_BDIR) {
1678+
continue;
1679+
}
1680+
16501681
if (i == 0) {
1651-
config->ep_cfg_in[i].caps.control = 1;
1652-
config->ep_cfg_in[i].caps.mps = 64;
1682+
config->ep_cfg_in[n].caps.control = 1;
1683+
config->ep_cfg_in[n].caps.mps = 64;
16531684
} else {
1654-
config->ep_cfg_in[i].caps.bulk = 1;
1655-
config->ep_cfg_in[i].caps.interrupt = 1;
1656-
config->ep_cfg_in[i].caps.iso = 1;
1657-
config->ep_cfg_in[i].caps.mps = mps;
1685+
config->ep_cfg_in[n].caps.bulk = 1;
1686+
config->ep_cfg_in[n].caps.interrupt = 1;
1687+
config->ep_cfg_in[n].caps.iso = 1;
1688+
config->ep_cfg_in[n].caps.mps = mps;
16581689
}
16591690

1660-
config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i;
1661-
err = udc_register_ep(dev, &config->ep_cfg_in[i]);
1691+
config->ep_cfg_in[n].caps.in = 1;
1692+
config->ep_cfg_in[n].addr = USB_EP_DIR_IN | i;
1693+
1694+
LOG_DBG("Register ep 0x%02x (%u)", USB_EP_DIR_IN | i, n);
1695+
err = udc_register_ep(dev, &config->ep_cfg_in[n]);
16621696
if (err != 0) {
16631697
LOG_ERR("Failed to register endpoint");
16641698
return err;
16651699
}
1700+
1701+
n++;
1702+
/* Also check the number of desired IN endpoints in devicetree. */
1703+
if (n >= MIN(ineps, config->num_in_eps)) {
1704+
break;
1705+
}
16661706
}
16671707

16681708
config->make_thread(dev);
@@ -1770,19 +1810,23 @@ static const struct udc_api udc_dwc2_api = {
17701810
irq_disable(DT_INST_IRQN(n)); \
17711811
} \
17721812
\
1773-
static struct udc_ep_config ep_cfg_out[UDC_DWC2_DRV_EP_NUM]; \
1774-
static struct udc_ep_config ep_cfg_in[UDC_DWC2_DRV_EP_NUM]; \
1813+
static struct udc_ep_config ep_cfg_out[DT_INST_PROP(n, num_out_eps)]; \
1814+
static struct udc_ep_config ep_cfg_in[DT_INST_PROP(n, num_in_eps)]; \
17751815
\
17761816
static const struct udc_dwc2_config udc_dwc2_config_##n = { \
1777-
.num_of_eps = UDC_DWC2_DRV_EP_NUM, \
1778-
.ep_cfg_in = ep_cfg_out, \
1779-
.ep_cfg_out = ep_cfg_in, \
1817+
.num_out_eps = DT_INST_PROP(n, num_out_eps), \
1818+
.num_in_eps = DT_INST_PROP(n, num_in_eps), \
1819+
.ep_cfg_in = ep_cfg_in, \
1820+
.ep_cfg_out = ep_cfg_out, \
17801821
.make_thread = udc_dwc2_make_thread_##n, \
17811822
.base = (struct usb_dwc2_reg *)UDC_DWC2_DT_INST_REG_ADDR(n), \
17821823
.pcfg = UDC_DWC2_PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
17831824
.irq_enable_func = udc_dwc2_irq_enable_func_##n, \
17841825
.irq_disable_func = udc_dwc2_irq_disable_func_##n, \
17851826
.quirks = UDC_DWC2_VENDOR_QUIRK_GET(n), \
1827+
.ghwcfg1 = DT_INST_PROP(n, ghwcfg1), \
1828+
.ghwcfg2 = DT_INST_PROP(n, ghwcfg2), \
1829+
.ghwcfg4 = DT_INST_PROP(n, ghwcfg4), \
17861830
}; \
17871831
\
17881832
static struct udc_dwc2_data udc_priv_##n = { \

drivers/usb/udc/udc_dwc2.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ struct dwc2_vendor_quirks {
2626
int (*shutdown)(const struct device *dev);
2727
/* Called at the end of IRQ handling */
2828
int (*irq_clear)(const struct device *dev);
29+
/* Called on driver pre-init */
30+
int (*caps)(const struct device *dev);
2931
};
3032

3133
/* Driver configuration per instance */
3234
struct udc_dwc2_config {
33-
size_t num_of_eps;
35+
size_t num_in_eps;
36+
size_t num_out_eps;
3437
struct udc_ep_config *ep_cfg_in;
3538
struct udc_ep_config *ep_cfg_out;
36-
int speed_idx;
3739
struct usb_dwc2_reg *const base;
3840
/* Pointer to pin control configuration or NULL */
3941
struct pinctrl_dev_config *const pcfg;
@@ -42,6 +44,9 @@ struct udc_dwc2_config {
4244
void (*make_thread)(const struct device *dev);
4345
void (*irq_enable_func)(const struct device *dev);
4446
void (*irq_disable_func)(const struct device *dev);
47+
uint32_t ghwcfg1;
48+
uint32_t ghwcfg2;
49+
uint32_t ghwcfg4;
4550
};
4651

4752
#define DWC2_QUIRK_FUNC_DEFINE(fname) \
@@ -63,5 +68,6 @@ DWC2_QUIRK_FUNC_DEFINE(post_enable)
6368
DWC2_QUIRK_FUNC_DEFINE(disable)
6469
DWC2_QUIRK_FUNC_DEFINE(shutdown)
6570
DWC2_QUIRK_FUNC_DEFINE(irq_clear)
71+
DWC2_QUIRK_FUNC_DEFINE(caps)
6672

6773
#endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_H */

dts/arm/intel_socfpga_std/socfpga.dtsi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@
227227
reg = <0xffb30000 0xffff>;
228228
interrupts = <0 127 4 IRQ_DEFAULT_PRIORITY>;
229229
interrupt-parent = <&intc>;
230+
num-out-eps = <16>;
231+
num-in-eps = <16>;
232+
ghwcfg1 = <0x00000000>;
233+
ghwcfg2 = <0x208ffc90>;
234+
ghwcfg4 = <0xfe0f0020>;
230235
status = "disabled";
231236
};
232237

@@ -235,6 +240,11 @@
235240
reg = <0xffb40000 0xffff>;
236241
interrupts = <0 128 4 IRQ_DEFAULT_PRIORITY>;
237242
interrupt-parent = <&intc>;
243+
num-out-eps = <16>;
244+
num-in-eps = <16>;
245+
ghwcfg1 = <0x00000000>;
246+
ghwcfg2 = <0x208ffc90>;
247+
ghwcfg4 = <0xfe0f0020>;
238248
status = "okay";
239249
};
240250

dts/bindings/usb/snps,dwc2.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,36 @@ properties:
1818

1919
phys:
2020
type: phandle
21+
22+
num-in-eps:
23+
type: int
24+
required: true
25+
description: |
26+
Number of configured OUT endpoints including control endpoint.
27+
28+
num-out-eps:
29+
type: int
30+
required: true
31+
description: |
32+
Number of configured IN endpoints including control endpoint.
33+
34+
ghwcfg1:
35+
type: int
36+
required: true
37+
description: |
38+
Value of the GHWCFG1 register. It is used to determine available endpoint
39+
types during driver pre-initialization.
40+
41+
ghwcfg2:
42+
type: int
43+
required: true
44+
description: |
45+
Value of the GHWCFG2 register. It is used to determine available endpoint
46+
types during driver pre-initialization.
47+
48+
ghwcfg4:
49+
type: int
50+
required: true
51+
description: |
52+
Value of the GHWCFG4 register. It is used to determine available endpoint
53+
types during driver pre-initialization.

samples/subsys/usb/cdc_acm/nucleo_f413zh_dwc2.overlay

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
interrupt-names = "fsotg";
1616
clocks = <&rcc STM32_CLOCK_BUS_AHB2 0x00000080>,
1717
<&rcc STM32_SRC_PLL_Q NO_SEL>;
18+
num-out-eps = <6>;
19+
num-in-eps = <6>;
20+
ghwcfg1 = <0x00000000>;
21+
ghwcfg2 = <0x229ed520>;
22+
ghwcfg4 = <0x17f08030>;
1823
status = "disabled";
1924
};
2025
};

samples/subsys/usb/shell/nucleo_f413zh_dwc2.overlay

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
interrupt-names = "fsotg";
1616
clocks = <&rcc STM32_CLOCK_BUS_AHB2 0x00000080>,
1717
<&rcc STM32_SRC_PLL_Q NO_SEL>;
18+
num-out-eps = <6>;
19+
num-in-eps = <6>;
20+
ghwcfg1 = <0x00000000>;
21+
ghwcfg2 = <0x229ed520>;
22+
ghwcfg4 = <0x17f08030>;
1823
};
1924
};
2025
};

0 commit comments

Comments
 (0)