Skip to content

usb: device_next: hid: Cherry-pick USB next fixes #1762

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 4, 2024
2 changes: 1 addition & 1 deletion cmake/linker_script/common/common-ram.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ endif()

if(CONFIG_USB_DEVICE_STACK OR CONFIG_USB_DEVICE_STACK_NEXT)
zephyr_iterable_section(NAME usb_cfg_data GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
zephyr_iterable_section(NAME usbd_contex GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
zephyr_iterable_section(NAME usbd_context GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
zephyr_iterable_section(NAME usbd_class_fs GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
zephyr_iterable_section(NAME usbd_class_hs GROUP DATA_REGION ${XIP_ALIGN_WITH_INPUT} SUBALIGN CONFIG_LINKER_ITERABLE_SUBALIGN)
endif()
Expand Down
125 changes: 71 additions & 54 deletions drivers/usb/udc/udc_dwc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -928,51 +928,6 @@ static void udc_dwc2_isr_handler(const struct device *dev)
(void)dwc2_quirk_irq_clear(dev);
}

static int udc_dwc2_ep_enqueue(const struct device *dev,
struct udc_ep_config *const cfg,
struct net_buf *const buf)
{
struct dwc2_drv_event evt = {
.ep = cfg->addr,
.type = DWC2_DRV_EVT_XFER,
};

LOG_DBG("%p enqueue %x %p", dev, cfg->addr, buf);
udc_buf_put(cfg, buf);

if (!cfg->stat.halted) {
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}

return 0;
}

static int udc_dwc2_ep_dequeue(const struct device *dev,
struct udc_ep_config *const cfg)
{
unsigned int lock_key;
struct net_buf *buf;

lock_key = irq_lock();

if (USB_EP_DIR_IS_IN(cfg->addr)) {
dwc2_flush_tx_fifo(dev, USB_EP_GET_IDX(cfg->addr));
}

buf = udc_buf_get_all(dev, cfg->addr);
if (buf) {
udc_submit_ep_event(dev, buf, -ECONNABORTED);
}

irq_unlock(lock_key);

udc_ep_set_busy(dev, cfg->addr, false);

LOG_DBG("dequeue ep 0x%02x", cfg->addr);

return 0;
}

static void dwc2_unset_unused_fifo(const struct device *dev)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
Expand Down Expand Up @@ -1221,7 +1176,8 @@ static int dwc2_unset_dedicated_fifo(const struct device *dev,
return 0;
}

static void dwc2_wait_for_bit(mem_addr_t addr, uint32_t bit)
static void dwc2_wait_for_bit(const struct device *dev,
mem_addr_t addr, uint32_t bit)
{
k_timepoint_t timeout = sys_timepoint_calc(K_MSEC(100));

Expand All @@ -1232,6 +1188,13 @@ static void dwc2_wait_for_bit(mem_addr_t addr, uint32_t bit)
* Busy looping is most likely fine unless profiling shows otherwise.
*/
while (!(sys_read32(addr) & bit)) {
if (dwc2_quirk_is_phy_clk_off(dev)) {
/* No point in waiting, because the bit can only be set
* when the PHY is actively clocked.
*/
return;
}

if (sys_timepoint_expired(timeout)) {
LOG_ERR("Timeout waiting for bit 0x%08X at 0x%08X",
bit, (uint32_t)addr);
Expand All @@ -1258,6 +1221,16 @@ static void udc_dwc2_ep_disable(const struct device *dev,
dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
dxepctl = sys_read32(dxepctl_reg);

if (dxepctl & USB_DWC2_DEPCTL_NAKSTS) {
/* Endpoint already sends forced NAKs. STALL if necessary. */
if (stall) {
dxepctl |= USB_DWC2_DEPCTL_STALL;
sys_write32(dxepctl, dxepctl_reg);
}

return;
}

if (USB_EP_DIR_IS_OUT(cfg->addr)) {
mem_addr_t dctl_reg, gintsts_reg, doepint_reg;
uint32_t dctl;
Expand All @@ -1276,9 +1249,13 @@ static void udc_dwc2_ep_disable(const struct device *dev,
dctl &= ~USB_DWC2_DCTL_SGOUTNAK;
}

dwc2_wait_for_bit(gintsts_reg, USB_DWC2_GINTSTS_GOUTNAKEFF);
dwc2_wait_for_bit(dev, gintsts_reg, USB_DWC2_GINTSTS_GOUTNAKEFF);

/* The application cannot disable control OUT endpoint 0. */
if (ep_idx != 0) {
dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPDIS;
}

dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPDIS;
if (stall) {
/* For OUT endpoints STALL is set instead of SNAK */
dxepctl |= USB_DWC2_DEPCTL_STALL;
Expand All @@ -1287,7 +1264,9 @@ static void udc_dwc2_ep_disable(const struct device *dev,
}
sys_write32(dxepctl, dxepctl_reg);

dwc2_wait_for_bit(doepint_reg, USB_DWC2_DOEPINT_EPDISBLD);
if (ep_idx != 0) {
dwc2_wait_for_bit(dev, doepint_reg, USB_DWC2_DOEPINT_EPDISBLD);
}

/* Clear Endpoint Disabled interrupt */
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, doepint_reg);
Expand All @@ -1306,12 +1285,12 @@ static void udc_dwc2_ep_disable(const struct device *dev,
}
sys_write32(dxepctl, dxepctl_reg);

dwc2_wait_for_bit(diepint_reg, USB_DWC2_DIEPINT_INEPNAKEFF);
dwc2_wait_for_bit(dev, diepint_reg, USB_DWC2_DIEPINT_INEPNAKEFF);

dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPDIS;
sys_write32(dxepctl, dxepctl_reg);

dwc2_wait_for_bit(diepint_reg, USB_DWC2_DIEPINT_EPDISBLD);
dwc2_wait_for_bit(dev, diepint_reg, USB_DWC2_DIEPINT_EPDISBLD);

/* Clear Endpoint Disabled interrupt */
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, diepint_reg);
Expand Down Expand Up @@ -1404,6 +1383,44 @@ static int udc_dwc2_ep_clear_halt(const struct device *dev,
return 0;
}

static int udc_dwc2_ep_enqueue(const struct device *dev,
struct udc_ep_config *const cfg,
struct net_buf *const buf)
{
struct dwc2_drv_event evt = {
.ep = cfg->addr,
.type = DWC2_DRV_EVT_XFER,
};

LOG_DBG("%p enqueue %x %p", dev, cfg->addr, buf);
udc_buf_put(cfg, buf);

if (!cfg->stat.halted) {
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}

return 0;
}

static int udc_dwc2_ep_dequeue(const struct device *dev,
struct udc_ep_config *const cfg)
{
struct net_buf *buf;

udc_dwc2_ep_disable(dev, cfg, false);

buf = udc_buf_get_all(dev, cfg->addr);
if (buf) {
udc_submit_ep_event(dev, buf, -ECONNABORTED);
}

udc_ep_set_busy(dev, cfg->addr, false);

LOG_DBG("dequeue ep 0x%02x", cfg->addr);

return 0;
}

static int udc_dwc2_set_address(const struct device *dev, const uint8_t addr)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
Expand Down Expand Up @@ -1712,9 +1729,6 @@ static int udc_dwc2_disable(const struct device *dev)
sys_set_bits(dctl_reg, USB_DWC2_DCTL_SFTDISCON);
LOG_DBG("Disable device %p", dev);

config->irq_disable_func(dev);
sys_clear_bits((mem_addr_t)&base->gahbcfg, USB_DWC2_GAHBCFG_GLBINTRMASK);

if (udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT)) {
LOG_DBG("Failed to disable control endpoint");
return -EIO;
Expand All @@ -1725,6 +1739,9 @@ static int udc_dwc2_disable(const struct device *dev)
return -EIO;
}

config->irq_disable_func(dev);
sys_clear_bits((mem_addr_t)&base->gahbcfg, USB_DWC2_GAHBCFG_GLBINTRMASK);

err = dwc2_quirk_disable(dev);
if (err) {
LOG_ERR("Quirk disable failed %d", err);
Expand Down
3 changes: 3 additions & 0 deletions drivers/usb/udc/udc_dwc2.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ struct dwc2_vendor_quirks {
int (*irq_clear)(const struct device *dev);
/* Called on driver pre-init */
int (*caps)(const struct device *dev);
/* Called while waiting for bits that require PHY to be clocked */
int (*is_phy_clk_off)(const struct device *dev);
};

/* Driver configuration per instance */
Expand Down Expand Up @@ -69,5 +71,6 @@ DWC2_QUIRK_FUNC_DEFINE(disable)
DWC2_QUIRK_FUNC_DEFINE(shutdown)
DWC2_QUIRK_FUNC_DEFINE(irq_clear)
DWC2_QUIRK_FUNC_DEFINE(caps)
DWC2_QUIRK_FUNC_DEFINE(is_phy_clk_off)

#endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_H */
6 changes: 6 additions & 0 deletions drivers/usb/udc/udc_dwc2_vendor_quirks.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ static inline int usbhs_init_caps(const struct device *dev)
return 0;
}

static inline int usbhs_is_phy_clk_off(const struct device *dev)
{
return !k_event_test(&usbhs_events, USBHS_VBUS_READY);
}

#define QUIRK_NRF_USBHS_DEFINE(n) \
struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \
.init = usbhs_enable_nrfs_service, \
Expand All @@ -252,6 +257,7 @@ static inline int usbhs_init_caps(const struct device *dev)
.shutdown = usbhs_disable_nrfs_service, \
.irq_clear = usbhs_irq_clear, \
.caps = usbhs_init_caps, \
.is_phy_clk_off = usbhs_is_phy_clk_off, \
};

DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE)
Expand Down
6 changes: 4 additions & 2 deletions include/zephyr/usb/class/usbd_hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,10 @@ struct hid_device_ops {
* feature, input, or output report, which is specified by the argument
* type. If there is no report ID in the report descriptor, the id
* argument is zero. The callback implementation must check the
* arguments, such as whether the report type is supported, and return
* a nonzero value to indicate an unsupported type or an error.
* arguments, such as whether the report type is supported and the
* report length, and return a negative value to indicate an
* unsupported type or an error, or return the length of the report
* written to the buffer.
*/
int (*get_report)(const struct device *dev,
const uint8_t type, const uint8_t id,
Expand Down
Loading
Loading