Skip to content
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

cpu/rpx0xx: add periph_usbus support #20817

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

fengelhardt
Copy link
Contributor

Contribution description

This is a WIP contribution to add USB support for the rpx0xx MCU.

Testing procedure

The code is in a quite early stage. It compiles with examples/usbus_minimal, but it hangs somewhere quite early in the initialization. I will debug it as we go, please do not look all to deeply into it yet.

With this PR I primarily want to clarify some question I have regarding the USB stack.

Issues/PRs references

#15822 is for RPx0xx feature tracking.

@github-actions github-actions bot added the Area: cpu Area: CPU/MCU ports label Aug 18, 2024
@dylad dylad self-requested a review August 19, 2024 08:43
@benpicco benpicco requested a review from bergzand August 19, 2024 08:43
@fengelhardt fengelhardt changed the title cpu/rpxx0xx: add periph_usbus support cpu/rpx0xx: add periph_usbus support Aug 20, 2024
Copy link
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance, you're missing NVIC_EnableIRQ(USBCTRL_IRQ_IRQn);.
I used a debugger and check the PLL configuration. It looks fine but I really think some bits are missing (currently 0) like VBUS_DETECTED and SPEED in SIE_STATUS register.

io_reg_atomic_set(&USBCTRL_REGS->MAIN_CTRL,
USBCTRL_REGS_MAIN_CTRL_CONTROLLER_EN_Msk);
io_reg_atomic_set(&USBCTRL_REGS->USB_MUXING,
USBCTRL_REGS_USB_MUXING_TO_PHY_Msk);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RP2040 datasheet also add USBCTRL_REGS_USB_MUXING_SOFTCON_Msk.

  • you should add
io_reg_atomic_set(&USBCTRL_REGS->USB_PWR,
                      USBCTRL_REGS_USB_PWR_VBUS_DETECT_Msk
                    | USBCTRL_REGS_USB_PWR_VBUS_DETECT_OVERRIDE_EN_Msk);

@dylad
Copy link
Member

dylad commented Aug 29, 2024

With these changes, I can now receive the setup request from the host. Its interrupt is fired but never served by _usbdev_esr() and it loops forever so we never response to the setup request.

}
if (USBCTRL_REGS->INTS) {
/* Device specific interrupt */
_disable_irq();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabling all IRQs here will have the side effect to also clear INTS register.
So when USBUS call its ESR callback, it doesn't process any IRQs as INTS is 0.
Depending on the interruption's source, you may need to clear this register either here or in usbdev_esr

@fengelhardt
Copy link
Contributor Author

With these changes, I can now receive the setup request from the host. Its interrupt is fired but never served by _usbdev_esr() and it loops forever so we never response to the setup request.

I already wondered how to handle setup packets. usbdev_event_t has no event defined for this case, so I can not notify usbus. Do I have to handle it here in the periph driver?

@fengelhardt
Copy link
Contributor Author

fengelhardt commented Aug 31, 2024

Added a fixup with your suggestions @dylad, and improved handling of INTS in isr_usbctrl(). Now the line reset seems to work, and the EP 0 is initialized.

I had no time to test further yet. Maybe tomorrow I can also check my packet flow.

@fengelhardt fengelhardt requested a review from aabadie as a code owner September 1, 2024 10:14
@github-actions github-actions bot added Area: USB Area: Universal Serial Bus Area: sys Area: System labels Sep 1, 2024
@fengelhardt
Copy link
Contributor Author

Now the setup requests are recognized by usbus, but control flow is still garbage.

@dylad
Copy link
Member

dylad commented Sep 4, 2024

From what I can tell this is normal.
Setup packets are not put in EP0 buffer but at the beginning of DPSRAM (offset 0).
Currently, you try to pass DPSRAM + 0x100 to USBUS which contains garbage, thus USBUS doesn't recognize the setup packets.
However, if you pass the right offset when you received a setup packet I can clearly see that the host is requesting the device descriptor 🎉
Unfortunately it's seem there is an issue when responding to the request. I'm not even sure the host sees the response yet. I'll try to check with wireshark next debugging session.

Comment on lines 627 to 631
USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_FULL_0_Pos,
buf_filled_bit);
_ep_buf_ctrl_reg_write(ep->num, ep->dir,
USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_AVAILABLE_0_Pos,
USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_AVAILABLE_0_Msk);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that you're also missing to set (at least) the length field in the Buffer Control register of the endpoint which might explain why the host didn't get the response of the first setup request.
If set to 0, hardware will try to transfer 0 bytes to the host which is obviously wrong.

@fengelhardt
Copy link
Contributor Author

Setup packets are not put in EP0 buffer but at the beginning of DPSRAM (offset 0).

Wow, I guess I misread the data sheet here...

Some tinkering today did not work so far, but I have an idea for solving that. Sadly I have not time over the weekend. Might take me some time to figure that out.

@fengelhardt
Copy link
Contributor Author

Still garbage, unfortunately. But I have found a few bugs.

I might simply have a race condition between the setup request handling and data packet handling. Not sure how to handle that with usbus. The USB controller treats setup request differently from normal data, which makes it difficult to hand over to usbus.

Maybe my endpoint initialization is not correct, too.

@dylad
Copy link
Member

dylad commented Sep 12, 2024

I'll see if I can find some times this weekend to help you with that.

Copy link
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a bad time trying to understand the PID stuff on this IP.
For setup request, TinyUSB and the standalone example from raspberry PI tell that we must answer the setup request on PID 1 but there is no such reference in the datasheet.

Comment on lines 636 to 643
_ep_buf_ctrl_reg_write(ep->num, ep->dir,
USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_FULL_0_Msk,
buf_filled_bit);
_ep_buf_ctrl_reg_write(ep->num, ep->dir,
USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_AVAILABLE_0_Msk,
USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_AVAILABLE_0_Msk);
_ep_buf_ctrl_reg_write(ep->num, ep->dir, len,
USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_LENGTH_0_Msk);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to use a variable and write the whole config at once instead.
Moreover, the datasheet states that the AVAILABLE bit should be set AFTER the rest of the configuration in this register. If USB PLL is running at 48 MHz which is our case IIRC. 3 nop instructions are needed before setting the available bit.

@fengelhardt
Copy link
Contributor Author

I have made some progress based on @dylad 's comments. I think I figured out the usb controller more or less. I get the setup requests and some control flow afterwards. However, I totally misunderstood how RIOT's usbus works and there is is still a problem. It seems I do not receive any packets and the control flow looks wrong.

@benpicco benpicco requested a review from OlegHahm October 7, 2024 17:20
@dylad
Copy link
Member

dylad commented Dec 18, 2024

I'm making small progress on my side. I am now stuck halfway through the enumeration process. I'll prepare a more in-depth review once the enumeration process works flawlessly.

@fengelhardt
Copy link
Contributor Author

Thank you very much @dylad for your effort. I recently thought that it could only be an issue of setting the PID correctly. I tried hardcoding the PID and I could then receive at least one correct packet from the device. Maybe I find some time to study the PID handling of the tinyUSB driver to implement that here.

Wish everyone a happy new year btw.

@dylad
Copy link
Member

dylad commented Jan 2, 2025

Hi @fengelhardt Happy new year to you too.
I wasn't able to work on this lately so no progress on my side. I was planning to start the next round of review once I got enumeration fully working but that's not the case yet.
If you want I can do the next round of review now so you can also work on this on your side in the meantime. Basically, ISR management and PID need rework + a few errors here and there. What do you prefer ?

@fengelhardt
Copy link
Contributor Author

Maybe let me first try to get it running. The PID handling I need to rework a lot, I think. I hope to find time in he coming days.

Copy link
Member

@dylad dylad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm starting the next round of review so it can help you:
The current ISR management needs a rework. Some events are called twice during a single handling which confuse USBUS.
As this is not really easy to explain with Github UI, you may look at my working branch to see what I have done on my side.
I didn't put all the changes I've made in my branch into this review as I am still debugging and thus not a 100% sure this is the right move but feel free to give it a try.

On my side, the first request is answered, then the device is assigned a new USB address by the host. Reset is issue to apply the new address and the enumeration process starts over. The first setup request with the new address is sent and answered but after that nothing... so the host issue a reset after 5 seconds or so and we start over ...
If you have question on some changes I've made, don't hesitate to ask here or on Matrix :)

(len & USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_LENGTH_0_Msk)
| buf_filled_bit
| pid_bit
| USBCTRL_DPRAM_EP0_IN_BUFFER_CONTROL_AVAILABLE_0_Msk;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't set the AVAILABLE bit here. RP2040 datasheet states that this bit needs to be set AFTER (using your busy_wait implementation) others bits to avoid sync issue inside the controller.


static rpx0xx_usb_ep_t *_get_ep(uint8_t ep_num, usb_ep_dir_t dir)
{
return &_hw_usb_dev.hw_ep[2 * ep_num + (dir == USB_EP_DIR_OUT ? 0 : 1)];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return &_hw_usb_dev.hw_ep[2 * ep_num + (dir == USB_EP_DIR_OUT ? 0 : 1)];
return &_hw_usb_dev.hw_ep[2 * ep_num + (dir == USB_EP_DIR_IN ? 0 : 1)];

This looks innocent but this changes is really important.

static void _ep_set_stall(usbdev_ep_t *ep, usbopt_enable_t enable)
{
rpx0xx_usb_ep_t *hw_ep = _get_ep(ep->num, ep->dir);
hw_ep->next_pid = 0u;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hw_ep->next_pid = 0u;

I think this line should be remove for a correct PID management otherwise we end up always using the same PID.

Comment on lines +685 to +689
if (hw_ep->data_buf == NULL) {
DEBUG(" tr completed\n");
_hw_usb_dev.dev.epcb(&hw_ep->ep, USBDEV_EVENT_TR_COMPLETE);
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for this. USBDEV_EVENT_TR_COMPLETE should be handle by ESR or EP_ESR directly.

DEBUG("[rpx0xx usb] Call _usbdev_ep_xmit(ep.num=%d, ep.dir=%s, buf=%p, len=%u)\n",
ep->num, PRINT_DIR(ep->dir), buf, len);

assert(buf != NULL);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may also drop this one.
USBUS can call this function with a NULL buffer / 0 length in order to send an ACK to Host.

@dylad
Copy link
Member

dylad commented Jan 8, 2025

@fengelhardt Finally I've got some good news !

Copie d'écran_20250108_212408

You may find all the changes I've made on my work branch.

@fengelhardt
Copy link
Contributor Author

fengelhardt commented Jan 8, 2025 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: cpu Area: CPU/MCU ports Area: sys Area: System Area: USB Area: Universal Serial Bus
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants