- 由 b178903294创建, 最后修改于9月 19, 2019 版权所有,谢绝转载
此看门狗相关寄存器位于PMC设备中,用于监控系统电源状态的。PMC (Power Management Controller) 是一个PCI device, pci地址为B/D/F=0/13/1,这个地址是IPC1/GCR/ACPI块的基地址。 关于pci地址如何计算出16进制物理地址请参照:PCI/PCIe基础——配置空间
近日接到需求,在系统log中看到iTCO_wdt在probe的过程中失败:
|
所以第一时间就去看了是哪里打印出了这一条错误的log,后来经过测试发现是在iTCO_wdt probe的过程中申请相关寄存器没有成功,所以返回错误,具体是下面这段报错:
iTCO_wdt_probe
|
request_mem_region的时候直接报错返回。那么肯定是这块内存申请不下来,那么这时候就去查了一下内存和谁冲突了??? 通过cat /proc/iomem 发现:
|
原来是这两个内存段冲突了。那么问题来了我们的内存段是从哪里申请过来的???platform_get_resource这句函数就是申请相关资源用的,但是哪里定义了这些资源呢???经过艰苦卓绝的寻找,发现再intel_pmc_ipc.c中定义了相关资源:
|
与iTCO_wdt相关的io和内存资源都在这里面定义了,具体是由下面这段代码添加的资源:
|
我们可以看到这些资源都是通过一个基地址加上偏移后才赋给tco_res的,那我们上面在iTCO_wdt probe那段代码中获取失败的是一个内存资源IORESOURCE_MEM,在上面这段代码中我们可以看到内存资源是由ipcdev.gcr_base这样的一个基地址获取的,通过寻找发现下面这段代码赋予了这个值;
|
通过阅读代码发现size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE; 这个给ipc块的内存给大了,器件手册559811-apl-iafw-vol2of2-bios-spec-rev2p1.pdf(详见附件)上写的这段内存为4k字节。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~先略过这里,最后再看~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(PS: 如果修改此处size会造成s0ix_residencu_usec异常,因为intel_pmc_s0ix_counter_read()中读写GCR寄存器是从这里申请和ioremap的,如果修改那么会造成gcr_data_readq()、gcr_data_readq()读写溢出。可见相关内核作者们做patch的时候也是考虑不周,挖下了深坑。如果想彻底修改好,应该直接把这里的addr地址加上1008h偏移传给iTCO_wdt.c中iTCO_wdt_probe()的iTCO_wdt_private.gcs_pmc,不需要再经过iTCO_wdt_probe()中的request_mem_region()和ioremap()即可使用。)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PMC is a PCI device with B/D/F = 0/13/1 and this device represents address space for IPC1/GCR/ACPI block. The PMC function contains three BARs:
- BAR0 (8KB) – Lower 4KB for IPC1, upper 4KB for GCR (GCR BAR aka PBASE).
- BAR1 (4KB) – PCI configuration space aliased as MMIO in ACPI mode.
- BAR2 (I/O 128B) – ACPI configuration. After programming these BARs, IA FW should configure the PMC function as ACPI function.
宏定义定义了这两个块的大小;
|
所以这就把本应该属于GCR的内存区域占用了,才会导致tco_res获取的内存资源在iTCO_wdt 的probe中request_mem_region时候失败。我们把这里修改后probe过程就能够正常了。但是阅读代码发现iTCO_wdt probe中iTCO_wdt_private.gcs_pmc_res获取的应该是GCR区域的PM CFG - Power Management Configuration (PMC_CFG)—Offset 1008h寄存器(详见附件557556-apl-soc-eds-vol2-rev2p4.pdf) ,但是这里却把整个GCR的内存资源给了他,这之后肯定会由于读写的寄存器错误而导致功能不正常的,所以在static int ipc_create_tco_device(void)原有的地址上加上偏移并修改区域大小再传给iTCO_wdt_private.gcs_pmc_res。
|
修改过后,对应寄存器的地址就都能正确对应上了。不仅块地址分配错误,还有一些寄存器的偏移地址也是错误的,所以在修改驱动的时候一定要对照器件手册,把每个寄存器和io都设置正确才能确保驱动正常运行。
那么肯定有小伙伴好奇了,ipc_plat_get_res(struct platform_device *pdev)中的platform_get_resource又是从哪里获取的资源呢???长春哥给出的建议是dts,在ARM平台中大家第一时间就会想到.dts 也就是设备树,那么在x86平台中不使用.dts我们该去哪里寻找呢??报着这个疑问在听取侠松哥的建议后开始在bios中寻找,又经过了一轮艰苦卓绝的寻找,终于在coreboot中找了定义的位置了。在coreboot/src/soc/intel/apollolake/include/soc/iomap.h中定义了该PCM块的基地址:
iomap.h
|
并在src/soc/intel/apollolake/pmc.c中使用了这个定义添加了相应资源:
pmc.c
|
ps:所以小伙伴们在找不到相关资源定义的时候不妨去bios中去寻找一下,没准会有什么新发现。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
经过以上修改再核对iTCO_wdt.c中对对应io和内存资源的使用确认软件逻辑上已经没有什么问题了,我们开开心心的去测试 使用cat > /dev/watchdog 连敲两次回车就会触发我们的狗启动,一旦启动没办法停止,只有不断喂狗才能保障系统不会重启,一旦停止喂狗系统将在设定的heartbeat值计数完成后重启。但是通过测试发现当出发后设定时间内并没有重启,而是有几率的,并且时间不定。那么问题又来了,是软件驱动仍然有问题还是什么???通过加log看相关寄存器的状态发现有一个寄存器并没有如期置1,log如下:
展开源码
可以看到当我们的剩余时间快为0的时候计数开始从4到1不断循环了不会到0,并且TCO_STS[17]寄存器读出来的值一致都为0,从器件手册(详见附件557556-apl-soc-eds-vol2-rev2p4.pdf)中的TCO_STS寄存器表中看到:
Second Timeout Status (second_to_sts): PMC sets this bit to 1 to indicate that the TIMEOUT bit had been (or is currently) set and a second timeout occurred before the TCO_RLD register was written. If this bit is set and the NO_REBOOT config bit is 0, then the PMC will reboot the system after the second timeout. The reboot is done by interrupting the Arc and starting a reset flow based on the OS_POLICY. This bit is only cleared by writing a 1 to this bit or by a reset. On some prior platforms, this field is reset on RSMRST_B, a reset signal based on a RSMRST# pin that indicates the suspend/ resume voltages are stable. This field is reset on RSM_RST_N de-assertion. This field is not reset on cold reset, warm reset, and Sx.
TCO_STS[17]这一位会被RSM_RST_N脚置零,所以系统不会重启。那么这位一直被置零的话岂不是永远不会重启了???所以就去看看RSM_RST_N脚的变换条件,通过器件手册pentium-celeron-n-series-j-series-datasheet-vol-1.pdf 找到了这个脚的定义:
signal | Dir. | I/O Voltage | Type |
---|---|---|---|
RSM_RST_N | I | V3P3 | Resume Well Reset Used for resetting the res well. An external RC circuit is required to guarantee that the resume well power is vaild prior to this signal going high. |
既然要用RC电路来保障他稳定没有毛刺,那么就顺便去看看他的电路图吧,我们先看看这个脚的编号,在器件手册pentium-celeron-n-series-j-series-datasheet-vol-1.pdf 的Table 6-1中可以看到此脚编号为AC57。那么我们去开发设备那看看对应的电路;
震惊震惊震惊!!!器件手册中说好的是输入脚但是在硬件原理图中变成了输出了,所以此脚的电平就是个未知的东西了,那么指望他去决定TCO_STS[17]的状态不就成了奢望了吗? 所以我们的看门狗会启动后不知道什么时候才会重启的原因就是RSM_RST_N的状态有问题!! 至此我们找到了iTCO_wdt 从内核驱动代码问题到硬件配置问题的一系列坑,由于硬件电路我们不能修改,即使我们的软件毫无问题,那么这个狗的工作也是不正常的,所以至此只能放弃这条狗了,不能让他活着时不时不受控的重启系统。
ps:理论上应该还要把Q80 NMOS拆下然后进行功能测试。
附件:559811-apl-iafw-vol2of2-bios-spec-rev2p1.pdf
557556-apl-soc-eds-vol2-rev2p4.pdf
pentium-celeron-n-series-j-series-datasheet-vol-1.pdf
本文链接:https://my.lmcjl.com/post/8801.html
4 评论