How to identify GPIOs using ACPI

ACPI-SSDT0000.ASL - Surface RT

if we take a look at the ACPI table we see several GpioIO functions.

Identifying the Volume Keys

Identifying the Volume keys is easy. We can dump the GPIO-Controller from UEFI shell while a volume key is pressed or released.

Left: Vol-Down pressed; right Vol-Down not pressed

We see that we changed something with a button press. Nice! We see that 1 Byte has changed from 0xC0 to 0x80 at address 0x438 In binary: 1100 0000 -> 1000 0000 => Bit 6 has changed.

Lets take a look at 0x438

0x438 is located on the 5th GPIO Controller and is the input register. check.

Now we know that we changed Pin6 in Port S => Vol-Down is connected to PS6

Match it to ACPI

S is the 19th letter of the Alphabet counting from 1 (A=1, B=2, C=3,...) S is the 18th letter of the Alphabet counting from 0 (A=0, B=1, C=2,...) TRM tells us that every port has 8 pins. So lets multiply 18 by 8 which equals 144 and add our PinOffset: 6 144+6 = 150 Convert 150 from dec to hex: 0x96

There is a Gpio with the number 0x96 it belongs to TEV2/MSHW0003
Windows device-manager screenshot

We can match the 4 GPIOs to the SurfaceHomeButtonDriver. We can see that Windows knows 4 Interrupts. At the moment we dont know how to map the WindowsIRQ to ACPI-IRQ or LinuxIRQ

Match it to Linux

Linux enumerates GPIO in the same way as ACPI: portNumber * 8 + pinOffset (+ gpioControllerOffset) (gpioControllerOffset is 0 on tegra 3)

We know that we deal with: Vol-Down: 0x96 / 150d Vol-Up: 0x97 / 151d We guess 0x97 because it is a normal shared interrupt. The other 2 interrupts are WakeUp Interrupts which should belong to the Power/WindowsButton

Lets use it the easy way

include libgpiod in your root filesystem type gpiomon <BUS> <Gpio-Number> Press the button a few times you should see the gpio chaning in realtime

Lets use it the hard way

first we must set the pins to GPIO mode. Tegra standard is SFIO mode. 0x6000 d000 is the GPIO Controller base 0x0000 0408 is PortS CNF register. We want to set pin 6/7 to GPIO mode -> 0b1100 0000 -> 0xC0

we can write memory with devmem devmem 0x6000d408 8 0xC0 Now the GPIO Controller treats PS6/7 as GPIO

cd /sys/class/gpio ls ./ should show export, gpiochip0, unexport gpiochip0 is the gpio controller; 0 tells the offset we can export a pin to userspace echo 150 > export echo 151 > export Now we can use PS6/7 check the direction with cat gpio150/direction it should show "in" but you can set it with echo in > gpio150/direction Now we can read the value cat gpio150/value this should return 1 if you press VOL-Down it should return 0 This tells us that the pin is pulled up and active_low.

Now remove the gpio from userspace that it can be used by drivers again echo 150 > unexport echo 151 > unexport