以Lynxpoint控制器的GPIO通过_HID ACPI方法在驱动程序和设备之间进行通信

问题陈述


Linux具有用于通过sysfs使用GPIO的标准接口。 它的文档可以在这里找到。

简而言之,在“ / sys / class / gpio”文件夹中有“导出”和“取消导出”文件。 通过将数字X写入导出文件,可以在用户空间中打开接口以控制GPIOX

#    user space   GPIO12 $ echo 12 > /sys/class/gpio/export 

打开界面后,将显示文件夹/ sys / class / gpio / gpioX /,其中将包含“值”或“方向”之类的文件,并在“方向”文件中写入“入”或“出”,并向文件中写入1或0 “值”可以直接从命令行控制GPIO输出。

 #  GPIO   $ echo "out" > /sys/class/gpio/gpio12/direction #  1   GPIO $ echo 1 > /sys/class/gpio/gpio12/value 

为了使用“ echo X> / sys / class / gpio / export”命令创建“ gpioX”文件夹,必须在内核中注册GPIO控制器驱动程序,这将打开GPIO线的接口。

碰巧我正在为基于Intel Haswell i7处理器的定制板移植coreboot [对于那些不知道的人,coreboot是开放源代码BIOS项目开放源代码( https://www.coreboot.org/ ) ]。 我的处理器内置了LynxpointLP南桥,其中有94条GPIO线。 我想在sysfs中打开它们...

解决问题(Linux中的驱动程序和设备通信)


简短搜索内核代码后,我发现该驱动程序已经编写,位于文件“ drivers \ gpio \ gpio-lynxpoint.c”中,并且已使用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. 

我正在使用的内核中启用了GPIO_LYNXPOINT选项,但是,“ / sys / class / gpio /”文件夹(应该是)中没有用于GPIO控制器的单个“ gpiochipN”文件夹,即使这样的脚本也没有导出任何内容线。

 $ for i in {0..255}; do echo $i > /sys/class/gpio/export; done 

查看coreboot代码或该南桥的文档,您会发现GPIO控制器不是独立的PCI设备。 它是另一个PCI设备的一部分:LPC接口桥。 使用该设备的PCI配置空间寄存器,必须启用GPIO控制器并在I / O空间中为其分配BASE_ADDRESS。 这将在1KV I / O空间中打开一个窗口。 通过在此窗口中写入/读取字节,可以控制GPIO线。

我们可以在coreboot代码中看到:

南桥\英特尔\ lyxpoint \ pch.h:

 #define DEFAULT_GPIOBASE 0x1400 #define DEFAULT_GPIOSIZE 0x400 ... #define GPIO_BASE 0x48 /* LPC GPIO Base Address Register */ #define GPIO_CNTL 0x4C /* LPC GPIO Control Register */ ... /* PCI Configuration Space (D31:F0): LPC */ #define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0) 

南桥\英特尔\ lynxpoint \ early_pch.c:

 /* Setup GPIO Base Address */ pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE|1); /* Enable GPIO functionality. */ pci_write_config8(PCH_LPC_DEV, GPIO_CNTL, 0x10); 

如果我们通过“ lspci -xxx”查看Linux中的LPC设备寄存器,我们将看到我们记录的数据在这些寄存器中。 因此,一切似乎都已按需配置。

继续查看驱动程序代码,我注意到Linux驱动程序通过.acpi_match_table字段与设备通信。 由于无法枚举我们的设备(它既不在PCI上也不在USB总线上),因此需要平台驱动程序,并且该驱动程序与设备的连接是通过ACPI表进行的。 对于x86,在ARM的情况下,通常将在DeviceTree中注册设备,或在内核中使用旧的硬代码。

驱动程序\ 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), }, }; 

它的工作原理是:如果内核在解析ACPI表时看到其中带有_HID标识符“ INT33C7”的设备,则它将尝试在结构“ .driver-> acpi_match_table”的字段中为其找到具有匹配标识符的平台驱动程序。

找到匹配项后,Linux将执行.probe驱动程序功能。

事实证明,该设备的ACPI代码是在coreboot中提供的,我只是将其注释掉了。 由于该设备Windows无法找到驱动程序并在设备管理器中显示“未知设备”,因此将其注释掉。 在下面的更多内容。

因此,我们对文件中的信息感兴趣
src \ southbridge \ intel \ lynxpoint \ acpi \ serialio.asl(代码稍微简化了):

 /*     * src\southbridge\intel\lynxpoint\pch.h * #define DEFAULT_GPIOBASE 0x1400 * #define DEFAULT_GPIOSIZE 0x400 */ Scope (\_SB) { Device (PCI0) { ... Device (GPIO) { // GPIO Controller Name (_HID, "INT33C7") Name (_CID, "INT33C7") Name (_UID, 1) Name (RBUF, ResourceTemplate() { DWordIo (ResourceProducer, MinFixed, // IsMinFixed MaxFixed, // IsMaxFixed PosDecode, // Decode EntireRange, // ISARanges 0x00000000, // AddressGranularity 0x00000000, // AddressMinimum 0x00000000, // AddressMaximum 0x00000000, // AddressTranslation 0x00000001, // RangeLength , // ResourceSourceIndex , // ResourceSource BAR0) Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, , , ) {14} }) Method (_CRS, 0, NotSerialized) { CreateDwordField (^RBUF, ^BAR0._MIN, BMIN) CreateDwordField (^RBUF, ^BAR0._MAX, BMAX) CreateDwordField (^RBUF, ^BAR0._LEN, BLEN) Store (DEFAULT_GPIOSIZE, BLEN) Store (DEFAULT_GPIOBASE, BMIN) Store (Subtract (Add (DEFAULT_GPIOBASE, DEFAULT_GPIOSIZE), 1), BMAX) Return (RBUF) } Method (_STA, 0, NotSerialized) { Return (0xF) } } ... } } 

要详细了解此代码,您应该熟悉ACPI规范中的ASL语法。

但是简而言之,这段代码创建了一个标识符为“ INT33C7”的设备,该设备具有2个资源:

 I/O memory: 1400-17ff; IRQ: 14; 

在其.probe Linux函数中,驱动程序按以下方式接收上述设备资源:

 io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0); irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 

根据此数据,驱动程序代码将填充gpio_chip结构并在系统中注册gpio控制器,这将使其可以通过sysfs接口进行访问。

返回设备的ASL代码并重新编译BIOS映像后,系统设法通过sysfs访问GPIO。

首先,文件夹“ gpiochip162”出现在/ sys / class / gpio中。 该文件夹包含文件“ base”和“ ngpio”。 基本文件负责此控制器的第一个GPIO的编号,ngpio为其编号。

 $ cat /sys/class/gpio/gpiochip162/base 162 $ cat /sys/class/gpio/gpiochip162/ngpio 94 

因此,所有内容均已导出。 我们执行脚本:

 $ for i in {162..255}; do echo $i > /sys/class/gpio/export; done 

之后,gpioN子文件夹将出现在/ sys / class / gpio /文件夹中,该文件夹中将存在用于控制该行状态的文件。

几点评论:

  • / sys / class / gpio162 /文件夹负责管理GPIO0,/ sys / class / gpio163 /文件夹负责GPIO1 发生这种移位的原因是,驱动程序在初始化控制结构“ struct gpio_chip”时分配了“ gc-> base = -1;”。 也就是说,我离开了内核自己选择数字。 这通常并不重要,但是值得记住。
  • 只能访问配置为GPIO的GPIO线路,而不能访问任何本地Southbridge功能。 对于此类行,驱动程序以dmesg显示信息:“为ACPI保留的gpio%d”。 如果使用coreboot,则在主板文件夹中的“ gpio.h”文件中配置GPIO。
  • 设备和驱动程序也可以使用_CID(兼容ID)方法进行映射,内核中我们主题的文档位于“基于ACPI的设备枚举”文档中

值得注意的是,在ACPI表中,INT33C7设备在同一芯片组(来自IBASE和DFI)上没有2个专有主板。 没错,很可能没有GPIO线输出(那时候我没有详细看文档)。

标识符“ INT33C7”


在提高sysfs功能之后,我有一个问题,“ INT33C7”标识号来自哪里?

在查看_HID方法的文档后,很明显值得一看http://www.uefi.org/PNP_ACPI_Registry

_HID(硬件ID)
_HID(硬件ID)
该对象用于向OSPM提供设备的PNP ID或ACPI ID *
描述平台时,可以使用任何_HID对象。 但是,_HID对象必须是
用于描述OSPM将枚举的任何设备。 OSPM仅枚举设备
当没有总线枚举器可以检测到设备ID时。 例如,ISA总线上的设备是
由OSPM枚举。 使用_ADR对象描述总线枚举器枚举的设备
除了OSPM。

参数:


返回值:
包含HID的整数或字符串
_HID对象的计算结果为数字32位压缩EISA类型ID或字符串。 如果一个
字符串,格式必须为字母数字PNP或ACPI ID,且不带星号或其他前导
字符。

有效的PNP ID的格式必须为“ AAA ####”,其中A为大写字母,#为十六进制
数字。 有效的ACPI ID的格式必须为“ NNNN ####”,其中N是大写字母或
数字('0'-'9'),而#是十六进制数字。 该规范保留字符串“ ACPI”仅用于
与设备定义的列表。 它还保留所有代表4个十六进制数字的字符串
专门用于PCI分配的供应商ID。

*-PNP ID和ACPI ID注册表位于http://www.uefi.org/PNP_ACPI_Registry

此链接上有3点:

  • 此处显示所有类型的3个字母标识符(PNP ID)
  • 此处显示以Microsoft保留的“ PNP”开头的PNP ID
  • 此处显示各种4个字母的标识符(ACPI ID)

目前尚不清楚原因,但是从PNP ID列表中,您可以发现INTERPHASE CORPORATION保留了“ INT”标识符:

 INTERPHASE CORPORATION INT 11/29/1996 

显然,没有发布完整的设备标识符的单个列表(字母部分+数字)。 但是在Google的帮助下,可以在此处此处找到设备列表及其_HID。

它们表明:

 INT33C7=Intel Serial I/O GPIO Host Controller 

从列表中的其余行来看,所有INTxxxx设备都是Intel设备(现在听起来很明显,但是与INTERPHASE CORPORATION的连接仍不清楚;也不清楚为什么编号从这么大的数字开始,但是在英特尔酌情决定权)。

Windows中的通讯驱动程序和设备


出于好奇,我决定将Windows下载到我的主板上。 不出所料,系统找不到该设备的驱动程序。 IBASE和DFI板卡的驱动程序没有帮助,这是可以理解的,因为在这些板卡的BIOS中未指示该设备。

我设法在Microsoft网站上找到了驱动程序

但是,仅在Windows 8.1和更高版本中提供了该驱动程序。 我仍在使用Windows 7。

但是,当我为未知设备搜索驱动程序时,我尝试下载其中一个驱动程序并指定其文件夹。

但是,调度程序无法将驱动程序映射到设备。 尽管inf文件明确包含有关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 

在分析INF文件的过程中,事实证明[Manufacturer]部分明确表明它不适用于我的系统:

从描述中可以理解Intel.NTamd64.6.3的含义:

 nt[Architecture][.[OSMajorVersion][.[OSMinorVersion] OSMajorVersion=6 => Windows 7/Windows 8.1/Windows Server 2012 R2/... OSMinorVersion=3 => Windows 8.1/Windows Server 2012 R2 

尝试通过用Intel.NTamd64.6.1替换Intel.NTamd64.6.3来推送Windows 7驱动程序,但失败了,因为它给了我蓝屏死机和无法启动的OS,因此我必须进行恢复。

仅在Internet上无法理解的网站上找到Win7的驱动程序,然后在设备管理器中的设备上显示带有感叹号的设备。

意识到他的无能为力之后,我决定在Windows 10上测试该功能。 英特尔芯片组设备软件(INF更新实用程序)为我的控制器安装了驱动程序,没有任何问题。



如您所见,该设备具有我们指示的资源。



从理论上讲,在使用GPIO控制器安装驱动程序之后,很可能可以通过IOCTL功能工作( 如本文档中所述)

但是,Windows没有GPIO编程任务,因此推迟了针对我的芯片组的相似文档的搜索。



结论:


本文使用_HID ACPI方法检查了驱动程序和设备之间的连接。 对于无法枚举的设备,在x86系统上可能需要此类通信。

  • 如果是Linux,则通过.acpi_match_table与驱动程序进行通信
  • 对于Windows,通过INF文件与驱动程序进行通信

Source: https://habr.com/ru/post/zh-CN429336/


All Articles