analogdevicesinc / msdk

Software Development Kit for Analog Device's MAX-series microcontrollers
Apache License 2.0
60 stars 75 forks source link

USB_CompositeDevice_MSC_CDC example on MAX32666FTHR will not enumerate after USB disconnect/reconnect #722

Open khpeterson opened 10 months ago

khpeterson commented 10 months ago

I know this app was written for the EvKit and not the FTHR but I am trying to integrate this functionality into a FTHR based application. Running the app on the EvKit works fine - it will enumerate each time USB on CN2 is connected to a host:

2023-08-28T11:31:37.600562: ***** MAX32665 USB Composite Device (CDCACM and Mass Storage) Example *****
2023-08-28T11:31:37.600775: Waiting for VBUS...
2023-08-28T11:34:58.818959: VBUS Connect
2023-08-28T11:34:58.819201: Suspended
2023-08-28T11:34:58.914743: Bus Reset
2023-08-28T11:34:58.915511: Bus Reset Done: High speed
2023-08-28T11:34:58.996455: Enumeration complete...
2023-08-28T11:35:07.898868: Suspended
2023-08-28T11:35:13.462384: Bus Reset
2023-08-28T11:35:13.463063: Bus Reset Done: High speed
2023-08-28T11:35:13.526044: Enumeration complete...

However, because VBUS on CN1 is providing power to the EvKit, the VBUS Disconnect event is never generated.

On a MAX32666FTHR powered with a LiPo battery, disconnecting produces VBUS Disconnect as expected but the Suspend, Bus Reset (and Done) events do not follow VBUS Connect as they do after power on:

2023-08-28T15:37:12.144284: ***** MAX32665 USB Composite Device (CDCACM and Mass Storage) Example *****
2023-08-28T15:37:12.146163: Waiting for VBUS...
2023-08-28T15:37:12.151887: VBUS Connect
2023-08-28T15:37:12.152778: Suspended
2023-08-28T15:37:12.252169: Bus Reset
2023-08-28T15:37:12.254097: Bus Reset Done: High speed
2023-08-28T15:37:12.327265: Enumeration complete...
2023-08-28T15:37:21.600820: VBUS Disconnect
2023-08-28T15:37:34.201727: VBUS Connect

I instrumented the usb.c driver to save the first 8 sets of interrupt flags handled by MXC_USB_IrqHandler():

static struct {
    uint32_t MXC_USBHS_intrusb;
    uint32_t MXC_USBHS_intrusben;
    uint32_t MXC_USB_mxm_flags;
} mxc_usb_irqhist[8];
static int mxc_usb_irqhist_idx;

...

    /* Note: Hardware clears these after read, so we must process them all or they are lost */
    MXC_USBHS_intrusb = MXC_USBHS->intrusb;
    MXC_USBHS_intrusben = MXC_USBHS->intrusben;
    MXC_USB_flags = MXC_USBHS_intrusb & MXC_USBHS_intrusben;
    in_flags = MXC_USBHS->intrin & MXC_USBHS->intrinen;
    out_flags = MXC_USBHS->introut & MXC_USBHS->introuten;

    /* These USB interrupt flags are W1C. */
    MXC_USB_mxm_flags = MXC_USBHS->mxm_int & MXC_USBHS->mxm_int_en;
    MXC_USBHS->mxm_int = MXC_USB_mxm_flags;

    if (mxc_usb_irqhist_idx < 8) {
    mxc_usb_irqhist[mxc_usb_irqhist_idx].MXC_USBHS_intrusb = MXC_USBHS_intrusb;
    mxc_usb_irqhist[mxc_usb_irqhist_idx].MXC_USBHS_intrusben = MXC_USBHS_intrusben;
    mxc_usb_irqhist[mxc_usb_irqhist_idx].MXC_USB_mxm_flags = MXC_USB_mxm_flags; 
    mxc_usb_irqhist_idx += 1;
    }

After the normal startup sequence that history looks like:

(gdb) p mxc_usb_irqhist[0]@mxc_usb_irqhist_idx
$9 = {{
    MXC_USBHS_intrusb = 0, 
    MXC_USBHS_intrusben = 0, 
    MXC_USB_mxm_flags = 1
  }, {
    MXC_USBHS_intrusb = 9, 
    MXC_USBHS_intrusben = 5, 
    MXC_USB_mxm_flags = 0
  }, {
    MXC_USBHS_intrusb = 4, 
    MXC_USBHS_intrusben = 5, 
    MXC_USB_mxm_flags = 0
  }, {
    MXC_USBHS_intrusb = 8, 
    MXC_USBHS_intrusben = 5, 
    MXC_USB_mxm_flags = 0
  }, {
    MXC_USBHS_intrusb = 8, 
    MXC_USBHS_intrusben = 5, 
    MXC_USB_mxm_flags = 0
  }, {
    MXC_USBHS_intrusb = 8, 
    MXC_USBHS_intrusben = 5, 
    MXC_USB_mxm_flags = 0
  }, {
    MXC_USBHS_intrusb = 8, 
    MXC_USBHS_intrusben = 5, 
    MXC_USB_mxm_flags = 0
  }, {
    MXC_USBHS_intrusb = 8, 
    MXC_USBHS_intrusben = 5, 
    MXC_USB_mxm_flags = 0
  }}

Resetting the history idx after the VBUS Disconnect and tracing the re-connection shows:

(gdb) p mxc_usb_irqhist[0]@mxc_usb_irqhist_idx
$11 = {{
    MXC_USBHS_intrusb = 8, 
    MXC_USBHS_intrusben = 0, 
    MXC_USB_mxm_flags = 1
  }}

I have tried forcing a shutdown/softreset/re-init when handling the VBUS Disconnect event:

        MXC_USB_Shutdown();
        MXC_USBHS->softreset = 3;
        if (MXC_USB_Init(&usb_opts) != 0) {
            printf("usb_init() failed\n");
        }

to mimic POR conditions but no go - the RESET interrupt never triggers after the second VBUS Connect. I have not confirmed that the host is initiating the second RESET but FWIW, I do see the same behavior on macOS 12.6.8 and win10.

I will keep digging but any suggestions from a MAXUSB expert would be greatly appreciated. It would be helpful if this example app showed the necessary steps to disconnect and reconnect USB when powered by something other than USB.

Jake-Carter commented 9 months ago

Hi @khpeterson, our MAXUSB expert recently transitioned to a different team. I've reached out to see if they are still available for some guidance.

In the meantime... I have limited experience with USB but conceptually I think you've illustrated the issue very well. So apologies if my follow-up questions are off-base...

Starting with the basics... I wonder if differential pair is being grounded after the first disconnect? I think the idea that the host is never seeing the re-connect is worth investigating. AFAIK the host detects the presence and speed class of the device based on a pull-up on DM or DP, but I don't see a pull-up on either the FTHR or EVKIT. So perhaps the POR state manages the signal levels correctly, but something about the disconnect logic is grounding them and preventing the reconnect from being detected. Can you probe the DM/DP pins after the second reconnect?

image

This is a really hacky potential workaround, but you could try issuing a system reset on the VBUS disconnect event. The micro's state should be almost identical to a POR. It's definitely ugly, but might make an interesting data point at least...

image

image

khpeterson commented 9 months ago

Thank you @Jake-Carter, I don't have a way to probe DM/DP until next week (and will do then) but FYI, your workaround suggestion to issue a full system reset on VBUS disconnect does do the right thing and enumeration completes successfully.

sanjayjaroli commented 9 months ago

Hi @khpeterson, Is it possible for you to monitor VDDB when USB is disconnected?

khpeterson commented 9 months ago

@Jake-Carter - I probed DP and DM (on U2 pins 4 and 3) on the first and second VBUS connects... I see the pull-up action on DP (DM stays low) on the first VBUS connect but nothing triggers on the second VBUS connect. Here's what I see on DP the first connect:

IMG_6825

@sanjayjaroli - I probed VDDB on one side of C27 and here's what that looks like when VBUS is disconnected - I hope the timescale is long enough to be useful to you.

IMG_6826

Jake-Carter commented 9 months ago

Thanks @khpeterson, we're looking at the VDDB behavior on the FTHR a little deeper. It's a little suspicious that it doesn't drop to 0 on disconnect.