Formulation of the problem
Linux has a standard interface for working with GPIO via sysfs. Documentation on it can be found
here .
In short, the "/ sys / class / gpio" folder contains the files "export" and "unexport". By writing the number X to the export file, you can open the interface in the user space to control GPIOX
After opening the interface, the folder / sys / class / gpio / gpioX / will appear in which there will be such files as “value” or “direction”, and by writing “in” or “out” to the file “direction” and writing 1 or 0 to the file "Value" can be controlled by the output of GPIO directly via the command line.
')
In order for the “echo X> / sys / class / gpio / export” command to create the “gpioX” folder, the GPIO controller driver must be registered in the kernel, opening the interface to the GPIO lines.
It so happened that I'm working on porting a coreboot for a custom card based on the Intel Haswell i7 processor [For those who don't know, coreboot is an open source BIOS project with open source (
https://www.coreboot.org/ ) ]. I have a LynxpointLP south bridge embedded in my processor with 94 GPIO lines. And I wanted to open them in sysfs ...
Problem solving (driver and device communication in Linux)
After doing a little search through the kernel code, I found that the driver was already written, is in the drivers \ gpio \ gpio-lynxpoint.c file, and is enabled using Kconfig
config GPIO_LYNXPOINT tristate "Intel Lynxpoint GPIO support" depends on ACPI && X86 select GPIOLIB_IRQCHIP help driver for GPIO functionality on Intel Lynxpoint PCH chipset Requires ACPI device enumeration code to set up a platform device.
The GPIO_LYNXPOINT option was enabled in the kernel with which I worked, but in the folder "/ sys / class / gpio /" there was not a single folder "gpiochipN" for the GPIO controller (which should be), and even such a script did not result in exporting any lines.
$ for i in {0..255}; do echo $i > /sys/class/gpio/export; done
Having looked at the coreboot code or having examined the documentation for this south bridge, it can be understood that the GPIO controller is not a separate PCI device. It is part of another PCI device: LPC Interface Bridge. Using the PCI configuration space registers of this device, you must enable the GPIO controller and assign it BASE_ADDRESS in the I / O space. This will open a window in I / O space of 1kV size. By writing / reading the bytes in this window, you can manage the lines of GPIO.
What we can see in the coreboot code:
southbridge \ intel \ lynxpoint \ pch.h:
#define DEFAULT_GPIOBASE 0x1400 #define DEFAULT_GPIOSIZE 0x400 ... #define GPIO_BASE 0x48 #define GPIO_CNTL 0x4C ... #define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0)
southbridge \ intel \ lynxpoint \ early_pch.c:
pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE|1); pci_write_config8(PCH_LPC_DEV, GPIO_CNTL, 0x10);
If we look at the LPC device registers in Linux through “lspci -xxx”, we will see that we have recorded information in these registers. So everything seems to be set up as it should.
Continuing to watch the driver code, I noticed that the Linux driver communicates with the device through the .acpi_match_table field. Since our device cannot be enumerated (it is not located either on the PCI or on the USB bus), then the driver is required for it, and the driver is connected to the device via the ACPI table. The usual case for x86, in the case of ARM, we would have registered our device in DeviceTree, or in the old hardcode in the kernel.
drivers \ gpio \ gpio-lynxpoint.c:
static const struct acpi_device_id lynxpoint_gpio_acpi_match[] = { { "INT33C7", 0 }, { "INT3437", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, lynxpoint_gpio_acpi_match); static struct platform_driver lp_gpio_driver = { .probe = lp_gpio_probe, .remove = lp_gpio_remove, .driver = { .name = "lp_gpio", .pm = &lp_gpio_pm_ops, .acpi_match_table = ACPI_PTR(lynxpoint_gpio_acpi_match), }, };
It works like this: if the kernel, when parsing the ACPI table, sees in it a device with _HID identifier "INT33C7", then it will try to find a platform driver for it with matching identifiers in the ".driver-> acpi_match_table" structure fields.
When a match is found, Linux will execute the .probe driver function.
As it turned out, the ACPI code for this device was presented in the coreboot, I just had it commented out. Commented out for the reason that for this device, Windows could not find the driver and output "Unknown device" in the device manager. More on this will be below.
So we are interested in information from the file.
src \ southbridge \ intel \ lynxpoint \ acpi \ serialio.asl (the code is a bit simplified):
Scope (\_SB) { Device (PCI0) { ... Device (GPIO) {
To parse this code in detail, you should familiarize yourself with the ASL language syntax in
the ACPI specification .
But in short, this code creates a device with the identifier "INT33C7" which has 2 resources:
I/O memory: 1400-17ff; IRQ: 14;
Inside its .probe Linux function, the driver obtains the above-indicated device resources as follows:
io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
Based on this data, the driver code will fill in the gpio_chip structure and register the gpio controller in the system, making it available through the sysfs interface.
Having returned the device's ASL code and recompiled the BIOS image, the system managed to access the GPIO via sysfs.
For a start, the folder "gpiochip162" appeared in / sys / class / gpio. This folder contains the file "base" and "ngpio". The base file is responsible for the number of the first GPIO of this controller, ngpio for their number.
$ cat /sys/class/gpio/gpiochip162/base 162 $ cat /sys/class/gpio/gpiochip162/ngpio 94
So everything was exported as it should. Run the script:
$ for i in {162..255}; do echo $i > /sys/class/gpio/export; done
After that, gpioN subfolders appear in the / sys / class / gpio / folder, inside which there will be files for managing the state of the line.
A couple of comments:
- The folder / sys / class / gpio162 / is responsible for managing GPIO0, the folder / sys / class / gpio163 / is responsible for GPIO1, etc. Such a shift occurred due to the fact that the driver, during the initialization of the “struct gpio_chip” control structure, assigned “gc-> base = -1;”. That is, provided the kernel to choose the numbers themselves. This is generally not critical, but it is worth remembering that.
- Access is opened only to GPIO lines that are configured as GPIO, and not as any native functions of the south bridge. For such lines, the driver displays information in dmesg: "gpio% d reserved for ACPI". In the case of coreboot, the GPIO setting is made in the “gpio.h” file in the folder with the motherboard.
- Comparison of the device and the driver can also go according to the _CID (Compatible ID) method, and documentation on our topic in the kernel is presented in the document “ACPI based device enumeration”
It should be noted that the “INT33C7” device is not in ACPI tables of 2 proprietary motherboards on the same chipset (from IBASE and DFI). The truth is, there most likely the GPIO lines are not displayed (I did not look at the documentation in detail at this point).
ID "INT33C7"
After raising the sysfs functionality, I had a question, where did the identification number “INT33C7” come from?
Looking at the documentation for the _HID method, it became clear that it is worth looking at
http://www.uefi.org/PNP_ACPI_Registry_HID (Hardware ID)_HID (Hardware ID)This object is used to supply OSPM with the device's PNP ID or ACPI ID *
When describing a platform, use of any _HID objects is optional. However, a _HID object must be
Used by OSPM. OSPM only enumerates a device
when no bus enumerator can detect the device ID. For example, devices on an ISA bus are
enumerated by OSPM. Use the _ADR object to describe devices enumerated by bus enumerators
other than OSPM.
Arguments:None
Return Value:An Integer or String containing the HID
A _HID object evaluates a 32-bit compressed EISA type ID or a string. If a
alphanumeric PNP or ACPI ID with no asterisk or other leading
characters.
A valid PNP ID where the form is "AAA ####" where A is an uppercase letter and # is a hex
digit. A valid ACPI ID must be NNNN #### where N is an uppercase letter or a
digit ('0' - '9') and # is a hex digit. This specification reserves the string "ACPI" for use only
with devices defined herein. It further reserves all strings of 4 HEX digits for
exclusive use with PCI-assigned Vendor IDs.
* -PNP ID and ACPI ID Registry is at
http://www.uefi.org/PNP_ACPI_Registry This link has 3 points:
- all kinds of 3 letter identifiers (PNP ID) are indicated here
- PNP IDs that begin with “PNP” are reserved by Microsoft here.
- All kinds of 4 letter identifiers (ACPI ID) are indicated here.
It is not very clear why, but according to the PNP ID list, you can find that the identifiers "INT" are reserved at INTERPHASE CORPORATION:
INTERPHASE CORPORATION INT 11/29/1996
Apparently, a single list of full device identifiers (letter part + numeric) is not published. But with the help of Google, we managed to find lists of devices and their _HID, for example,
here or
here .
They indicate:
INT33C7=Intel Serial I/O GPIO Host Controller
And judging by the remaining lines from this list, all INTxxxx devices are Intel devices (now it sounds quite obvious, but the connection with INTERPHASE CORPORATION is not clear; it is also not very clear why the numbering starts with such large numbers, but it seems to be Intel discretion).
Driver and device communication in Windows
Satisfying my curiosity, I decided to download Windows on my board. As expected, the system could not find a driver for the device. There was no help from the drivers for the IBASE and DFI boards, which is understandable, because this device is not indicated in the BIOS of these boards.
I managed to find a driver
on the Microsoft websiteHowever, there this driver is presented only for Windows 8.1 and higher. I'm still working with Windows 7.
Nevertheless, I tried to download one of the drivers and specify its folder when searching for a driver for my unknown device.
However, the dispatcher could not match the driver to the device. Although the inf file clearly contained information about the device INT33C7.
[Manufacturer] %INTEL%=Intel,NTamd64.6.3 [Intel.NTamd64.6.3] %iaLPSS_GPIO.DeviceDesc_LPT%=iaLPSS_GPIO_Device, ACPI\INT33C7 %iaLPSS_GPIO.DeviceDesc_WPT%=iaLPSS_GPIO_Device, ACPI\INT3437
In the process of parsing the INF file, it turned out that in the [Manufacturer] section it was clearly stated that it was not intended for my system:
What Intel.NTamd64.6.3 means can be understood
by the description :
nt[Architecture][.[OSMajorVersion][.[OSMinorVersion] OSMajorVersion=6 => Windows 7/Windows 8.1/Windows Server 2012 R2/... OSMinorVersion=3 => Windows 8.1/Windows Server 2012 R2
I did not succeed in trying to push the Windows 7 driver by replacing Intel.NTamd64.6.3 with Intel.NTamd64.6.1, as it gave me a blue screen of death and an unbootable OS, and I had to restore it.
The driver for Win7 was found only on an incomprehensible website on the Internet, and then the device is displayed in the device manager with an exclamation mark.
Realizing his impotence, I decided to test the functionality on Windows 10. There was a pleasant surprise.
"Intel Chipset Device Software (INF Update Utility)" installed the driver for my controller without any problems.

As you can see, this device has resources indicated by us.

In theory, after installing the driver with the GPIO controller, it will most likely be possible to work through the IOCTL functions (
such as in this document) .
However, there were no GPIO programming tasks from Windows, so the search for a similar document for my chipset was postponed.
Conclusion:
This article has reviewed the driver and device communication using the _HID ACPI method. Such a connection may be required on an x86 system for devices that cannot be enumerated.
- In the case of Linux, communication with the driver is done via .acpi_match_table
- In the case of Windows, communication with the driver is carried out through the INF file