【随笔记】全志 T507 PF4 引脚无法被正常设置为中断模式的问题分析

相关信息

硬件平台:全志T507
系统版本:Android 10 / Linux 4.9.170
问题描述:PF4 无法通过标准接口设置为中断模式,而 PF1、PF2、PF3、PF5 正常可用。

分析过程

一开始以为是引脚被其它驱动占用引起,或者该引脚不具备中断功能,经过排查,已排除这两种可能,因此通过从源码分析来找问题的根因。

以下是以 gpio_keys.c 驱动为入口进行分析:

// drivers/input/keyboard/gpio_keys.c
static int gpio_keys_setup_key(struct platform_device *pdev,
				struct input_dev *input,
				struct gpio_button_data *bdata,
				const struct gpio_keys_button *button)
{
	......
	error = devm_request_any_context_irq(&pdev->dev, bdata->irq,
					     isr, irqflags, desc, bdata);
}

// kernel/irq/devres.c
int devm_request_any_context_irq(struct device dev, unsigned int irq,
irq_handler_t handler, unsigned long irqflags,
const char devname, void *dev_id)
{
......
rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id);
if (rc < 0) {
devres_free(dr);
return rc;
}
......
return rc;
}

// kernel/irq/manage.c
int request_any_context_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char name, void dev_id)
{
......
ret = request_irq(irq, handler, flags, name, dev_id);
return !ret ? IRQC_IS_HARDIRQ : ret;
}

// include/linux/interrupt.h
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char name, void dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

// kernel/irq/manage.c
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char devname, void dev_id)
{
......
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(desc);
......
return retval;
}

// kernel/irq/manage.c
static int __setup_irq(unsigned int irq, struct irq_desc desc, struct irqaction new)
{
......

<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>shared<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	ret <span class="token operator">=</span> <span class="token function">irq_request_resources</span><span class="token punctuation">(</span>desc<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
		<span class="token function">pr_err</span><span class="token punctuation">(</span><span class="token string">"Failed to request resources for %s (irq %d) on irqchip %s\n"</span><span class="token punctuation">,</span>
		       new<span class="token operator">-&gt;</span>name<span class="token punctuation">,</span> irq<span class="token punctuation">,</span> desc<span class="token operator">-&gt;</span>irq_data<span class="token punctuation">.</span>chip<span class="token operator">-&gt;</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">goto</span> out_mask<span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span> 
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

}

// kernel/irq/manage.c
static int irq_request_resources(struct irq_desc desc)
{
struct irq_data d = &desc->irq_data;
struct irq_chip *c = d->chip;

<span class="token keyword">return</span> c<span class="token operator">-&gt;</span>irq_request_resources <span class="token operator">?</span> c<span class="token operator">-&gt;</span><span class="token function">irq_request_resources</span><span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static struct irq_chip sunxi_pinctrl_edge_irq_chip = {
.name = “sunxi_pio_edge”,
.irq_ack = sunxi_pinctrl_irq_ack,
.irq_mask = sunxi_pinctrl_irq_mask,
.irq_unmask = sunxi_pinctrl_irq_unmask,
.irq_request_resources = sunxi_pinctrl_irq_request_resources,
.irq_release_resources = sunxi_pinctrl_irq_release_resources,
.irq_set_type = sunxi_pinctrl_irq_set_type,
.irq_set_wake = sunxi_pinctrl_irq_set_wake,
};

// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static int sunxi_pinctrl_irq_request_resources(struct irq_data d)
{
struct sunxi_pinctrl pctl = irq_data_get_irq_chip_data(d);
struct sunxi_desc_function *func;

func <span class="token operator">=</span> <span class="token function">sunxi_pinctrl_desc_find_function_by_pin</span><span class="token punctuation">(</span>pctl<span class="token punctuation">,</span>
				pctl<span class="token operator">-&gt;</span>irq_array<span class="token punctuation">[</span>d<span class="token operator">-&gt;</span>hwirq<span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"irq"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>func<span class="token punctuation">)</span>
	<span class="token keyword">return</span> <span class="token operator">-</span>EINVAL<span class="token punctuation">;</span>

<span class="token comment">/* Change muxing to INT mode */</span>
<span class="token function">printk</span><span class="token punctuation">(</span>KERN_EMERG<span class="token string">"[lmx] irq:%d set int mode pin:%d d-&gt;hwirq:%ld func-&gt;muxval:%d\n"</span><span class="token punctuation">,</span> d<span class="token operator">-&gt;</span>irq<span class="token punctuation">,</span> pctl<span class="token operator">-&gt;</span>irq_array<span class="token punctuation">[</span>d<span class="token operator">-&gt;</span>hwirq<span class="token punctuation">]</span><span class="token punctuation">,</span> d<span class="token operator">-&gt;</span>hwirq<span class="token punctuation">,</span> func<span class="token operator">-&gt;</span>muxval<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">sunxi_pmx_set</span><span class="token punctuation">(</span>pctl<span class="token operator">-&gt;</span>pctl_dev<span class="token punctuation">,</span> pctl<span class="token operator">-&gt;</span>irq_array<span class="token punctuation">[</span>d<span class="token operator">-&gt;</span>hwirq<span class="token punctuation">]</span><span class="token punctuation">,</span> func<span class="token operator">-&gt;</span>muxval<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static void sunxi_pmx_set(struct pinctrl_dev pctldev,
unsigned pin,
u8 config)
{
struct sunxi_pinctrl pctl = pinctrl_dev_get_drvdata(pctldev);
unsigned long flags;
u32 val, mask;

<span class="token function">raw_spin_lock_irqsave</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>pctl<span class="token operator">-&gt;</span>lock<span class="token punctuation">,</span> flags<span class="token punctuation">)</span><span class="token punctuation">;</span>
pin <span class="token operator">-=</span> pctl<span class="token operator">-&gt;</span>desc<span class="token operator">-&gt;</span>pin_base<span class="token punctuation">;</span>
val <span class="token operator">=</span> <span class="token function">readl</span><span class="token punctuation">(</span>pctl<span class="token operator">-&gt;</span>membase <span class="token operator">+</span> <span class="token function">sunxi_mux_reg</span><span class="token punctuation">(</span>pin<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mask <span class="token operator">=</span> MUX_PINS_MASK <span class="token operator">&lt;&lt;</span> <span class="token function">sunxi_mux_offset</span><span class="token punctuation">(</span>pin<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">writel</span><span class="token punctuation">(</span><span class="token punctuation">(</span>val <span class="token operator">&amp;</span> <span class="token operator">~</span>mask<span class="token punctuation">)</span> <span class="token operator">|</span> config <span class="token operator">&lt;&lt;</span> <span class="token function">sunxi_mux_offset</span><span class="token punctuation">(</span>pin<span class="token punctuation">)</span><span class="token punctuation">,</span>
	pctl<span class="token operator">-&gt;</span>membase <span class="token operator">+</span> <span class="token function">sunxi_mux_reg</span><span class="token punctuation">(</span>pin<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">raw_spin_unlock_irqrestore</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>pctl<span class="token operator">-&gt;</span>lock<span class="token punctuation">,</span> flags<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

无论有多复杂的代码,最终都需要通过读写寄存器的方式来实现控制芯片,而通过上述代码分析,即可发现 sunxi_pmx_set() 接口用于配置寄存器,是最底层的接口,可以通过打印输出传入的参数,来检查是否有问题。

PF3 打印输出为:

[   10.683205] [lmx] irq:148 set int mode pin:163 d->hwirq:131 func->muxval:6

 
 
  • 1

PF4 打印输出为:

[   10.683557] [lmx] irq:149 set int mode pin:196 d->hwirq:132 func->muxval:6

 
 
  • 1

这里就能看出很奇怪的地方,PF3 的引脚编号是 163,而 PF4 却是 196,跨度很大。

通过以下指令查询 PF4 的正确引脚编号,也可以得知 196 引脚编号是哪一组:

mercury-demo:/ # cat /sys/kernel/debug/pinctrl/pio/pins
registered pins: 137
......
pin 160 (PF0)
pin 161 (PF1)
pin 162 (PF2)
pin 163 (PF3)
pin 164 (PF4)
pin 165 (PF5)
pin 166 (PF6)
......
pin 196 (PG4)
pin 197 (PG5)
......

确认 PF4 正确引脚编号是 164,而 196 对应是 PG4,实际生效的是 PG4,通过以下指令即可确认:

mercury-demo:/sys/kernel/debug/sunxi_pinctrl # echo PG4 > sunxi_pin
mercury-demo:/sys/kernel/debug/sunxi_pinctrl # cat *
pin[PG4] data: 1
pio
pin[PG4] dlevel: 1
pin[PG4] funciton: 6
NOMATCH
pin[PG4] pull: 1
PG4
pin[PG4] funciton: 6
pin[PG4] data: 1
pin[PG4] dlevel: 1
pin[PG4] pull: 1

根据代码确定引脚编号来源于 pctl->irq_array 数组,通过 pctl->irq_array 赋值的地方进行打印输出,是否一开始就出错了:

// drivers/pinctrl/sunxi/pinctrl-sunxi.c
static int sunxi_pinctrl_build_state(struct platform_device *pdev)
{
	......
	/* Count functions associated groups */
	for (i = 0; i < pctl->desc->npins; i++) {
		const struct sunxi_desc_pin *pin = pctl->desc->pins + i;
		struct sunxi_desc_function *func = pin->functions;
		while (func->name) {
			/* Create interrupt mapping while we're at it */
			if (!strcmp(func->name, "irq")) {
				int irqnum = func->irqnum + func->irqbank * IRQ_PER_BANK;
				pctl->irq_array[irqnum] = pin->pin.number;
				printk(KERN_EMERG"[lmx] pctl->irq_array[%d] = %d   (func->irqnum:%d func->irqbank:%d)\n", irqnum, pin->pin.number, func->irqnum, func->irqbank);
			}
			sunxi_pinctrl_add_function(pctl, func->name);
			func++;
		}
	}
	......
	return 0;
}
// drivers/pinctrl/sunxi/pinctrl-sunxi.h
#define IRQ_PER_BANK		32

在这里插入图片描述
可以发现,PF4(164)对应的索引是 132,原本被正确赋值为 164,但又被覆盖为 PG4(196)。
不难发现,出现覆盖的原因是因为 PG4 的 func->irqbank 数值错误(4),导致索引下标计算错误。

根据前后文来看,func->irqbank 的正确数值应该是 5,代入计算得到正确的值 164:
int irqnum(164) = func->irqnum(4) + func->irqbank(5) * IRQ_PER_BANK(32);

大概率硬件资源描述配置出错,通过搜索 irqbank 被赋值的方法,来定位描述配置出错的地方:

// drivers/pinctrl/sunxi/pinctrl-sunxi.h
#define SUNXI_FUNCTION_IRQ_BANK(_val, _bank, _irq)		\
	{							\
		.name = "irq",					\
		.muxval = _val,					\
		.irqbank = _bank,				\
		.irqnum = _irq,					\
	}

使用的是 SUNXI_FUNCTION_IRQ_BANK 宏,重点检查第二个参数:

//	drivers/pinctrl/sunxi/pinctrl-sun50iw9p1.c
static const struct sunxi_desc_pin sun50iw9p1_pins[] = {
	......
	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
		SUNXI_FUNCTION(0x0, "gpio_in"),
		SUNXI_FUNCTION(0x1, "gpio_out"),
		SUNXI_FUNCTION(0x2, "sdc1"),		/* D1 */
		SUNXI_FUNCTION_IRQ_BANK(0x6, 5, 3),  /*  PG_EINT3	*/
		SUNXI_FUNCTION(0x7, "io_disabled")),
	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
		SUNXI_FUNCTION(0x0, "gpio_in"),
		SUNXI_FUNCTION(0x1, "gpio_out"),
		SUNXI_FUNCTION(0x2, "sdc1"),		/* D2 */
		// 可以发现第二个参数恰好是 4,根据分析结果,以及结合上下文,正确的应该是 5
		SUNXI_FUNCTION_IRQ_BANK(0x6, 4, 4),  /*  PG_EINT4	*/
		SUNXI_FUNCTION(0x7, "io_disabled")),
	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
		SUNXI_FUNCTION(0x0, "gpio_in"),
		SUNXI_FUNCTION(0x1, "gpio_out"),
		SUNXI_FUNCTION(0x2, "sdc1"),		/* D3 */
		SUNXI_FUNCTION_IRQ_BANK(0x6, 5, 5),  /*  PG_EINT5	*/
		SUNXI_FUNCTION(0x7, "io_disabled")),
	......
};

修改之后的 pctl->irq_array 打印输出正确:
在这里插入图片描述

进行实测,PF4 已经可以正常的被设置为中断模式。

问题总结

全志原厂提供的 SoCs pinctrl driver 中的 PG4 中断信息描述错误,导致覆盖了 PF4 的引脚编号,因此只要修正 PG4 的描述信息,即可解决问题。

这个问题不仅仅会影响 PF4 无法使用,也会影响 PG4 引脚无法使用,从代码来看,想要设置为 PG4 为中断模式,实际修改的会 PA0(0)。

--- a/longan/kernel/linux-4.9/drivers/pinctrl/sunxi/pinctrl-sun50iw9p1.c
+++ b/longan/kernel/linux-4.9/drivers/pinctrl/sunxi/pinctrl-sun50iw9p1.c
@@ -693,7 +693,7 @@
                SUNXI_FUNCTION(0x0, "gpio_in"),
                SUNXI_FUNCTION(0x1, "gpio_out"),
                SUNXI_FUNCTION(0x2, "sdc1"),            /* D2 */
-               SUNXI_FUNCTION_IRQ_BANK(0x6, 4, 4),  /*  PG_EINT4       */
+               SUNXI_FUNCTION_IRQ_BANK(0x6, 5, 4),  /*  PG_EINT4       */
                SUNXI_FUNCTION(0x7, "io_disabled")),
        SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
                SUNXI_FUNCTION(0x0, "gpio_in"),
            </div><div data-report-view="{&quot;mod&quot;:&quot;1585297308_001&quot;,&quot;spm&quot;:&quot;1001.2101.3001.6548&quot;,&quot;dest&quot;:&quot;https://blog.csdn.net/lovemengx/article/details/130782060&quot;,&quot;extend1&quot;:&quot;pc&quot;,&quot;ab&quot;:&quot;new&quot;}"><div></div></div>
            <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-98b95bb57c.css" rel="stylesheet">
            <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-c216769e99.css" rel="stylesheet">
    </div>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值