• 关于ZYNQ-7000中断调试一点感想


    背景

    在ZYNQ 平台下,需要对各种需要的底层接口进行初始化。

    我依次调试了很多驱动,从最简单的网口到USB;再到读写PL端的寄存器(通过AXI总线,内存映射读写物理地址实现),到中断的时候一直卡着不动。

    我调试了很久,最终调完了,回顾自己这1个月的调试,还是有很多的感想。

    里程碑

    确保PL中断正常

    我通过开发板供应商提供的文档,完成了在PS-standalone端的裸机中断验证。

    根据文档,我也获取到了对应的硬件中断号:61(之后因为PL的改动,变成了63,但是关系不大)

    同时,我编写了有关的行动记录,明确地记录了每一步做了什么,方便以后复现。

    但是,由于缺少管理,导致各种文档重复的地方很多,难以寻找。(反思:定期整理)

    加强调试速度

    由于在ZYNQ平台,事先使用了PetaLinux,并将常见的操作整理成了脚本,同时,使用脱机化的构建方式。

    这一步是在其他验证中同时做好的,但是我想,如果没有这一步,后面的调试会花费我大部分的时间。

    同时,我还专门搭建了一个虚拟机,用于直连调试。

    因为这样,我只需要按一下复位按键,传一个文件到tftp服务器的根目录,就可以完成一次调试。

    寻找Linux端驱动实现,失败

    因为Linux的特殊性,一些底层功能的配置需要依赖于Linux的框架。

    我在网上找了很多文档,大致分为3个方向:
    1、直接的驱动实现,不依赖设备树
    2、修改设备树,添加对应的结点
    3、在第二种方向的基础上,使用UIO作为实现。

    以及一些有用的信息:

    • 如果想要ZYNQ的中断能够成功使用:
      1、/proc/interrupts中需要看到对应注册的中断。
      2、/proc/device-tree/amba_pl/中需要看到对应的设备树结点(如果是使用设备树)。

    但是无论是哪个方向,我都试过了,获取不到我要的中断。

    #错误1
    
    type mismatch, failed to map hwirq-61 for /amba/interrupt-controller@f8f01000!
    
    # 错误2 
    insmod: can't insert 'plInt.ko': Invalid argument
    root@sd01-usb0-eth0:~# dmesg
    genirq: Flags mismatch irq 29. 00000002 (pl-key) vs. 00000084 (mmc1)
    can not request irq for pl-key[-16]
    

    此后,根据UIO的有关资料,继续配置,但是同样地,没有结果。

    为了不拖累进度,我跑去社区找答案,但是开发板的社区访问不了,我又去了赛灵思的社区,表达了我遇到的困境,几天后得到了答复。

    有一位专家向我提供了一个驱动模块(有编程语法错误,我还修改了一下),但因为限制于之前经验的条条框框,我还是以老的文档作为基点,对设备树进行改动,并意料之内地失败了。

    翻阅有关资料

    我又复习了之前整理的设备树资料,以及搜索了一些有关的情况。

    我根据网友的文档进行新增的设备树的属性应该是没有问题的,而且我也反复确认过好几次。

    但是我在StackOverflow上看到过关于compatible属性可能需要改变。

    我曾经怀疑过是否因为PL的配置的问题,即使很清楚standalone下已经确定PL没问题了;我还是要求同事再配一下电平。

    后面依旧没有结果以后,我就死心了。

    为了不被这个难题阻碍开发进度,于是我转而在“假设中断能够正常使用的情况下”,完成了驱动模块其他方面的设计。

    因为被这个难题压抑了太久,我在另外的设计上用力很猛,也很顺利;此后,我又开始面对这个问题。

    我开始冷静思考,重新回顾了我所看过的文档;基本上这一个月来,网上的文档我都看过了(包括wiki)。

    但是我还是觉得应该再仔细好好看看。

    根据何晔: 当ZYNQ遇到Linux Userspace I/O(UIO)文章中提到的:配置内核驱动、对设备树的修改。

    其中将pl.dtsi中的属性,在system-user.dtsi中重写。

    /include/ "system-conf.dtsi"
    /{
        usb_phy0: usb_phy@0 {
            compatible = "ulpi-phy";
            #phy-cells = <0>;
            reg = <0xe0002000 0x1000>;
            view-port = <0x0170>;
            drv-vbus;
        };
    };
    
    &usb0 {
        dr_mode = "host";
        usb-phy = <&usb_phy0>;
    };
    
    &amba_pl {
        // A. 这个改动会覆盖 pl.dtsi 中的配置
        gpio@41300000 {
            compatible = "generic-uio","uio"; // 使用UIO配置匹配中断
             interrupts = <0 32 4>; // 从 0-31-4 改成 0-32-4,避免中断号冲突
        };
        
        // B. 新增的节点
        uio@0 {
            compatible = "generic-uio";
            status = "okay";
            interrupt-controller;
           interrupt-parent=<&intc>;
         interrupts = <0 30 4>; // IRQ 31+32 Rising High
            reg = <0x0 0x41300000 0x0 0x10000>;
        };
        
        uio@1 {
            compatible = "generic-uio";
            status = "okay";
            interrupt-controller;
           interrupt-parent=<&intc>;
         interrupts = <0 31 4>; // IRQ 32+32 Rising High
            reg = <0x0 0x41300000 0x0 0x10000>;
        };
    };
    

    得到的现象:
    1、能够注册到一些中断,但是无法响应。
    2、即使是不新增B的节点,使用普通的中断驱动也可以注册到中断了,但是按键都要被我按烂了,可中断死活没有反应。

    于是我继续思考,应该就是设备树和中断驱动之间的问题,中断号已经正确了,剩下来的事情可能就是在驱动这里了。

    回顾赛灵思社区的答复

    我根据专家的答案,尝试性地按我自己的理解配置了一下。

    设备树里只需要把axi_gpio node里的compatible替换为这个module文件里的compatible名字。这个module代码自动申请中断资源。

    首先,我根据从UIO需要修改设备树的方法,重写了gpio中断的compatible属性。

     cat dts_kernel/system-user.dtsi 
    /include/ "system-conf.dtsi"
    /{
        // USB 支持
        usb_phy0: usb_phy@0 {
            compatible = "ulpi-phy";
            #phy-cells = <0>;
            reg = <0xe0002000 0x1000>;
            view-port = <0x0170>;
            drv-vbus;
        };
        // 重新设置的驱动属性
    	amba_pl: amba_pl {
    		axi_gpio_1: gpio@41300000 {
    			compatible = "vendor,gpioirq";
            };
                
        };
    
    };
    // USB 支持
    &usb0 {
        dr_mode = "host";
        usb-phy = <&usb_phy0>;
    };
    

    结合之前那位专家提供的驱动模块:

    /*  gpioirq.c - The simplest kernel module.
    */
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/io.h>
    #include <linux/interrupt.h>
    
    #include <linux/of_address.h>
    #include <linux/of_device.h>
    #include <linux/of_platform.h>
    
    /* Standard module information, edit as appropriate */
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR
    ("Xilinx Inc.");
    MODULE_DESCRIPTION
    ("gpioirq - loadable module template generated by petalinux-create -t modules");
    
    #define DRIVER_NAME "gpioirq"
    
    #define PRINTK_LV "<4>"
    
    #define DATA_0_RO_OFFSET		0x0
    #define XGPIO_INTSTS_OFFSET		0x120
    #define XGPIO_TRI_OFFSET		0x4
    #define XGPIO_GIER_OFFSET		0x11C
    #define XGPIO_IER_OFFSET	0x128
    
    /* Simple example of how to receive command line parameters to your module.
       Delete if you don't need them */
    
    struct gpioirq_local {
        int irq;
        unsigned long mem_start;
        unsigned long mem_end;
        void __iomem *base_addr;
    };
    
    static irqreturn_t gpioirq_irq(int irq, void *p)
    {
        struct gpioirq_local * lp = (struct gpioirq_local *)p;
        unsigned int data;
    
        printk(PRINTK_LV "gpioirq interrupt
    ");
    
        /* 
         * Check gpio Value
         */
        data = ioread32(lp->base_addr + DATA_0_RO_OFFSET);
        data = data;
        printk(PRINTK_LV "axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x%08X
    ",data);
    
        /* 
         * Clear Interrupt
         */
        data = ioread32(lp->base_addr + XGPIO_INTSTS_OFFSET);
        iowrite32(data,
                   lp->base_addr + XGPIO_INTSTS_OFFSET);
    
        return IRQ_HANDLED;
    }
    
    static int gpioirq_probe(struct platform_device *pdev)
    {
        struct resource *r_irq; /* Interrupt resources */
        struct resource *r_mem; /* IO mem resources */
        struct device *dev = &pdev->dev;
        struct gpioirq_local *lp = NULL;
        unsigned int data;
    
        int rc = 0;
    
        printk(PRINTK_LV  "Device Tree Probing
    ");
    
    
        /* Get iospace for the device */
        r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!r_mem) {
            dev_err(dev, "invalid address
    ");
            return -ENODEV;
        }
    
        lp = (struct gpioirq_local *) kmalloc(sizeof(struct gpioirq_local), GFP_KERNEL);
        if (!lp) {
            dev_err(dev, "Cound not allocate gpioirq device
    ");
            return -ENOMEM;
        }
    
        dev_set_drvdata(dev, lp);
    
        lp->mem_start = r_mem->start;
        lp->mem_end = r_mem->end;
    
        if (!request_mem_region(lp->mem_start,
                                lp->mem_end - lp->mem_start + 1,
                                DRIVER_NAME)) {
            dev_err(dev, "Couldn't lock memory region at %p
    ",
                    (void *)lp->mem_start);
            rc = -EBUSY;
            goto error1;
        }
    
        lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
        if (!lp->base_addr) {
            dev_err(dev, "gpioirq: Could not allocate iomem
    ");
            rc = -EIO;
            goto error2;
        }
    
        /* Get IRQ for the device */
        r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!r_irq) {
            printk(PRINTK_LV  "no IRQ found
    ");
            printk(PRINTK_LV  "gpioirq at 0x%08x mapped to 0x%08x
    ",
                     (unsigned int __force)lp->mem_start,
                     (unsigned int __force)lp->base_addr);
            return 0;
        }
        lp->irq = r_irq->start;
    
        rc = request_irq(lp->irq, gpioirq_irq, 0, DRIVER_NAME, lp);
        if (rc) {
            dev_err(dev, "testmodule: Could not allocate interrupt %d.
    ",
                    lp->irq);
            goto error3;
        }
    
        printk(PRINTK_LV "gpioirq at 0x%08x mapped to 0x%08x, irq=%d
    ",
                 (unsigned int __force)lp->mem_start,
                 (unsigned int __force)lp->base_addr,
                 lp->irq);
    
    
        /* 
         * Set gpio direction
         */
        iowrite32(0xFFFFFFFF,
                  lp->base_addr + XGPIO_TRI_OFFSET);
        data = ioread32(lp->base_addr + XGPIO_TRI_OFFSET);
        printk("axigpioirq_init: gpio channael 0 directions 0x%08X
    ",data);
    
        /* 
         * Enable gpio irq
         */
        iowrite32(0x1,
                  lp->base_addr + XGPIO_IER_OFFSET);
        data = ioread32(lp->base_addr + XGPIO_IER_OFFSET);
        printk("axigpioirq_init: IER status 0x%08X
    ",data);		
        iowrite32(0x80000000,
                  lp->base_addr + XGPIO_GIER_OFFSET);		
        data = ioread32(lp->base_addr + XGPIO_GIER_OFFSET);
        printk("axigpioirq_init: GIER status 0x%08X
    ",data);
    
    
        /* 
         * Set gpio irq type&polarity
         */
        data = ioread32(lp->base_addr + XGPIO_INTSTS_OFFSET);
        printk("axigpioirq_init: irq status 0x%08X
    ",data);
    
    
        return 0;
    error3:
        free_irq(lp->irq, lp);
    error2:
        release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    error1:
        kfree(lp);
        dev_set_drvdata(dev, NULL);
        return rc;
    }
    
    static int gpioirq_remove(struct platform_device *pdev)
    {
        struct device *dev = &pdev->dev;
        struct gpioirq_local *lp = dev_get_drvdata(dev);
        free_irq(lp->irq, lp);
        release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
        kfree(lp);
        dev_set_drvdata(dev, NULL);
        return 0;
    }
    
    #ifdef CONFIG_OF
    static struct of_device_id gpioirq_of_match[] = {
    /* 这个流程我不熟悉。你可以用附件里的kernel module来试一下。
     * 设备树里只需要把axi_gpio node里的compatible替换为这个module文件里的compatible名字。
     * 这个module代码自动申请中断资源。在zcu102板子上测试过的,zynq7000上还没测试过。
     * https://forums.xilinx.com/t5/%E5%B5%8C%E5%85%A5%E5%BC%8F-%E7%A1%AC%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91/%E5%85%B3%E4%BA%8EZYNQ-PL%E4%B8%AD%E6%96%AD%E6%97%B6PS%E7%AB%AF%E6%97%A0%E6%B3%95%E5%93%8D%E5%BA%94%E7%9A%84%E9%97%AE%E9%A2%98/m-p/1135119#M4672
     */
        { .compatible = "vendor,gpioirq", },
        { /* end of list */ },
    };
    MODULE_DEVICE_TABLE(of, gpioirq_of_match);
    #else
    # define gpioirq_of_match
    #endif
    
    
    static struct platform_driver gpioirq_driver = {
        .driver = {
            .name = DRIVER_NAME,
            .owner = THIS_MODULE,
            .of_match_table	= gpioirq_of_match,
        },
        .probe		= gpioirq_probe,
        .remove		= gpioirq_remove,
    };
    
    static int __init gpioirq_init(void)
    {
        printk("<1>Hello module world.
    ");
    
        return platform_driver_register(&gpioirq_driver);
    }
    
    
    static void __exit gpioirq_exit(void)
    {
        platform_driver_unregister(&gpioirq_driver);
        printk(KERN_ALERT "Goodbye module world.
    ");
    }
    
    module_init(gpioirq_init);
    module_exit(gpioirq_exit);
    

    结果马上就可以了。

    以下是log

    root@20200827-2s-to-irq-cpu0:/mnt# insmod my_gpio.ko 
    my_gpio: loading out-of-tree module taints kernel.
    <1>Hello module world.
    <4>Device Tree Probing
    <4>gpioirq at 0x41300000 mapped to 0xf0a30000, irq=48
    axigpioirq_init: gpio channael 0 directions 0xFFFFFFFF
    axigpioirq_init: IER status 0x00000001
    axigpioirq_init: GIER status 0x80000000
    axigpioirq_init: irq status 0x00000000
    
    root@20200827-2s-to-irq-cpu0:/mnt# cat /proc/interrupts
               CPU0       CPU1       
     16:          0          0     GIC-0  27 Edge      gt
     17:          0          0     GIC-0  43 Level     ttc_clockevent
     18:      23185      24246     GIC-0  29 Edge      twd
     19:          0          0     GIC-0  37 Level     arm-pmu
     20:          0          0     GIC-0  38 Level     arm-pmu
     21:         43          0     GIC-0  39 Level     f8007100.adc
     23:          0          0     GIC-0  57 Level     cdns-i2c
     25:          0          0     GIC-0  35 Level     f800c000.ocmc
     26:        103          0     GIC-0  82 Level     xuartps
     27:         10          0     GIC-0  51 Level     e000d000.spi
     28:      34766          0     GIC-0  54 Level     eth0
     29:        254          0     GIC-0  56 Level     mmc0
     30:        613          0     GIC-0  79 Level     mmc1
     31:          0          0     GIC-0  45 Level     f8003000.dmac
     32:          0          0     GIC-0  46 Level     f8003000.dmac
     33:          0          0     GIC-0  47 Level     f8003000.dmac
     34:          0          0     GIC-0  48 Level     f8003000.dmac
     35:          0          0     GIC-0  49 Level     f8003000.dmac
     36:          0          0     GIC-0  72 Level     f8003000.dmac
     37:          0          0     GIC-0  73 Level     f8003000.dmac
     38:          0          0     GIC-0  74 Level     f8003000.dmac
     39:          0          0     GIC-0  75 Level     f8003000.dmac
     40:          0          0     GIC-0  40 Level     f8007000.devcfg
     46:         30          0     GIC-0  53 Level     e0002000.usb
     47:          0          0     GIC-0  41 Edge      f8005000.watchdog
     48:         34          0     GIC-0  63 Level     gpioirq
    IPI1:          0          0  Timer broadcast interrupts
    IPI2:       2339      11847  Rescheduling interrupts
    IPI3:          4          0  Function call interrupts
    IPI4:          0          0  CPU stop interrupts
    IPI5:         32          0  IRQ work interrupts
    IPI6:          0          0  completion interrupts
    Err:          0
    root@20200827-2s-to-irq-cpu0:/mnt# ls /proc/device-tree/amba
    amba/    amba_pl/ 
    root@20200827-2s-to-irq-cpu0:/mnt# ls /proc/device-tree/amba_pl/
    #address-cells     #size-cells        compatible         gpio@41200000      gpio@41300000      name               ranges             xadc_wiz@41400000
    root@20200827-2s-to-irq-cpu0:/mnt# <4>gpioirq interrupt
    <4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000002
    <4>gpioirq interrupt
    <4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000000
    <4>gpioirq interrupt
    <4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000002
    <4>gpioirq interrupt
    <4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000000
    

    经验教训

    过于依赖网上的文档

    虽然说网上的文档在这一块的资料不够充足(而且经常有省略),但是因为过于依赖文档的内容导致了进度缓慢。

    不得不说的是,赛灵思的文档真的是有点零散啊。

    基础知识不够扎实

    设备树这一块的知识我在年初就进行了整理,但是这次调试的时候才发现掌握得很差。

    驱动框架的内容也是我的一个短板,平时没有关注到这个领域,学习进度也比较慢。

    做得好的

    不拖延进度

    正如读书那会,老师会告诉我们,不懂的题目先跳过。

    我也是在区分好功能单元的情况下,先假设中断能够使用,再开发功能,冷静以后再分析实际问题。

    此外,之前搭建的快速验证的环境也是帮了我很大的忙。构建好项目以后,我就没有在PetaLinux的环境下进行构建,节省了大量的时间。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    SpringBoot获取配置文件,就这么简单。
    IDEA 插件推荐 —— 让你写出好代码的神器!
    太高效了!玩了这么久的Linux,居然不知道这7个终端快捷键!
    万字长文!一次性弄懂 Nginx 处理 HTTP 请求的 11 个阶段
    一个排查了大半天儿的问题,差点又让 MyBatis 背锅
    使用Flutter开发的抖音国际版
    一文回顾Redis五大对象(数据类型)
    Gradle系列之初识Gradle
    OpenJFX DJ 风格 Java 桌面音乐播放器
    【高并发】高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!
  • 原文地址:https://www.cnblogs.com/schips/p/a_little_lament_when_debug_irq_in_zynq_linux.html
Copyright © 2020-2023  润新知