dll可以在linux下使用吗_Linux下如何使用X86 CPU的GPIO

作者:wsg1100

出处:http://www.cnblogs.com/wsg1100/

1.前言

在arm嵌入式开发中,各个外设具有固定的物理地址,我们可以直接通过芯片手册来编写驱动配置后使用。但是在x86中有所不同,所有外设控制器集成在PCH(曾经的南桥)中,每个外设都是作为一个PCI设备挂在PCH的PCI总线上,PCH再通过DMI与CPU相联。对于标压处理器H/K系列(也就是我们台式机),南桥还在主板上,对于x86移动处理器(Y/U结尾系列),已将PCH和CPU集成到同一封装中,与如今各类SOC类似,如下(详见 datasheet )。

e1353d010e40b54a19176afe833f5816.png

由于x86中每个外设是一个PCI设备,所以我们要使用某个外设就需要为其分配内存空间映射、IRQ和I/O基址,x86中这些资源配置是由BIOS(UEFI)完成的,因为每块主板设计和外设使用不一样,就需要不一样的配置,所以不同的主板厂商需要定制自己主板的BIOS 。

BIOS配置好主板使用的外设后,一些BIOS(UEFI)通过ACPI(高级配置和电源接口)的DSDT来传递设备信息(类似arm设备树,但功能更强)给操作系统,获取这些设备信息后我们才能配置和使用这个外设,但ACPI对各个操作系统有兼容性问题,这就会出现你在Windows设备管理器能看到该设备,到linux下什么也没有,因为大部分X86硬件厂商的BIOS主要兼容Windows为主,一般桌面CPU都是用的Windows系统嘛。

本文说的GPIO就是这么个问题,linux下无法使用,由于涉及的东西有点多,所以简单介绍在如何将x86工控机引出的GPIO使用起来的(注意:是CPU的GPIO引脚,不是Super IO的GPIO)。

CPU :英特尔7代低压处理器( Kaby Lake) i5-7200U/赛扬3865Ulinux:linux 4.0以上

2.linux pinctrl子系统

要使用gpio需要先看一下linux系统PINCTRL子系统,层级如下所示(图片来源蜗窝科技):

24261e6a88230cebf730139470d40501.png

最底层是硬件控制器,其上是操作这些硬件的相关驱动(pin controller driver),不同的控制器有不同底层驱动,一般由芯片厂商BSP完成;pin controller driver初始化的时候会向pin control core模块注册pin control设备(通过pinctrl_register这个bootom level interface)。pin control core模块是一个硬件无关模块,它抽象了所有pin controller的硬件特性,仅仅从用户(各个driver就是pin control subsystem的用户)角度给出了top level的接口函数,这样, 各个driver不需要关注pin controller的底层硬件相关的内容,使用时直接向pinctrl子系统申请IO资源即可。 关于linux GPIO与pinctrl子系统信息,详见 蜗窝科技-GPIO子系统 .

pin controller driver成功注册到pin control core后,我们通过pin control core导出到sysfs的文件就可以直接操作一个GPIO,使其输入输出,而不需要专门去写一个驱动模块。

3. pin controller driver

搞嵌入式的一定对platform bus非常熟悉,pin controller driver的注册同样离不开platform bus,driver与device必须经过某种匹配后,才能进一步执行probe注册到系统中。

4c9a8081afcbd2f4904d7e00c98ee3e3.png

结合前言中对x86设备的描述,platform bus可通过以下两种方式来判断driver和device是否匹配。

probe()probe()

别忘了前提,启动时BIOS必须为使用的PCI设备分配好设备中断号(中断vector)、映射空间地址等我们才能用。那对于我们的GPIO设备linux系统使用的是哪种方式呢,这需要到源码中来看,首先七代系列CPU linux pinctrl driver源码文件为 driverspinctrlintelpinctrl-sunrisepoint.c ,看如下代码。

static const struct acpi_device_id spt_pinctrl_acpi_match[] = {{ "INT344B", (kernel_ulong_t)&sptlp_soc_data },{ "INT345D", (kernel_ulong_t)&spth_soc_data },{ }};MODULE_DEVICE_TABLE(acpi, spt_pinctrl_acpi_match);.....static struct platform_driver spt_pinctrl_driver = {.probe = spt_pinctrl_probe,.driver = {.name = "sunrisepoint-pinctrl",.acpi_match_table = spt_pinctrl_acpi_match,.pm = &spt_pinctrl_pm_ops,},};

可以看到使用的是ACPI模式,那么驱动的注册逻辑应该如下,

16758e403cd1b97c5cab2f2037be7de4.png

其中driver把系统中所有的pin描述出来,并将driver注册到platform bus。driver需要对应的device才能工作, 但是linux因为ACPI的兼容性问题,linux并没有解析DSDT并创建出GPIO 相关的device,所以没有触发执行probe来将pin controller driver注册到pin control core中,pinctrl子系统没有工作当然无法使用 。到这里我们去解决内核对ACPI的解析(或者说兼容性问题)显然是不太现实的(自己太菜(╯﹏╰)),有没有其他办法呢?

先阅读源码看看,probe()执行过程中需要用到device的哪些resource,只要我们能获取到这些resource,自己手动构造一个device注册到platform bus不就行了,O(∩_∩)O哈哈~。

int intel_pinctrl_probe(struct platform_device *pdev,const struct intel_pinctrl_soc_data *soc_data){    ......    for (i = 0; i < pctrl->ncommunities; i++) {    ......res = platform_get_resource(pdev, IORESOURCE_MEM,    community->barno);//0regs = devm_ioremap_resource(&pdev->dev, res);......}......irq = platform_get_irq(pdev, 0);    ......}

可以看到pin controller driver需要pincontrler 的地址空间和使用的中断号两部分资源,其中地址空间是三个,因为所有GPIO由三个GPIO控制器组成,三个GPIO控制器共享相同的中断线,三个GPIO控制器作为一个PCI设备。如何获取这两个信息呢?

4.手动构造device

上面通过阅读源代码得知, intel-pinctrl 需要 pincontrler 地址空间、和使用的中断号两部分资源。

地址空间起始地址可通过PCI 设备 P2SB Bridge (D31:F1) 获得。中断 vectorBIOS 配置,反编译 BIOS 给linux传递的 ACPI 信息,看是否有中断vector相关信息:

在板子上进入 /sys/firmware/acpi/tables ,将目录下所有文件考出,使用acpi工具 iasl 对DSDT文件进行反编译:

iasl -d DSDT.dat

得到 AML 文件 DSDT.dsl ,里面包含 BIOS 开发的各设备节点信息。

打开 DSDT.dsl 并找到pin controler设备节点描述,只需要搜索驱动里的"INT344B"或"INT345D"就能定位到。到这里我们也明白了,为什么驱动里的 spt_pinctrl_acpi_match[] 有两像,原来是一个代表标压处理器(H),一个代表低压处理器(U)。

Device (GPI0)        {            Method (_HID, 0, NotSerialized)  // _HID: Hardware ID            {                If ((PCHV () == SPTH))              {                    If ((PCHG == 0x02))                  {                        Return ("INT3451")                    }                    Return ("INT345D")    //表示7代标压处理器                }                Return ("INT344B")//表示7代低压处理器            Name (LINK, "_SB.PCI0.GPI0")            Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings            {                Name (RBUF, ResourceTemplate ()                {                    Memory32Fixed (ReadWrite,                        0x00000000,         // Address Base                        0x00010000,         // Address Length  地址空间大小                        _Y2E)                    Memory32Fixed (ReadWrite,                        0x00000000,         // Address Base                        0x00010000,         // Address Length   地址空间大小                        _Y2F)                    Memory32Fixed (ReadWrite,                        0x00000000,         // Address Base                        0x00010000,         // Address Length   地址空间大小                        _Y31)                    Interrupt (ResourceConsumer, Level, ActiveLow, Shared, ,, _Y30)                    {                        0x0000000E,//中断号                    }                })                                CreateDWordField (RBUF, _SB.PCI0.GPI0._CRS._Y2E._BAS, COM0)  // _BAS: Base Address              CreateDWordField (RBUF, _SB.PCI0.GPI0._CRS._Y2F._BAS, COM1)  // _BAS: Base Address              CreateDWordField (RBUF, _SB.PCI0.GPI0._CRS._Y30._INT, IRQN)  // _INT: Interrupts              COM0 = (SBRG + 0x00AF0000)              COM1 = (SBRG + 0x00AE0000)              CreateDWordField (RBUF, _SB.PCI0.GPI0._CRS._Y31._BAS, COM3)  // _BAS: Base Address              COM3 = (SBRG + 0x00AC0000)              IRQN = SGIR /* SGIR */              Return (RBUF) /* _SB_.PCI0.GPI0._CRS.RBUF */                       }

你可能看不懂上面面的信息,到底哪个是标压哪个是低压?没关系,我们去pin controller driver中,里面有注释,反推一下就知道 INT345D 代表的是标压, INT344B 代表的是低压。

/* Sunrisepoint-LP */static const struct pinctrl_pin_desc sptlp_pins[] = {    ....}static const struct intel_pinctrl_soc_data sptlp_soc_data = {.pins = sptlp_pins,    ...}...../* Sunrisepoint-H */static const struct pinctrl_pin_desc spth_pins[] = {    ....}static const struct intel_pinctrl_soc_data spth_soc_data = {.pins = spth_pins,    ...}static const struct acpi_device_id spt_pinctrl_acpi_match[] = {{ "INT344B", (kernel_ulong_t)&sptlp_soc_data },{ "INT345D", (kernel_ulong_t)&spth_soc_data },{ }};

回到正题,我们从 DSDT.dsl 获取得到中断号: 0xE,三个地址空间起始地址及大小。构建一个platform_device 如下:

#include #include #include #include #include #define P2SB_PORTID_SHIFT 16#define P2SB_PORT_GPIO3 0xAC#define P2SB_PORT_GPIO2 0xAD/*未使用*/#define P2SB_PORT_GPIO1 0xAE#define P2SB_PORT_GPIO0 0xAF#define sbreg_addr 0xfd000000 /*Address Base*//*Community 0*/#define SPT_PINCTRL_COMMUNITY0_OFFSETsbreg_addr + (P2SB_PORT_GPIO0 << P2SB_PORTID_SHIFT)#define SPT_PINCTRL_COMMUNITY0_SIZE0x00010000/*Community 1*/#define SPT_PINCTRL_COMMUNITY1_OFFSETsbreg_addr + (P2SB_PORT_GPIO1 << P2SB_PORTID_SHIFT)#define SPT_PINCTRL_COMMUNITY1_SIZE0x00010000 /*Community 2*/#define SPT_PINCTRL_COMMUNITY2_OFFSETsbreg_addr + (P2SB_PORT_GPIO2 << P2SB_PORTID_SHIFT)#define SPT_PINCTRL_COMMUNITY2_SIZE0x00010000/*Community 3*/#define SPT_PINCTRL_COMMUNITY3_OFFSETsbreg_addr + (P2SB_PORT_GPIO3 << P2SB_PORTID_SHIFT)#define SPT_PINCTRL_COMMUNITY3_SIZE0x00010000static struct resource intel_pinctrl_dev_resources[] = {/* iomem resource */DEFINE_RES_MEM_NAMED(SPT_PINCTRL_COMMUNITY0_OFFSET, SPT_PINCTRL_COMMUNITY0_SIZE, NULL),DEFINE_RES_MEM_NAMED(SPT_PINCTRL_COMMUNITY1_OFFSET, SPT_PINCTRL_COMMUNITY1_SIZE, NULL),//DEFINE_RES_MEM_NAMED(SPT_PINCTRL_COMMUNITY2_OFFSET, SPT_PINCTRL_COMMUNITY2_SIZE, NULL),/*未使用*/DEFINE_RES_MEM_NAMED(SPT_PINCTRL_COMMUNITY3_OFFSET, SPT_PINCTRL_COMMUNITY3_SIZE, NULL),/* irq resource */DEFINE_RES_IRQ(0x0E), /*反编译BIOS DSDT获取*/};static struct platform_device intel_pinctrl_device = {.name= "sunrisepoint-pinctrl",.id= -1,.resource= intel_pinctrl_dev_resources,.num_resources= ARRAY_SIZE(intel_pinctrl_dev_resources),};static int __init intel_spt_device_init(void){return platform_device_register(&intel_pinctrl_device);}module_init(intel_spt_device_init);static void __exit intel_spt_device_exit(void){platform_device_unregister(&intel_pinctrl_device);}module_exit(intel_spt_device_exit);MODULE_AUTHOR("wsg1100");MODULE_DESCRIPTION("Intel  sunrisepoint pinctrl device");MODULE_LICENSE("GPL v2");

随内核编译后,加载模块,intel pinctrl子系统正常工作,(^o^)/。

注意,相同平台,不同BIOS PCI信息可能不同!文中提供的只是一种方法

作者:wsg1100

出处:http://www.cnblogs.com/wsg1100/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值