Contribute
Register

Fixing HPET IRQ conflicts with a SSDT

Status
Not open for further replies.

shilohh

Moderator
Joined
Jan 1, 2012
Messages
1,752
Motherboard
Asus Prime Z490-A
CPU
i9-10850K
Graphics
RX 5700 XT
Mac
  1. MacBook Pro
  2. Mac Pro
Mobile Phone
  1. Android
The following is based on my Asus X79 Rampage IV Extreme and Rampage IV Black Edition.

Background

Both boards exhibit the issue that Device USBE (2nd USB 2.0 controller) wont load. The cause of this issue is that Device HPET (High Precision Event Timer) grabs Interrupt 21 (15 Hex) preventing USBE from using it. My Asus HPET devices use 20, 21, 11 and 12 (Hex 14, 15, 0B and 0C) and the device does not specify which IRQs to use in the DSDT.

Code:
            Device (HPET)
            {
                Name (_HID, EisaId ("PNP0103"))
                Name (CRS, ResourceTemplate ()
                {
                    Memory32Fixed (ReadWrite,
                        0xFED00000,         // Address Base
                        0x00000400,         // Address Length
                        _Y24)
                })
                OperationRegion (HCNT, SystemMemory, HPTC, 0x04)
                Field (HCNT, DWordAcc, NoLock, Preserve)
                {
                    HPTS,   2, 
                        ,   5, 
                    HPTE,   1
                }

                Method (_STA, 0, NotSerialized)
                {
                    If (HPTE)
                    {
                        Return (0x0F)
                    }
                    Else
                    {
                        Return (Zero)
                    }
                }

                Method (_CRS, 0, NotSerialized)
                {
                    CreateDWordField (CRS, \_SB.PCI0.HPET._Y24._BAS, HTBS)
                    Multiply (HPTS, 0x1000, Local0)
                    Add (Local0, 0xFED00000, HTBS)
                    Return (CRS)
                }
            }

The real Mac HPET uses 2, 8, 11 and 12 but only specifies 0 and 8 in the DSDT.
Code:
                Device (HPET)
                {
                    Name (_HID, EisaId ("PNP0103"))
                    Name (_CID, EisaId ("PNP0C01"))
                    Name (BUF0, ResourceTemplate ()
                    {
[COLOR=#008000]                        IRQNoFlags ()
                            {0}
                        IRQNoFlags ()
                            {8}[/COLOR]
                        Memory32Fixed (ReadWrite,
                            0xFED00000,         // Address Base
                            0x00000400,         // Address Length
                            )
                    })
                    Method (_STA, 0, NotSerialized)
                    {
                        If (LGreaterEqual (OSYS, 0x07D1))
                        {
                            If (HPAE)
                            {
                                Return (0x0F)
                            }
                        }
                        Else
                        {
                            If (HPAE)
                            {
                                Return (0x0B)
                            }
                        }

                        Return (0x00)
                    }

                    Method (_CRS, 0, Serialized)
                    {
                        If (HPAE)
                        {
                            CreateDWordField (BUF0, 0x0A, HPT0)
                            If (LEqual (HPAS, 0x01))
                            {
                                Store (0xFED01000, HPT0)
                            }

                            If (LEqual (HPAS, 0x02))
                            {
                                Store (0xFED02000, HPT0)
                            }

                            If (LEqual (HPAS, 0x03))
                            {
                                Store (0xFED03000, HPT0)
                            }
                        }

                        Return (BUF0)
                    }
                }

My goal was to get my Asus HPET to use the same IRQs as the real Mac. When I give my Asus HPET the same IRQs as as the Mac HPET (0 and 8), it still doesn't use 2 like the Mac does.
Code:
            Device (HPET)
            {
                Name (_HID, EisaId ("PNP0103"))
                Name (CRS, ResourceTemplate ()
                {
[COLOR=#008000]                    IRQNoFlags ()
                        {0}
                    IRQNoFlags ()
                        {8}[/COLOR]
                    Memory32Fixed (ReadWrite,
                        0xFED00000,         // Address Base
                        0x00000400,         // Address Length
                        _Y24)
                })
                OperationRegion (HCNT, SystemMemory, HPTC, 0x04)
                Field (HCNT, DWordAcc, NoLock, Preserve)
                {
                    HPTS,   2, 
                        ,   5, 
                    HPTE,   1
                }

                Method (_STA, 0, NotSerialized)
                {
                    If (HPTE)
                    {
                        Return (0x0F)
                    }
                    Else
                    {
                        Return (Zero)
                    }
                }

                Method (_CRS, 0, NotSerialized)
                {
                    CreateDWordField (CRS, \_SB.PCI0.HPET._Y24._BAS, HTBS)
                    Multiply (HPTS, 0x1000, Local0)
                    Add (Local0, 0xFED00000, HTBS)
                    Return (CRS)
                }
            }

I also had to remove 0 from Device TMR:
Code:
                Device (TMR)
                {
                    Name (_HID, EisaId ("PNP0100"))
                    Name (_CRS, ResourceTemplate ()
                    {
                        IO (Decode16,
                            0x0040,             // Range Minimum
                            0x0040,             // Range Maximum
                            0x00,               // Alignment
                            0x04,               // Length
                            )
[COLOR=#ff0000]                        IRQNoFlags ()
                            {0}[/COLOR]
                    })
                }

And 8 from RTC0:

Code:
                Device (RTC0)
                {
                    Name (_HID, EisaId ("PNP0B00"))
                    Name (_CRS, ResourceTemplate ()
                    {
                        IO (Decode16,
                            0x0070,             // Range Minimum
                            0x0070,             // Range Maximum
                            0x00,               // Alignment
                            0x02,               // Length
                            )
[COLOR=#ff0000]                        IRQNoFlags ()
                            {8}[/COLOR]
                    })
                }

And then I removed 2 from Device PIC, and HPET then uses 2.
Code:
                Device (PIC)
                {
                    Name (_HID, EisaId ("PNP0000"))
                    Name (_CRS, ResourceTemplate ()
                    {
                        IO (Decode16,
                            0x0020,             // Range Minimum
                            0x0020,             // Range Maximum
                            0x00,               // Alignment
                            0x02,               // Length
                            )
                        IO (Decode16,
                            0x00A0,             // Range Minimum
                            0x00A0,             // Range Maximum
                            0x00,               // Alignment
                            0x02,               // Length
                            )
[COLOR=#ff0000]                        IRQNoFlags ()
                            {2}[/COLOR]
                    })
                }

To make sure that HPET wouldn't grab anything else random, I gave it 11 and 12:
Code:
                Name (CRS, ResourceTemplate ()
                {
                    IRQNoFlags ()
                        {0}
                    IRQNoFlags ()
                        {8}
[COLOR=#008000]                    IRQNoFlags ()
                        {11}
                    IRQNoFlags ()
                        {12}[/COLOR]
                    Memory32Fixed (ReadWrite,
                        0xFED00000,         // Address Base
                        0x00000400,         // Address Length
                        _Y24)
                })
Now both USB 2.0 devices load and I haven't had an issue since.

A while ago, based on RivoGirl's and PikeRAlpha's tiny SSDT examples, I started using SSDTs to inject, disable and modify devices in the OEM Asus ACPI tables (DSDT and SSDTs). I considered using SSDTs to replace all the patching I had done in the DSDT but the HPET device held me back. I asked some who understand the programming better than me if I could give HPET IRQs via SSDT and they said no. Based on my knowledge, you cant disable/hide a device and add it back without renaming it and HPET wont work with a different name. I asked myself why use any SSDTs because, if I have to patch anything in the DSDT, why not just do everything in the DSDT? However, I decided to keep using SSDTs for PCI cards and some devices that you would want to work from the very first boot (Wifi, LAN and more). I always thought it would be nice to use SSDTs for all ACPI patching as updating the BIOS and changing, moving or removing PCI cards requires the DSDT to re-extracted and patched again. It finally dawned on me to disable the DSDT's HPET and inject the patched one at a new location from a SSDT. With a new device path, I wouldn't have to rename it to inject it as a new device.

The SSDT

I disabled the HPET with:

Code:
    External (\_SB_.PCI0.HPET._STA, MethodObj)    // 0 Arguments

    Scope (_SB)
    {
        Method (_INI, 0, NotSerialized)
        {
            Store (Zero, \_SB.PCI0.HPET._STA)
        }
    }

I added it back in a new chid device of PCI0 (PCI1) with this:

Code:
    Device (\_SB.PCI0.[COLOR=#008000]PCI1[/COLOR])
    {
        Name (_ADR, One)
        Device (HPET)
        {
            Name (_HID, EisaId ("PNP0103"))
            Name (CRS, ResourceTemplate ()
            {
                IRQNoFlags ()
                    {0}
                IRQNoFlags ()
                    {8}
                IRQNoFlags ()
                    {11}
                IRQNoFlags ()
                    {12}
                Memory32Fixed (ReadWrite,
                    0xFED00000,         // Address Base
                    0x00000400,         // Address Length
                    _Y00)
            })
            Name (HPTC, 0xFED1F404)
            OperationRegion (HCNT, SystemMemory, HPTC, 0x04)
            Field (HCNT, DWordAcc, NoLock, Preserve)
            {
                HPTS,   2, 
                    ,   5, 
                HPTE,   1
            }

            Method (_STA, 0, NotSerialized)
            {
                If (HPTE)
                {
                    Return (0x0F)
                }
                Else
                {
                    Return (Zero)
                }
            }

            Method (_CRS, 0, NotSerialized)
            {
                CreateDWordField (CRS, \_SB.PCI0.PCI1.HPET._Y00._BAS, HTBS)
                Multiply (HPTS, 0x1000, Local0)
                Add (Local0, 0xFED00000, HTBS)
                Return (CRS)
            }
        }
    }
Note that I had to add "Name (HPTC, 0xFED1F404)" from the DSDT because an external reference wouldn't solve a compilation error.

I had to do the same with TMR, RTC0 and PIC. I disabled them with:

Code:
    Name (_SB.PCI0.SBRG.TMR._STA, Zero)
    Name (_SB.PCI0.SBRG.RTC0._STA, Zero)
    Name (_SB.PCI0.SBRG.PIC._STA, Zero)

I added the patched devices back in their same device path with new names matching the real Mac DSDT device names.
Code:
    Device (_SB.PCI0.SBRG.TIMR)
    {
        Name (_HID, EisaId ("PNP0100"))
        Name (_CRS, ResourceTemplate ()
        {
            IO (Decode16,
                0x0040,             // Range Minimum
                0x0040,             // Range Maximum
                0x00,               // Alignment
                0x04,               // Length
                )
        })
    }

    Device (_SB.PCI0.SBRG.RTC)
    {
        Name (_HID, EisaId ("PNP0B00"))
        Name (_CRS, ResourceTemplate ()
        {
            IO (Decode16,
                0x0070,             // Range Minimum
                0x0070,             // Range Maximum
                0x01,               // Alignment
                0x08,               // Length
                )
        })
    }

    Device (_SB.PCI0.SBRG.IPIC)
    {
        Name (_HID, EisaId ("PNP0000"))
        Name (_CRS, ResourceTemplate ()
        {
            IO (Decode16,
                0x0020,             // Range Minimum
                0x0020,             // Range Maximum
                0x00,               // Alignment
                0x02,               // Length
                )
            IO (Decode16,
                0x00A0,             // Range Minimum
                0x00A0,             // Range Maximum
                0x00,               // Alignment
                0x02,               // Length
                )
        })
    }

Here is a full example of a SSDT that fixes my IRQ conflict. However, I use the code as part of a much bigger SSDT.
Code:
/*
 * Intel ACPI Component Architecture
 * AML Disassembler version 20100331
 *
 * Disassembly of iASLFavXp7.aml, Sun Apr 17 00:57:29 2016
 *
 *
 * Original Table Header:
 *     Signature        "SSDT"
 *     Length           0x0000020B (523)
 *     Revision         0x01
 *     Checksum         0x31
 *     OEM ID           "shiloh"
 *     OEM Table ID     "HPET_IRQ"
 *     OEM Revision     0x00001000 (4096)
 *     Compiler ID      "INTL"
 *     Compiler Version 0x20100331 (537920305)
 */
DefinitionBlock ("iASLFavXp7.aml", "SSDT", 1, "shiloh", "HPET_IRQ", 0x00001000)
{
    External (\_SB_.PCI0.HPET._STA)

    Scope (_SB)
    {
        Method (_INI, 0, NotSerialized)
        {
            Store (Zero, \_SB.PCI0.HPET._STA)
        }
    }

    Device (\_SB.PCI0.PCI1)
    {
        Name (_ADR, One)
        Device (HPET)
        {
            Name (_HID, EisaId ("PNP0103"))
            Name (CRS, ResourceTemplate ()
            {
                IRQNoFlags ()
                    {0}
                IRQNoFlags ()
                    {8}
                IRQNoFlags ()
                    {11}
                IRQNoFlags ()
                    {12}
                Memory32Fixed (ReadWrite,
                    0xFED00000,         // Address Base
                    0x00000400,         // Address Length
                    _Y00)
            })
            Name (HPTC, 0xFED1F404)
            OperationRegion (HCNT, SystemMemory, HPTC, 0x04)
            Field (HCNT, DWordAcc, NoLock, Preserve)
            {
                HPTS,   2, 
                    ,   5, 
                HPTE,   1
            }

            Method (_STA, 0, NotSerialized)
            {
                If (HPTE)
                {
                    Return (0x0F)
                }
                Else
                {
                    Return (Zero)
                }
            }

            Method (_CRS, 0, NotSerialized)
            {
                CreateDWordField (CRS, \_SB.PCI0.PCI1.HPET._Y00._BAS, HTBS)
                Multiply (HPTS, 0x1000, Local0)
                Add (Local0, 0xFED00000, HTBS)
                Return (CRS)
            }
        }
    }

    Name (_SB.PCI0.SBRG.TMR._STA, Zero)
    Device (_SB.PCI0.SBRG.TIMR)
    {
        Name (_HID, EisaId ("PNP0100"))
        Name (_CRS, ResourceTemplate ()
        {
            IO (Decode16,
                0x0040,             // Range Minimum
                0x0040,             // Range Maximum
                0x00,               // Alignment
                0x04,               // Length
                )
        })
    }
}
 
In your case the _STA is defined as a name, so you can use the "set to zero in _INI" trick.

It is even easier in the case that the original code didn't define _STA.

It works because according to ACPI spec, _STA, if not provided defaults to 0xF (device present).

So, the following definitions are equal in ACPI...

With _STA:
Code:
Device(FOO)
{
    Name(_STA, 0xF)
}

or:
Code:
Device(FOO)
{
    Method(_STA) { Return(0x0F) }
}

or, without _STA:
Code:
Device(FOO)
{
}

So, by injecting an _STA=0 for a device that has no _STA (via SSDT), you disable the original ACPI device definition. Injecting new objects is easy... And as you find out you can also set _STA in an _INI to effectively disable the device.

And, in your case, where _STA is defined as an IntObj (via Name), you can set it to zero in an _INI (more on that later).

Problems come in if it is defined as a method. In that case, you would need to rename the method with a Clover patch, so you could inject _STA. I suspect that in the case that _STA is a method, you could also set _HID in an _INI so it doesn't match on a known PNP type.

For example:
Code:
    Device(RMD1)
    {
        Name(_HID, "RMD10000")
        Method(_INI) { \_SB.PCI0.XPET._HID = 0 } // assuming HPET renamed to XPET
    }

The object would still load (as _STA is still evaluates non-zero), but it wouldn't be "the HPET" anymore as it lost is PNP identification...

Note: More on renaming HPET->XPET later...

You can use External declarations in an SSDT to refer to objects elsewhere.

You can use Clover to rename objects that you disabled (such as your HPET example), which would allow you to use the original name and ACPI path for the new one.

For example, say you renamed HPET to XPET with Clover. The original HPET becomes known as _SB.PCI0.XPET. Your new HPET can then be defined at the original path: _SB.PCI0.HPET.

And injecting an _INI into Scope(_SB) only works if there is no existing _INI at Scope(_SB). Better to make your own _INI at its own scope (like I did above with the _INI for setting _HID).

Code:
DefinitionBlock("", "SSDT", 2, "hack", "HPET", 0)
{
    External(_SB.PCI0.XPET._STA, IntObj)
    
    Device(SH01)
    {
        Name(_HID, "SHD10000")
        Method(_INI) { Store (Zero, \_SB.PCI0.XPET._STA)  }
    }
    Device (_SB.PCI0.HPET)
    {
        Name (_HID, EisaId ("PNP0103"))
        Name (CRS, ResourceTemplate ()
        {
            IRQNoFlags () {0,8,11,12}
            Memory32Fixed (ReadWrite,
                0xFED00000,         // Address Base
                0x00000400,         // Address Length
                _Y00)
        })
        External(\_SB.PCI0.XPET.HPTS, FieldUnitObj)
        External(\_SB.PCI0.XPET.HPTE, FieldUnitObj)

        Method (_STA, 0, NotSerialized)
        {
            If (^^XPET.HPTE)
            {
                Return (0x0F)
            }
            Else
            {
                Return (Zero)
            }
        }

        Method (_CRS, 0, NotSerialized)
        {
            CreateDWordField (CRS, ^_Y00._BAS, HTBS)
            Multiply (^^XPET.HPTS, 0x1000, Local0)
            Add (Local0, 0xFED00000, HTBS)
            Return (CRS)
        }
    }
}

And since you know that HPET is always enabled...
The end result code, with a bit of cleanup...

Code:
DefinitionBlock("", "SSDT", 2, "hack", "HPET", 0)
{
    External(_SB.PCI0, DeviceObj)
    External(_SB.PCI0.XPET.HPTS, FieldUnitObj)
    
    Device(SH01)
    {
        Name(_HID, "SHD10000")
        Method(_INI) { Store (Zero, \_SB.PCI0.XPET._STA)  }
    }    
    Device(_SB.PCI0.HPET)
    {
        Name(_HID, EisaId ("PNP0103"))
        Name(CRS, ResourceTemplate ()
        {
                IRQNoFlags() {0,8,11,12}
                Memory32Fixed(ReadWrite,0xFED00000,0x00000400,CRST)
        })
        // _STA is default 0xF according to ACPI spec, so no need to specify it
        Method(_CRS)
        {
            CreateDWordField (CRS, ^CRST._BAS, HTBS)
            HTBS = (^^XPET.HPTS * 0x1000) + 0xFED00000
            Return (CRS)
        }
    }
}

And of course you need a patch for your DSDT that renames HPET to XPET (in config.plist/ACPI/DSDT/Patches). Coming up with the patch requires a little time in a hex editor finding the pattern which represents Device(HPET) in the OEM DSDT.
 
Ancient thread I know, but I just had to say thanks for sharing this Information with the internet. Just spent several days trying to get Catalina installed on my ML350p Gen8 and this is what finally allowed the USB ports to work.

Thanks again
 
Status
Not open for further replies.
Back
Top