Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 129 additions & 38 deletions drivers/usb/udc/udc_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,41 +269,11 @@ static bool ep_check_config(const struct device *dev,
return true;
}

static void ep_update_mps(const struct device *dev,
const struct udc_ep_config *const cfg,
const uint8_t attributes,
uint16_t *const mps)
{
struct udc_device_caps caps = udc_caps(dev);
const uint16_t spec_int_mps = caps.hs ? 1024 : 64;
const uint16_t spec_bulk_mps = caps.hs ? 512 : 64;

/*
* TODO: It does not take into account the actual speed of the
* bus after the RESET. Should be fixed/improved when the driver
* for high speed controller are ported.
*/
switch (ep_attrib_get_transfer(attributes)) {
case USB_EP_TYPE_BULK:
*mps = MIN(cfg->caps.mps, spec_bulk_mps);
break;
case USB_EP_TYPE_INTERRUPT:
*mps = MIN(cfg->caps.mps, spec_int_mps);
break;
case USB_EP_TYPE_CONTROL:
__fallthrough;
case USB_EP_TYPE_ISO:
__fallthrough;
default:
return;
}
}

int udc_ep_try_config(const struct device *dev,
const uint8_t ep,
const uint8_t attributes,
uint16_t *const mps,
const uint8_t interval)
int udc_ep_claim_config(const struct device *dev,
const uint8_t ep,
const uint8_t attributes,
const uint16_t mps,
const uint8_t interval)
{
const struct udc_api *api = dev->api;
struct udc_ep_config *cfg;
Expand All @@ -316,16 +286,128 @@ int udc_ep_try_config(const struct device *dev,

api->lock(dev);

ret = ep_check_config(dev, cfg, ep, attributes, *mps, interval);
if (ret == true && *mps == 0U) {
ep_update_mps(dev, cfg, attributes, mps);
ret = ep_check_config(dev, cfg, ep, attributes, mps, interval);
if (ret) {
cfg->m_mps = MAX(cfg->m_mps, mps);
cfg->stat.claimed = 1;
}

api->unlock(dev);

return (ret == false) ? -ENOTSUP : 0;
}

void udc_get_eps_fifo_size(const struct device *dev,
size_t *const rx_size,
size_t *const tx_size,
uint16_t *const out_mps,
uint16_t *const in_mps)
{
struct udc_data *const data = dev->data;
uint16_t t_out_mps = 0;
uint16_t t_in_mps = 0;
size_t t_rx_size = 0;
size_t t_tx_size = 0;

for (unsigned int i = 0; i < ARRAY_SIZE(data->ep_lut); i++) {
struct udc_ep_config *const cfg = data->ep_lut[i];
uint16_t tpl;

if (cfg == NULL || !cfg->stat.claimed) {
continue;
}

tpl = USB_MPS_TO_TPL(cfg->m_mps);

if (USB_EP_DIR_IS_IN(cfg->addr)) {
t_tx_size += ROUND_UP(tpl, 4);
if (tpl > USB_MPS_TO_TPL(t_in_mps)) {
t_in_mps = cfg->m_mps;
}
} else {
t_rx_size += ROUND_UP(tpl, 4);
if (tpl > USB_MPS_TO_TPL(t_out_mps)) {
t_out_mps = cfg->m_mps;
}
}
}

if (rx_size != NULL) {
*rx_size = t_rx_size;
}

if (tx_size != NULL) {
*tx_size = t_tx_size;
}

if (out_mps != NULL) {
*out_mps = t_out_mps;
}

if (in_mps != NULL) {
*in_mps = t_in_mps;
}
}

void udc_get_claimed_eps(const struct device *dev,
uint8_t *const out_eps, uint8_t *const in_eps)
{
struct udc_data *const data = dev->data;
uint16_t t_out_eps = 0;
uint16_t t_in_eps = 0;

for (unsigned int i = 0; i < ARRAY_SIZE(data->ep_lut); i++) {
struct udc_ep_config *const cfg = data->ep_lut[i];

if (cfg == NULL || !cfg->stat.claimed) {
continue;
}

if (USB_EP_DIR_IS_IN(cfg->addr)) {
t_in_eps++;
} else {
t_out_eps++;
}
}

if (out_eps != NULL) {
*out_eps = t_out_eps;
}

if (in_eps != NULL) {
*in_eps = t_in_eps;
}
}

static void udc_release_eps(const struct device *dev)
{
struct udc_data *const data = dev->data;

for (unsigned int i = 0; i < ARRAY_SIZE(data->ep_lut); i++) {
struct udc_ep_config *const cfg = data->ep_lut[i];

if (cfg != NULL) {
cfg->stat.claimed = 0;
cfg->m_mps = 0;
cfg->mps = 0;
}
}
}

static inline void udc_log_fifo_size(const struct device *dev)
{
size_t rx_size, tx_size;
uint16_t out_mps, in_mps;
uint8_t out_eps, in_eps;

udc_get_eps_fifo_size(dev, &rx_size, &tx_size, &out_mps, &in_mps);
LOG_DBG("FIFO size RX %u TX %u, MPS OUT 0x%04x IN 0x%04x",
rx_size, tx_size, out_mps, in_mps);

udc_get_claimed_eps(dev, &out_eps, &in_eps);
LOG_DBG("Number of used endpoints OUT %u IN %u", out_eps, in_eps);
}

int udc_ep_enable_internal(const struct device *dev,
const uint8_t ep,
const uint8_t attributes,
Expand All @@ -351,6 +433,11 @@ int udc_ep_enable_internal(const struct device *dev,
return -ENODEV;
}

if (USB_EP_GET_IDX(ep) && USB_MPS_TO_TPL(mps) > USB_MPS_TO_TPL(cfg->m_mps)) {
LOG_ERR("Endpoint 0x%02x MPS %u is too large", cfg->addr, mps);
return -EINVAL;
}

cfg->attributes = attributes;
cfg->mps = mps;
cfg->interval = interval;
Expand Down Expand Up @@ -714,6 +801,9 @@ int udc_enable(const struct device *dev)
}

data->stage = CTRL_PIPE_STAGE_SETUP;
if (UDC_COMMON_LOG_LEVEL == LOG_LEVEL_DBG) {
udc_log_fifo_size(dev);
}

ret = api->enable(dev);
if (ret == 0) {
Expand Down Expand Up @@ -766,6 +856,7 @@ int udc_init(const struct device *dev,
goto udc_init_error;
}

udc_release_eps(dev);
data->event_cb = event_cb;
data->event_ctx = event_ctx;

Expand Down
44 changes: 44 additions & 0 deletions drivers/usb/udc/udc_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,50 @@ static inline void udc_submit_sof_event(const struct device *dev)
#define udc_submit_sof_event(dev) ARG_UNUSED(dev)
#endif

/**
* @brief Estimate required FIFO sizes.
*
* This function estimates the required FIFO sizes for the IN and OUT
* endpoints, as well as determining the largest IN and OUT endpoint sizes
* based on the maximum packet size value provided by the higher layer during
* initialization.
*
* All numbers are without control endpoints. Drivers must take this into
* account.
*
* This function provides the correct numbers if the configurations have
* identical endpoint mapping. Otherwise, it provides worst-case RX and TX FIFO
* sizes.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] rx_size Required RX FIFO size
* @param[in] tx_size Required TX FIFO size
* @param[in] out_mps Largest OUT MPS
* @param[in] in_mps Largest IN MPS
*/
void udc_get_eps_fifo_size(const struct device *dev,
size_t *const rx_size,
size_t *const tx_size,
uint16_t *const out_mps,
uint16_t *const in_mps);

/**
* @brief Get number of claimed endpoints.
*
* This function provides the correct numbers if the configurations have
* identical endpoint mapping. Otherwise, it provides worst-case numbers.
*
* All numbers are without control endpoints. Drivers must take this into
* account.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] out_eps Number of claimed OUT endpoints
* @param[in] in_eps Number of claimed IN endpoints
*/
void udc_get_claimed_eps(const struct device *dev,
uint8_t *const out_eps,
uint8_t *const in_eps);

/**
* @brief Helper function to enable endpoint.
*
Expand Down
72 changes: 33 additions & 39 deletions drivers/usb/udc/udc_dwc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,20 @@ enum dwc2_drv_event_type {
DWC2_DRV_EVT_HIBERNATION_EXIT_HOST_RESUME,
};

/* Minimum RX FIFO size in 32-bit words considering the largest used OUT packet
* of 512 bytes. The value must be adjusted according to the number of OUT
* endpoints.
*/
#define UDC_DWC2_GRXFSIZ_FS_DEFAULT (15U + 512U/4U)
/* Default Rx FIFO size in 32-bit words calculated to support High-Speed with:
* * 1 control endpoint in Completer/Buffer DMA mode: 13 locations
* * Global OUT NAK: 1 location
* * Space for 3 * 1024 packets: ((1024/4) + 1) * 3 = 774 locations
* Driver adds 2 locations for each OUT endpoint to this value.
/* Default Rx FIFO size in 32-bit words calculated in Completer/Buffer DMA mode
*
* (5 * number of control endpoints + 8) +
* ((largest endpoints size / 4) + 1 for status) * n +
* (2 * number of OUT endpoints) + 1 for global NAK
*
* For example, largest endpoint size is 1024,
* there are 3 OUT endpoints plus control OUT endpoint,
* number of extra transactions to support high_bandwidth endpoints is 2:
*
* 13 + (1024/4 + 1) * (1 + 2) + 2 * 4 + 1 = 793 (3172 bytes)
*/
#define UDC_DWC2_GRXFSIZ_HS_DEFAULT (13 + 1 + 774)
#define UDC_DWC2_GRXFSIZ_DEFAULT(ep_size, extra, out_eps) \
(13 + ((ep_size) / 4 + 1) * (1 + (extra)) + (out_eps + 1) * 2 + 1)

/* TX FIFO0 depth in 32-bit words (used by control IN endpoint)
* Try 2 * bMaxPacketSize0 to allow simultaneous operation with a fallback to
Expand All @@ -66,9 +68,6 @@ enum dwc2_drv_event_type {
/* Get Data FIFO access register */
#define UDC_DWC2_EP_FIFO(base, idx) ((mem_addr_t)base + 0x1000 * (idx + 1))

/* Percentage limit of how much SPRAM can be allocated for RxFIFO */
#define MAX_RXFIFO_GDFIFO_PERCENTAGE 25

enum dwc2_suspend_type {
DWC2_SUSPEND_NO_POWER_SAVING,
DWC2_SUSPEND_HIBERNATION,
Expand Down Expand Up @@ -1391,7 +1390,11 @@ static int dwc2_set_dedicated_fifo(const struct device *dev,
/* Keep everything but FIFO number */
tmp = *diepctl & ~USB_DWC2_DEPCTL_TXFNUM_MASK;

reqdep = DIV_ROUND_UP(udc_mps_ep_size(cfg), 4U);
/* Use the maximum possible MPS value to ensure that the alternate
* setting does not result in too small memory window being allocated
* and locked because a higher FIFO is still in use.
*/
reqdep = DIV_ROUND_UP(USB_MPS_EP_SIZE(cfg->m_mps), 4U);
if (dwc2_in_buffer_dma_mode(dev)) {
/* In DMA mode, TxFIFO capable of holding 2 packets is enough */
reqdep *= MIN(2, (1 + addnl));
Expand Down Expand Up @@ -1555,7 +1558,7 @@ static int udc_dwc2_ep_activate(const struct device *dev,
return -EINVAL;
}

if (USB_EP_DIR_IS_IN(cfg->addr) && udc_mps_ep_size(cfg) != 0U) {
if (USB_EP_DIR_IS_IN(cfg->addr) && USB_MPS_EP_SIZE(cfg->m_mps) != 0U) {
int ret = dwc2_set_dedicated_fifo(dev, cfg, &dxepctl);

if (ret) {
Expand Down Expand Up @@ -2220,34 +2223,25 @@ static int udc_dwc2_init_controller(const struct device *dev)
if (priv->dynfifosizing) {
uint32_t gnptxfsiz;
uint32_t default_depth;
uint32_t spram_size;
uint32_t max_rxfifo;

/* Get available SPRAM size and calculate max allocatable RX fifo size */
val = sys_read32((mem_addr_t)&base->gdfifocfg);
spram_size = usb_dwc2_get_gdfifocfg_gdfifocfg(val);
max_rxfifo = ((spram_size * MAX_RXFIFO_GDFIFO_PERCENTAGE) / 100);

/* TODO: For proper runtime FIFO sizing UDC driver would have to
* have prior knowledge of the USB configurations. Only with the
* prior knowledge, the driver will be able to fairly distribute
* available resources. For the time being just use different
* defaults based on maximum configured PHY speed, but this has
* to be revised if e.g. thresholding support would be necessary
* on some target.
*/
if (hs_phy) {
default_depth = UDC_DWC2_GRXFSIZ_HS_DEFAULT;
} else {
default_depth = UDC_DWC2_GRXFSIZ_FS_DEFAULT;
}
default_depth += priv->outeps * 2U;
uint16_t out_ep_size;
uint8_t num_out_eps;
uint8_t extra;
uint16_t mps;

udc_get_eps_fifo_size(dev, NULL, NULL, &mps, NULL);
udc_get_claimed_eps(dev, &num_out_eps, NULL);
extra = USB_MPS_ADDITIONAL_TRANSACTIONS(mps);
out_ep_size = USB_MPS_EP_SIZE(mps);

LOG_DBG("Largest OUT MPS 0x%04x, TPL %ux %u bytes",
mps, extra + 1, out_ep_size);
default_depth = UDC_DWC2_GRXFSIZ_DEFAULT(out_ep_size, extra, num_out_eps);

/* Driver does not dynamically resize RxFIFO so there is no need
* to store reset value. Read the reset value and make sure that
* the programmed value is not greater than what driver sets.
*/
priv->rxfifo_depth = MIN(MIN(priv->rxfifo_depth, default_depth), max_rxfifo);
priv->rxfifo_depth = MIN(priv->rxfifo_depth, default_depth);
sys_write32(usb_dwc2_set_grxfsiz(priv->rxfifo_depth), grxfsiz_reg);

/* Set TxFIFO 0 depth */
Expand Down
13 changes: 13 additions & 0 deletions drivers/usb/udc/udc_rpi_pico.c
Original file line number Diff line number Diff line change
Expand Up @@ -1118,8 +1118,21 @@ static int udc_rpi_pico_init(const struct device *dev)
{
const struct rpi_pico_config *config = dev->config;
const struct pinctrl_dev_config *const pcfg = config->pcfg;
size_t rx_size;
size_t tx_size;
int err;

/*
* There are 3840 bytes available for endpoint buffers, including
* control endpoints, in DPSRAM. This means that 3712 bytes
* (58 x 64 byte blocks) are available for interface endpoints.
*/
udc_get_eps_fifo_size(dev, &rx_size, &tx_size, NULL, NULL);
if ((rx_size + tx_size) > 3712U) {
LOG_WRN("Required memory size %u + %u exceeds available DPRAM space %u",
rx_size, tx_size, 3712U);
}

if (pcfg != NULL) {
err = pinctrl_apply_state(pcfg, PINCTRL_STATE_DEFAULT);
if (err) {
Expand Down
Loading