问题陈述
Linux具有用于通过sysfs使用GPIO的标准接口。 它的文档可以在
这里找到。
简而言之,在“ / sys / class / gpio”文件夹中有“导出”和“取消导出”文件。 通过将数字X写入导出文件,可以在用户空间中打开接口以控制GPIOX
打开界面后,将显示文件夹/ sys / class / gpio / gpioX /,其中将包含“值”或“方向”之类的文件,并在“方向”文件中写入“入”或“出”,并向文件中写入1或0 “值”可以直接从命令行控制GPIO输出。
为了使用“ 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 #define GPIO_CNTL 0x4C ... #define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0)
南桥\英特尔\ lynxpoint \ early_pch.c:
pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE|1); 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(代码稍微简化了):
Scope (\_SB) { Device (PCI0) { ... Device (GPIO) {
要详细了解此代码,您应该熟悉
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文件与驱动程序进行通信