相关信息 硬件平台:全志T507
系统版本:Android 10 / Linux 4.9.170
问题描述:PF4 无法通过标准接口设置为中断模式,而 PF1、PF2、PF3、PF5 正常可用。 分析过程一开始以为是引脚被其它驱动占用引起,或者该引脚不具备中断功能,经过排查,已排除这两种可能,因此通过从源码分析来找问题的根因。 以下是以 gpio_keys.c 驱动为入口进行分析: // drivers/input/keyboard/gpio_keys.cstatic 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.cint 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.cint 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.hstatic inline int __must_checkrequest_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.cint 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.cstatic int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new){ ...... if (!shared) { ret = irq_request_resources(desc); if (ret) { pr_err("Failed to request resources for %s (irq %d) on irqchip %s\n", new->name, irq, desc->irq_data.chip->name); goto out_mask; } ...... } ......}// kernel/irq/manage.cstatic int irq_request_resources(struct irq_desc *desc){ struct irq_data *d = &desc->irq_data; struct irq_chip *c = d->chip; return c->irq_request_resources ? c->irq_request_resources(d) : 0;}// drivers/pinctrl/sunxi/pinctrl-sunxi.cstatic 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.cstatic 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 = sunxi_pinctrl_desc_find_function_by_pin(pctl, pctl->irq_array[d->hwirq], "irq"); if (!func) return -EINVAL; /* Change muxing to INT mode */ printk(KERN_EMERG"[lmx] irq:%d set int mode pin:%d d->hwirq:%ld func->muxval:%d\n", d->irq, pctl->irq_array[d->hwirq], d->hwirq, func->muxval); sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval); return 0;}// drivers/pinctrl/sunxi/pinctrl-sunxi.cstatic 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; raw_spin_lock_irqsave(&pctl->lock, flags); pin -= pctl->desc->pin_base; val = readl(pctl->membase + sunxi_mux_reg(pin)); mask = MUX_PINS_MASK << sunxi_mux_offset(pin); writel((val & ~mask) | config << sunxi_mux_offset(pin), pctl->membase + sunxi_mux_reg(pin)); raw_spin_unlock_irqrestore(&pctl->lock, flags);}无论有多复杂的代码,最终都需要通过读写寄存器的方式来实现控制芯片,而通过上述代码分析,即可发现 sunxi_pmx_set() 接口用于配置寄存器,是最底层的接口,可以通过打印输出传入的参数,来检查是否有问题。 PF3 打印输出为: [ 10.683205] [lmx] irq:148 set int mode pin:163 d->hwirq:131 func->muxval:6PF4 打印输出为: [ 10.683557] [lmx] irq:149 set int mode pin:196 d->hwirq:132 func->muxval:6这里就能看出很奇怪的地方,PF3 的引脚编号是 163,而 PF4 却是 196,跨度很大。 通过以下指令查询 PF4 的正确引脚编号,也可以得知 196 引脚编号是哪一组: mercury-demo:/ # cat /sys/kernel/debug/pinctrl/pio/pinsregistered 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_pinmercury-demo:/sys/kernel/debug/sunxi_pinctrl # cat *pin[PG4] data: 1piopin[PG4] dlevel: 1pin[PG4] funciton: 6NOMATCHpin[PG4] pull: 1PG4pin[PG4] funciton: 6pin[PG4] data: 1pin[PG4] dlevel: 1pin[PG4] pull: 1根据代码确定引脚编号来源于 pctl->irq_array 数组,通过 pctl->irq_array 赋值的地方进行打印输出,是否一开始就出错了: // drivers/pinctrl/sunxi/pinctrl-sunxi.cstatic 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.cstatic 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"),
|