drivers: usb: add Nordic USBHS wrapper and USBHS BC12 drivers#106759
drivers: usb: add Nordic USBHS wrapper and USBHS BC12 drivers#106759jfischer-no wants to merge 5 commits intozephyrproject-rtos:mainfrom
Conversation
tmon-nordic
left a comment
There was a problem hiding this comment.
Significantly better than #105731 but there are errors in actual bit handling and the synchronization is currently only apparent. It should be possible to do proper synchronization with the overall structure.
| /* Additionally suspend PHY */ | ||
| inputoverride = USBHS_PHY_INPUTOVERRIDE_ID_Msk | | ||
| USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; |
There was a problem hiding this comment.
This path is not forcing PHY SUSPENDM0 to suspend mode, the code in if (suspended) does it.
| struct nrf_usbhs_wrapper_data *const data = dev->data; | ||
|
|
||
| wrapper_enable_phy_internal(dev, suspended); | ||
| atomic_inc(&data->refcount); |
There was a problem hiding this comment.
How is this synchronization working? Reference counter is atomic, but the actual wrapper accesses are in no way protected. I think a spin lock would be necessary (can change the refcount to just normal variable then).
There was a problem hiding this comment.
Synchronization between what? The UDC driver quirk does not access the wrapper until the BC12 driver finishes. The BC12 driver has preference over the UDC driver and always accesses wrapper registers from ISR context. UDC wrapper access seems to be doomed, but I could not observe it, perhaps because of the VREG hysteresis.
There was a problem hiding this comment.
BC12 role can be changed independently of UDC state. Input overrides are effectively controlled by both UDC and BC12 driver.
wrapper_enable_phy_internal() is not in any way synchronized by the use of atomic_inc() at all.
| /* Wait for PHY clock to start */ | ||
| k_busy_wait(45); |
There was a problem hiding this comment.
Who controls reference clock? PHY clock won't start without it.
There was a problem hiding this comment.
Yes, and it is still called from the UDC driver only.
| const void *bc12_cb_data; | ||
| regulator_callback_t phy_cb; | ||
| const void *phy_cb_data; | ||
| atomic_t refcount; |
There was a problem hiding this comment.
I think there should be separate flag for whether udc and bc12 are enabled. Refcount is not really suitable here because the actual override sequence does depend on enabled order (bc12 or udc first) and vregusb interrupt.
There was a problem hiding this comment.
No, I do not think so. Refcount is very well suited, otherwise it would not be there. It is also not about enabled order, when there is BC12 driver, it always takes the precedence.
There was a problem hiding this comment.
UDC can be enabled and be kept enabled all the time. BC12 needs to perform Battery Charging operations on every VBUS detection (if role is set to Portable Device) and then leave with the state suitable for UDC to continue its operation.
| const struct usbhs_bc_config *const config = dev->config; | ||
| NRF_USBHS_Type *const wrapper = config->base; | ||
|
|
||
| wrapper->PHY.OVERRIDEVALUES = USBHS_PHY_OVERRIDEVALUES_OPMODE0_Msk | |
There was a problem hiding this comment.
OPMODE0 has to be binary 01, not 11. BIT(USBHS_PHY_OVERRIDEVALUES_OPMODE0_Pos) will produce correct value.
| NRF_USBHS_Type *const wrapper = config->base; | ||
|
|
||
| wrapper->PHY.OVERRIDEVALUES = USBHS_PHY_OVERRIDEVALUES_OPMODE0_Msk | | ||
| USBHS_PHY_OVERRIDEVALUES_XCVRSEL0_Msk; |
There was a problem hiding this comment.
XCVRSEL0 has to be binary 01, not 11. BIT(USBHS_PHY_OVERRIDEVALUES_XCVRSEL0_Pos) will produce correct value.
| /* | ||
| * Enable VDP_SRC onto DP and keep it enabled until DWC2 driver | ||
| * is ready and enables D+ pull-up. | ||
| */ | ||
| wrapper->PHY.BATTCHRG = USBHS_PHY_BATTCHRG_VDATSRCENB0_Msk; |
There was a problem hiding this comment.
This has to turned off in case DWC2 enables D+ pull-up.
Nordic USBHS wrapper is a set of registers used by both USB BC12 driver and vendor specific part in UDC driver. The wrapper can be considered to be MFD device. Both drivers depends on the VBUS regulator driver and require some synchronisation when accessing wrapper register. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
Use USBHS wrapper driver to register VREGUSB regulator callback and enable USB peripheral. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
Add driver for Nordic USB PD Charging-Type detector. This driver depends on the USBHS wrapper driver. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no> Co-authored-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
Add and enable USBHS charging-type detector node. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
c28e199 to
e6ee67b
Compare
keith-zephyr
left a comment
There was a problem hiding this comment.
BC12 driver looks good to me
Add an example of how to use USB BC12 driver callback among the USB device support. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
e6ee67b to
c9d0a45
Compare
|
|
|
||
| wrapper->ENABLE = 0UL; | ||
|
|
||
| nrf_usbhs_wrapper_disable(parent); |
There was a problem hiding this comment.
Because this effectively cancels out nrf_usbhs_wrapper_enable_udc(), this should be renamed to nrf_usbhs_wrapper_disable_udc().
| { | ||
| const struct usbhs_bc_config *const config = dev->config; | ||
|
|
||
| nrf_usbhs_wrapper_disable(config->parent); |
There was a problem hiding this comment.
Because this effectively cancels out nrf_usbhs_wrapper_enable_phy(), this should be renamed to nrf_usbhs_wrapper_disable_phy() to match.
However, I think the nrf_usbhs_wrapper_enable_phy() should be changed to nrf_usbhs_wrapper_enable_bc() because what we need is BC functionality. Technically we cannot just enable PHY alone without enabling CORE. If nrf_usbhs_wrapper_enable_phy() gets renamed to nrf_usbhs_wrapper_enable_bc() then nrf_usbhs_wrapper_disable_bc() would be the correct name to use here.
| if (role == BC12_DISCONNECTED) { | ||
| data->partner_state.type = BC12_TYPE_NONE; | ||
| data->partner_state.bc12_role = role; | ||
| err = nrf_usbhs_wrapper_vreg_disable(config->parent); | ||
| if (err) { | ||
| LOG_ERR("Failed to disable regulator"); | ||
| } | ||
|
|
||
| return err; | ||
| } |
There was a problem hiding this comment.
The logic will still trigger BC operations if UDC driver called nrf_usbhs_wrapper_vreg_enable().
There are multiple ways to solve the problem. Example would be to check bc12_role before starting BC operations. The assigned role would need synchronization via spin lock because the check would be done inside interrupt handler. If role is set to BC12_DISCONNECTED while BC detection is in progress, then I believe the detection should still finish normally and the change should only refer to VBUS detections that happens after role was set.
| struct nrf_usbhs_wrapper_data *const data = dev->data; | ||
|
|
||
| wrapper_enable_phy_internal(dev, suspended); | ||
| atomic_inc(&data->refcount); |
There was a problem hiding this comment.
BC12 role can be changed independently of UDC state. Input overrides are effectively controlled by both UDC and BC12 driver.
wrapper_enable_phy_internal() is not in any way synchronized by the use of atomic_inc() at all.
| LOG_INF("Enable %s, refcount %lu", | ||
| "USB peripheral", atomic_get(&data->refcount)); |
There was a problem hiding this comment.
This log is not atomic. The return value from atomic_inc() must be used instead of atomic_get() if you want to use the primitive correctly.
I believe just using the data->lock spinlock would be superior here. Using atomic API in non-atomic way is effectively making this rather pointless.
| LOG_INF("BC12 detection finished, refcount %lu", atomic_get(&data->refcount)); | ||
|
|
||
| k_event_post(&data->events, NRF_USBHS_PHY_READY); | ||
| if (atomic_get(&data->refcount) > 1) { |
There was a problem hiding this comment.
Using atomic here sounds like atomic misuse. There really should be a bit indicating that nrf_usbhs_wrapper_enable_udc() was called. The bit should be protected with a spin lock.
| */ | ||
| wrapper->PHY.INPUTOVERRIDE = USBHS_PHY_INPUTOVERRIDE_ID_Msk; | ||
|
|
||
| if (IS_ENABLED(CONFIG_USB_BC12_NRF_USBHS)) { |
There was a problem hiding this comment.
This shouldn't be only based on whether the driver is compiled, but also if BC12 operations were performed.
| #include <nrf_usbhs_wrapper.h> | ||
|
|
||
| #include <zephyr/logging/log.h> | ||
| LOG_MODULE_REGISTER(usbhs, LOG_LEVEL_ERR); |
There was a problem hiding this comment.
It would be nice it if this was not hardcoded.
| const void *bc12_cb_data; | ||
| regulator_callback_t phy_cb; | ||
| const void *phy_cb_data; | ||
| atomic_t refcount; |
There was a problem hiding this comment.
UDC can be enabled and be kept enabled all the time. BC12 needs to perform Battery Charging operations on every VBUS detection (if role is set to Portable Device) and then leave with the state suitable for UDC to continue its operation.



Add drivers for Nordic USBHS wrapper and USB PD Charging-Type detector.
Alternative to #105731