编辑
2025-01-22
记录知识
0
请注意,本文编写于 156 天前,最后修改于 59 天前,其中某些信息可能已经过时。

目录

一、问题现象
二、分析
2.1 hdmi的寄存器无法访问
三、解决
3.1 正向解决,修改hdmi的驱动
3.2 规避方法,hdmi完全启动时才处理中断
3.3 为什么可以规避
四、改动

我们在使用openharmony 5.10内核的时候,开机有概率卡死,这里定位了和hdmi的irq有关系。这里给出解决办法

一、问题现象

此问题是开机时直接卡死

问题日志如下:

[ 61.871358] rcu: INFO: rcu_sched detected stalls on CPUs/tasks: [ 61.871405] rcu: 0-...0: (6 ticks this GP) idle=3ea/1/0x4000000000000000 softirq=65/67 fqs=6000 [ 61.871426] (detected by 2, t=18002 jiffies, g=-1099, q=60) [ 61.871441] Task dump for CPU 0: [ 61.871456] task:irq/46-fe0a0000 state:R running task stack: 0 pid: 157 ppid: 2 flags:0x0000002a [ 61.871485] Call trace: [ 61.871511] __switch_to+0x138/0x164 [ 61.871534] schedule+0x50/0xb0

通过上面可以发现,这里出问题的是中断46号导致了cpu0出现了rcu 超时检测,所以定位此问题肯定是hdmi的irq导致的

二、分析

根据此时的日志分析,我们可以知道,正常情况下hdmi的中断通常不可能会出现无法响应,所以我们查看中断内做的事情如下:

intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); if (intr_stat) { hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); return IRQ_WAKE_THREAD; } hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT); if (hdcp_stat) { hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK); return IRQ_WAKE_THREAD; }

这里以write举例,其实现如下:

static inline void hdmi_writeb(struct dw_hdmi *hdmi, u8 val, int offset) { regmap_write(hdmi->regm, offset << hdmi->reg_shift, val); }

这里的regmap的实际为如下:

hdmi->regm = devm_regmap_init_mmio(dev, hdmi->regs, reg_config); iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);

所以可以知道,这里直接访问的是hdmi的寄存器地址。

2.1 hdmi的寄存器无法访问

根据上面的分析,我们可以知道的结论是hdmi的寄存器访问hung住了,这通常是hdmi的phy没有良好的初始化导致的,但是我们可以知道,这是在系统启动过程,这也就意味着,此时的hdmi的phy的probe有概率存在异常,导致hdmi产生的中断信号,但是mmio的地址无法正常的读写。

说清楚点,也就是,openharmony的hdmi的bridge驱动适配的不是很好,导致hdmi概率启动失败。

关于这一点问题,我们在4.19内核上,每次开机过程中的irq都是能正常的读写mmio映射的地址,这从而佐证了openharmony的内核的hdmi驱动存在问题

三、解决

根据上面的分析,我们两种思路解决:

3.1 正向解决,修改hdmi的驱动

此方案需要重构dw_hdmi.c的所有相关代码,目前来看设计工作量较大。花费时间过多,目前本人没精力处理。

3.2 规避方法,hdmi完全启动时才处理中断

根据3.1的结论,我们如果正向解决openharmony的内核问题,花费时间较多,所以为了赶项目计划节点,规避方法也是可行的。

首先,我们知道hdmi再接收到中断的时候,openharmony的内核的mmio地址是无法访问的

其次,我们知道hdmi的结构体有变量bridge_is_on,如下定义

bool bridge_is_on; /* indicates the bridge is on */

根据上面可以知道,bridge power on的时候这个变量起来。所以我们可以在中断中防御式的编程,如果hdmi没有power on,我们不处理中断。

3.3 为什么可以规避

并不是所有情况可以规避,这里能够规避的前提条件是这里的中断是需要写清零的,如下:

hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);

这也就意味着,如果我没有处理,也就不会写清零,顶多导致我中断延迟处理了而已。

四、改动

针对此问题,我们利用hdmi本身是否bridge_is_on的标志位来过滤中断的入口函数,从而做到规避hdmi异常中断的问题,如下

diff --git a/dw-hdmi.c b/dw-hdmi.c index 1b25fdd..e4e13b0 100644 --- a/dw-hdmi.c +++ b/dw-hdmi.c @@ -3763,6 +3763,10 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) u8 intr_stat, hdcp_stat; irqreturn_t ret = IRQ_NONE; + if (!hdmi->bridge_is_on){ + return ret; + } + if (hdmi->i2c) ret = dw_hdmi_i2c_irq(hdmi); @@ -3816,6 +3820,11 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat, hdcp_stat; enum drm_connector_status status = connector_status_unknown; + if (!hdmi->bridge_is_on){ + printk("kylin: dw_hdmi not power on\n"); + return IRQ_HANDLED; + } + intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0); phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);