[知其然不知其所以然-4] 为什么我的Surface 3触摸屏不工作

其实不是我的触摸屏,是bugzilla上有人在问,为什么他Surface 3上的触摸屏不工作。然后根据dmesg看到启动时的一堆可疑告警信息:

Sep 05 14:14:04 localhost kernel: ACPI Error: No handler for Region [GPOR] (ffff88013f4af3a8) [GeneralPurposeIo] (20150619/evregion-163)
Sep 05 14:14:04 localhost kernel: ACPI Error: Region GeneralPurposeIo (ID=8) has no handler (20150619/exfldio-297)
Sep 05 14:14:04 localhost kernel: ACPI Error: Method parse/execution failed [\_SB_.PCI0.SPI1.NTRG._STA] (Node ffff88013f4b15a0), AE_NOT_EXIST (20150619/psparse-536)

(Full log at https://bugzilla.kernel.org/attachment.cgi?id=186711 )

Which in the DSDT corresponds to:
    Scope (_SB.PCI0.SPI1)
    {
        Device (NTRG)
        {
            Name (_HID, "MSHW0037")  // _HID: Hardware ID
            Name (_UID, One)  // _UID: Unique ID
            Name (_DEP, Package (0x04)  // _DEP: Dependencies
            {
                GPO0, 
                GPO1, 
                GPO3, 
                SPI1
            })

The MSHW0037 device is the touchscreen, in Windows' device manager program.


我的第一反应是没有反应。因为我对acpi不熟。于是翻阅了一下代码,从No handler for Region这句告警开始搜代码,

发现大概意思是,linux没有给OperationRegion这种区域设置操作方法,导致访问这个OperationRegion区间时出错。

什么是OperationRegion? 就是一段物理地址区间,它用的比较多的场合,是一些IO空间,比如说该问题里的:

 OperationRegion (GPOR, GeneralPurposeIo, Zero, One)
            Field (GPOR, ByteAcc, NoLock, Preserve)
            {
                Connection (
                    GpioIo (Shared, PullDefault, 0x0000, 0x0000, IoRestrictionNone,
                        "\\_SB.GPO3", 0x00, ResourceConsumer, ,
                        )
                        {   // Pin list
                            0x0051
                        }
                ), 
                SDCD,   1, 
                Connection (
                    GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
                        "\\_SB.GPO3", 0x00, ResourceConsumer, ,
                        )
                        {   // Pin list
                            0x003D
                        }
                ), 
                PDN1,   1
            }

表示的是,从GenerialPurposeIo开始,偏移为0字节,长度为1字节的一段区间,我们给它个别名,叫GPOR,

然后,我们还想访问这个区间里的某些字节,比如SDCD开始的一个bit,或者PDN1开始的一个bit。

或者说我们直接想访问gpio controller的pinlist 0x51,或者pinlist 0x3D,都需要操作这个区间,而ASL代码里,

访问这些区间的代码片段,一般就是Store(SDCD,0xaaa)这种。那么对于Linux来说,需要把Store的这句话解析

成访问GeneralPurposeIo空间的访问,这个针对具体空间的操作,就需要相关的驱动来实现,比如这个例子里,

就需要实现对GeneralPurposeIo读写的回调,也就是对GPIO controller的读写。这就是address space handler

的来历。对cherryview平台来说,回调是pinctrl driver在probe到INT33FF这个gpio controller后,在

acpi_gpiochip_request_regions函数内,通过acpi_install_address_space_handler安装上了gpio区间的操作

实现函数。


在开头提出的故障现象中,可以看出,有设备尝试访问gpio区间却发现gpio区间函数没有安装,于是失败了。失败的结果

是,设备MSHW0037的gpio驱动初始化不正常,导致触摸屏无法工作。再详细一点说,就是MSHW0037设备在

访问_STA时,尝试通过读取GPO0的数据,而这个时候,GPO0的驱动,也就是cherryview的驱动还没有加载,从而

执行_STA返回错误(注意,不是_STA的值返回错误,而是执行_STA这个动作返回错误):

          

  Method (_STA, 0, NotSerialized)  // _STA: Status
            {
                If ((^^^^GPO0.HELD == One))
                {
                    Return (0x0F)
                }
                Else
                {
                    Return (Zero)
                }
            }
执行失败的结果是,MSHW0037设备对应的acpi_device不会被创建,于是SPI slave驱动加载时,将不会成功,从而触摸屏不工作。
MSHW0039执行_STA的时机是:acpi_scan_init->acpi_bus_scan->acpi_bus_check_add->acpi_bus_type_and_status

->acpi_bus_get_status_handle->

acpi_evaluate_integer(handle, "_STA", NULL, sta);

返回AE_NOT_EXIST,从而acpi_bus_type_and_status返回-ENODEV,

从而acpi_bus_check_add认为已经执行完毕,就返回了,不会继续执行acpi_add_single_object来

为设备创建acpi_device。


那么问题出在哪里呢,是_STA设计不周全,应该考虑到启动过程中,gpio可能还不可访问。要改_STA比较麻烦,

毕竟MS是不太愿意改bios的,于是在linux里做些小动作,我们可以这样,就算_STA返回不可执行,我们

也给设备状态设置为0,表示当前不可用,但执行这个_STA还是返回成功,这样让后续流程继续执行

acpi_device的创建。从而让SPI驱动能够被加载成功。


你可能感兴趣的:([知其然不知其所以然-4] 为什么我的Surface 3触摸屏不工作)