Contribute
Register

USB-C Hotplug not working

Status
Not open for further replies.
The annoying thing is that different vendors will implement it differently even within their own products. My DZ77RE-75K implements Thunderbolt 1 more like Apple does TB3 now (controller's just always on), this Dell implements TB3 like Apple did TB2 on the late 2013s (powers up/down as needed), and vendors like Asus have those PCI-e add-in boards. There probably is no panacea to get TB working, but I'd guess there are probably a limited number of ways that the various manufacturers could implement it and still have it work.

The Linux kernel has drivers for most of these at this point, which may provide a helpful reference. I've always gotten stuck on the enumeration part, which is where I am now with this and, not to be a downer, where I had to basically abandon my attempts to ACPI patch Thunderbolt hotplug into Windows on the Late 2013 rMBP. At the very least, I do know it is absolutely possible to get working, otherwise board vendors wouldn't have it working on their own machines. :p It's a cookie I'd quite like to crack.
 
Last edited:
The annoying thing is that different vendors will implement it differently even within their own products. My DZ77RE-75K implements Thunderbolt 1 more like Apple does TB3 now (controller's just always on), this Dell implements TB3 like Apple did TB2 on the late 2013s (powers up/down as needed), and vendors like Asus have those PCI-e add-in boards. There probably is no panacea to get TB working, but I'd guess there are probably a limited number of ways that the various manufacturers could implement it and still have it work.

The Linux kernel has drivers for most of these at this point, which may provide a helpful reference. I've always gotten stuck on the enumeration part, which is where I am now with this and, not to be a downer, where I had to basically abandon my attempts to ACPI patch Thunderbolt hotplug into Windows on the Late 2013 rMBP. At the very least, I do know it is absolutely possible to get working, otherwise board vendors wouldn't have it working on their own machines. :p It's a cookie I'd quite like to crack.

I'll try some of the other things you have in SSDT-TYPC just to see the effects.
I know there are some name dependencies (NHI0, for example), as some names from Apple's ACPI appear in the kext binaries...
 
Oh! Didn't realize they were just in the binaries. Which binaries are the ones worth looking at?

Additionally, it seems like the 13" touchbar MBP has a 4-lane controller working as a 2-lane device like my laptop. I'm going to try and determine which of Apple's 3 Thunderbolt SSDTs apply to that and use its structure instead of the one I used. Might be more fruitful.
 
Oh! Didn't realize they were just in the binaries. Which binaries are the ones worth looking at?

I was looking at the Thunderbolt kext.
You can press Command+K in ioreg to go to a kext associated with any node.
And you can use grep -R to do recursive searches...
 
Hey, so something awesome just happened: Thunderbolt hotplug mysteriously worked (on insert!) I don't know why it did, but it did. Normally I just get what's in the attached image, but the attached ioreg shows a successful insertion. Removal crashed my system (as it does normally), and I noticed the "expresscard" menu item stated something different (also attached).

Unfortunately, I'm now (and will be for a a few days) in a location where the Internet connection stinks so I can't upload a full set of "problem reporting files," but here's proof that it can work somehow. On reboot, I get the normal undesired behavior again.

Worth noting: I was using the SSDT-TYPC that has my attempt at adding in a device tree. No changes since the last upload (my laptop had just woken up from a 2-day long sleep, however). Trying again after a sleep yields the "normal" situation. :/
 

Attachments

  • Archive.zip
    2.4 MB · Views: 200
Last edited:
Hey, so something awesome just happened: Thunderbolt hotplug mysteriously worked (on insert!) I don't know why it did, but it did. Normally I just get what's in the attached image, but the attached ioreg shows a successful insertion. Removal crashed my system (as it does normally), and I noticed the "expresscard" menu item stated something different (also attached).

Unfortunately, I'm now (and will be for a a few days) in a location where the Internet connection stinks so I can't upload a full set of "problem reporting files," but here's proof that it can work somehow. On reboot, I get the normal undesired behavior again.

Worth noting: I was using the SSDT-TYPC that has my attempt at adding in a device tree. No changes since the last upload (my laptop had just woken up from a 2-day long sleep, however). Trying again after a sleep yields the "normal" situation. :/

The crash might be caused by the TB3 controller turning off (disconnecting from the PCI bus) when nothing is plugged in.
Seems to me the key to start with is finding the way to disable PCI hotplug for the TB3 controller...
 
I'm not so concerned about that part just yet; I can't get it to respond on hotplug correctly again. I've been looking through Apple's implementation and I've found that Apple's Thunderbolt related _GPE (_L55) calls stuff like "GGII" and "SGII" with what looks like some sort of delay, before calling "AMPE" (AMPE simply Notifies NHI0 with 0, which is a Bus check). Note: CMPE is what Apple makes non-Darwin OSes call instead, which notifies PEG1 (RP15 in our case) with a bus check instead of specifically NHI0.

Here's the GPE:
Code:
 Method (_L55, 0, NotSerialized)  // _Lxx: Level-Triggered GPE
                {
                    If (!OSDW ())
                    {
                        If ((\_SB.PCI0.PEG1.POC0 == One))
                        {
                            Return (Zero)
                        }

                        Sleep (0x0190)
                        If ((\_SB.PCI0.PEG1.WTLT () == One))
                        {
                            \_SB.PCI0.PEG1.ICMS ()
                        }
                        Else
                        {
                            \_SB.SGOV (0x01070004, Zero, \_SB.SGDO (0x01070004), If (\_SB.PCI0.PEG1.UPMB)
                                {
                                    \_SB.PCI0.PEG1.UPMB = Zero
                                    Sleep (One)
                                })
                        }

                        Else
                        {
                        }

                        \_SB.PCI0.PEG1.CMPE ()
                    }
                    Else
                    {
                        If ((\_SB.GGII == 0x01070015))
                        {
                            One
                            \_SB.SGII (0x01070015, Zero)
                        }
                        Else
                        {
                            \_SB.SGII (0x01070015, One)
                        }

                        \_SB.PCI0.PEG1.UPSB.AMPE ()
                    }
                }

AMPE is on UPSB
Code:
 Method (AMPE, 0, Serialized)
                {
                    Notify (\_SB.PCI0.PEG1.UPSB.DSB0.NHI0, Zero)
                }

Could that be it?

Alternatively, I've found in AppleThunderboltNHI that the only TB3 TypeC ACPI methods that actually exist are:

SXFP
MUST
XRST
RTPC
TRPE

These are all on NHI0 in ACPI.

The rest in the kext (XRIN, XRIL, others) I know are for TB1 & TB2 ports. SXFP I can't tell what it does (no idea what SGOV and SGDO are), but it used to be a force-power method:
Code:
 Method (SXFP, 1, Serialized) //Don't think this actually does anything
                      {
                          If ((Arg0 == Zero))
                          {
                              If ((GGDV == 0x01070007))
                              {
                                  One
                                  SGOV (0x01070007, Zero, SGDO (0x01070007), Sleep (0x64))
                              }

                              SGOV (0x01070004, Zero, SGDO (0x01070004))
                          }
                      }

MUST I don't know if we need to worry about (notifies XHC that it's a USB device instead of a TB device).
XRST looks like it does nothing. Nothing seems to call it from ACPI.
RTPC might be related to removing thunderbolt, though all it does is set the variable RTBT. Nothing seems to call it from ACPI.

TRPE is MASSIVE:
Code:
 Method (TRPE, 2, Serialized)
                        {
                            If (OSDW ())
                            {
                                If ((Arg0 <= One))
                                {
                                    If ((Arg0 == Zero))
                                    {
                                        \_SB.PCI0.PEG1.PUPD (Zero, 0x02)
                                        \_SB.PCI0.PEG1.PSTA = 0x03
                                        SGOV (0x01070004, Zero, SGDO (0x01070004), Else
                                            {
                                                Local0 = Zero
                                                If (((GGOV == 0x01070004) && Zero))
                                                {
                                                    (GGDV == 0x01070004)
                                                    Zero
                                                    \_SB.PCI0.PEG1.PSTA = Zero
                                                    While (One)
                                                    {
                                                        SGDI (0x01070004)
                                                        Local1 = Zero
                                                        Sleep (0x1E)
                                                        \_SB.PCI0.PEG1.PUPD (One, 0x02)
                                                        Local2 = (Timer + 0x00989680)
                                                        While ((Timer <= Local2))
                                                        {
                                                            If ((\_SB.PCI0.PEG1.LACR == Zero))
                                                            {
                                                                If ((\_SB.PCI0.PEG1.LTRN != One))
                                                                {
                                                                    Break
                                                                }
                                                            }
                                                            ElseIf (((\_SB.PCI0.PEG1.LTRN != One) && (\_SB.PCI0.PEG1.LACT == One)))
                                                            {
                                                                Break
                                                            }

                                                            Sleep (0x0A)
                                                        }

                                                        Sleep (Arg1)
                                                        While ((Timer <= Local2))
                                                        {
                                                            If ((\_SB.PCI0.PEG1.UPSB.AVND != 0xFFFFFFFF))
                                                            {
                                                                Local1 = One
                                                                Break
                                                            }

                                                            Sleep (0x0A)
                                                        }

                                                        If ((Local1 == One))
                                                        {
                                                            MABT = One
                                                            Break
                                                        }

                                                        If ((Local0 == 0x04))
                                                        {
                                                            Return (Zero)
                                                        }

                                                        Local0++
                                                        SGOV (0x01070004, Zero, SGDO (0x01070004), Sleep (0x03E8))
                                                    }

                                                    If ((\_SB.PCI0.PEG1.CSPD != 0x03))
                                                    {
                                                        If ((\_SB.PCI0.PEG1.SSPD == 0x03))
                                                        {
                                                            If ((\_SB.PCI0.PEG1.UPSB.SSPD == 0x03))
                                                            {
                                                                If ((\_SB.PCI0.PEG1.TSPD != 0x03))
                                                                {
                                                                    \_SB.PCI0.PEG1.TSPD = 0x03
                                                                }

                                                                If ((\_SB.PCI0.PEG1.UPSB.TSPD != 0x03))
                                                                {
                                                                    \_SB.PCI0.PEG1.UPSB.TSPD = 0x03
                                                                }

                                                                \_SB.PCI0.PEG1.LRTN = One
                                                                Local2 = (Timer + 0x00989680)
                                                                While ((Timer <= Local2))
                                                                {
                                                                    If ((\_SB.PCI0.PEG1.LACR == Zero))
                                                                    {
                                                                        If (((\_SB.PCI0.PEG1.LTRN != One) && (\_SB.PCI0.PEG1.UPSB.AVND != 0xFFFFFFFF)))
                                                                        {
                                                                            Local1 = One
                                                                            Break
                                                                        }
                                                                    }
                                                                    ElseIf ((((\_SB.PCI0.PEG1.LTRN != One) && (\_SB.PCI0.PEG1.LACT == One)) && (\_SB.PCI0.PEG1.UPSB.AVND != 0xFFFFFFFF)))
                                                                    {
                                                                        Local1 = One
                                                                        Break
                                                                    }

                                                                    Sleep (0x0A)
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            })
                                    }
                                }
                            }

                            Return (Zero)
                        }
But nothing calls it from ACPI. Would be nice if all we needed was just AMPE...

EDIT: I think the flow of control for Apple's drivers is something like this:
Plug in device --> GPE _L55 notifies NHI through AMPE --> NHI wakes up, driver checks bus. If not a TB device, driver calls MUST from ACPI (can drivers even do that?) --> USB takes over

If it IS a TB device, driver calls... TRPE, SXFP, XRST (RTPC is likely for removal). But I don't know which one yet or in what order.

I'm not sure how Dell is differentiating between a USB device and a TB device on the same port. Maybe it expects the OS to do that?
 
Last edited:
Just an update: I hijacked my ACPI brightness buttons such that they would [also] run bus checks to the Thunderbolt controller (brightness up notified RP15 like CMPE and brightness down notified NHI0 like AMPE), and I tried simulating the checks after delays to no avail.

I'm attaching the DSDT if anyone wants to look through it and has any specific suggestions.

Renaming the XTBT recursive call to YTBT and making it return 0, plus renaming all _RMV to XRMV, fixed the "realize something has been inserted" part with the attached SSDT-TYPC (in which USB-C hot plug works perfectly). Again, the main problem is with Thunderbolt device enumeration (and system crash-on-removal because the Thunderbolt controller powers off on unplug). Note: The _DSMs in the SSDT are not properly implemented, so they don't actually do anything.
 

Attachments

  • DSDT.dsl
    1.1 MB · Views: 263
  • SSDT-TYPC_preSTA_SUN.aml
    1.4 KB · Views: 297
Last edited:
IT JUST WORKED AGAIN!

Screw my internet, here's a full set of problem reporting files.

Process that got it to happen:
Reboot, login to desktop -> Put to sleep overnight -> Open lid -> Login -> wait 20 minutes -> Plug in Thunderbolt device. (all on AC power)

Note: System profiler hangs/doesn't load.
Curiously, there's an IOKernelDebugger present (maybe that's because I have all debug stuff turned on at the moment)...
 

Attachments

  • ProblemReportingFiles.zip
    9.6 MB · Views: 179
It was an exciting thread to follow - were there any developments since? In general, can someone shed some light on current state of TB3 / USB-C hotplug support? My biggest issue is that I can't hotplug USB devices into an USB-C hub (xps 9560) - they only work if they've been plugged-in while booting. Curiously, though, the devices even get the power sometimes (mouse LEDs are on), but can't move the cursor / use the keyboard...

On the other hand, HDMI port on the same hub works nicely and hot-plugs without problems!
 
Last edited:
Status
Not open for further replies.
Back
Top