打印
[技术讨论]

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

[复制链接]
544|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
神棍地海棠|  楼主 | 2023-11-24 14:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
相关信息
硬件平台:全志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:6
PF4 打印输出为:
[   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"),


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

284

主题

292

帖子

1

粉丝