• Zynq-Linux移植学习笔记之27UIO机制响应外部中断实现【转】


    转自:https://blog.csdn.net/zhaoxinfan/article/details/80285150

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jj12345jj198999/article/details/80285150
    1、  背景介绍
    最近项目中使用了盛科的交换芯片8086,该交换芯片除了使用PCIE连接到zynq外,还提供了四根GPIO引脚连入zynq。盛科技术人员的说法是该芯片支持GPIO管脚中断和PCIE MSI中断,使用过程中二选一即可。目前PCIE MSI中断已经解决,需要调试GPIO管脚中断方式,ZYNQ连接示意图如下。

    如上图所示,四根线之间连入一个concat,再加上PCIE的引脚,组成一个向量连入zynq的IRQ管脚。Zynq中启用了PL-PS的中断,分配的中断号为61-65.

    2、  UIO机制引入
    通常来说,zynq上挂接的中断都需要与一个控制器或IP核相对应,比如i2c,网络等,可以在devicetree中看到中断号,如下图

    该中断号与UG585中中断描述的章节相一致,下表中的IRQ ID为对应设备的中断号+32的值(0x19+32=57,正好是i2c0的IRQ ID)

    对于这种四根线直接接入的,devicetree中没有对应的设备,导致在操作系统中看不到中断。幸运的是Linux内核中提供了UIO机制,详细介绍见:https://01.org/linuxgraphics/gfx-docs/drm/driver-api/uio-howto.html

    对我而言,UIO就是处理没有具体设备只有引脚的一种机制。

    3、  devicetree设置
    利用UIO,可以在devicetree中为四根GPIO线设置对应的设备,如下图所示。

    四根线对应的中断号为0x1e-0x21,正好是62-65号中断。同时,需要修改devicetree启动项。

    让操作系统在加载过程中执行uio驱动程序。

     devicetree.dts全部代码如下:

    /dts-v1/;

    / {
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    compatible = "xlnx,zynq-7000";

    cpus {
    #address-cells = <0x1>;
    #size-cells = <0x0>;

    cpu@0 {
    compatible = "arm,cortex-a9";
    device_type = "cpu";
    reg = <0x0>;
    clocks = <0x1 0x3>;
    clock-latency = <0x3e8>;
    cpu0-supply = <0x2>;
    operating-points = <0xa4cb8 0xf4240 0x5265c 0xf4240>;
    };

    cpu@1 {
    compatible = "arm,cortex-a9";
    device_type = "cpu";
    reg = <0x1>;
    clocks = <0x1 0x3>;
    };
    };

    fpga-full {
    compatible = "fpga-region";
    fpga-mgr = <0x3>;
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    ranges;
    };

    pmu@f8891000 {
    compatible = "arm,cortex-a9-pmu";
    interrupts = <0x0 0x5 0x4 0x0 0x6 0x4>;
    interrupt-parent = <0x4>;
    reg = <0xf8891000 0x1000 0xf8893000 0x1000>;
    };

    fixedregulator {
    compatible = "regulator-fixed";
    regulator-name = "VCCPINT";
    regulator-min-microvolt = <0xf4240>;
    regulator-max-microvolt = <0xf4240>;
    regulator-boot-on;
    regulator-always-on;
    linux,phandle = <0x2>;
    phandle = <0x2>;
    };

    amba {
    u-boot,dm-pre-reloc;
    compatible = "simple-bus";
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    interrupt-parent = <0x4>;
    ranges;

    adc@f8007100 {
    compatible = "xlnx,zynq-xadc-1.00.a";
    reg = <0xf8007100 0x20>;
    interrupts = <0x0 0x7 0x4>;
    interrupt-parent = <0x4>;
    clocks = <0x1 0xc>;
    };

    can@e0008000 {
    compatible = "xlnx,zynq-can-1.0";
    status = "disabled";
    clocks = <0x1 0x13 0x1 0x24>;
    clock-names = "can_clk", "pclk";
    reg = <0xe0008000 0x1000>;
    interrupts = <0x0 0x1c 0x4>;
    interrupt-parent = <0x4>;
    tx-fifo-depth = <0x40>;
    rx-fifo-depth = <0x40>;
    };

    can@e0009000 {
    compatible = "xlnx,zynq-can-1.0";
    status = "disabled";
    clocks = <0x1 0x14 0x1 0x25>;
    clock-names = "can_clk", "pclk";
    reg = <0xe0009000 0x1000>;
    interrupts = <0x0 0x33 0x4>;
    interrupt-parent = <0x4>;
    tx-fifo-depth = <0x40>;
    rx-fifo-depth = <0x40>;
    };

    gpio@e000a000 {
    compatible = "xlnx,zynq-gpio-1.0";
    #gpio-cells = <0x2>;
    clocks = <0x1 0x2a>;
    gpio-controller;
    interrupt-controller;
    #interrupt-cells = <0x2>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x14 0x4>;
    reg = <0xe000a000 0x1000>;
    };

    i2c@e0004000 {
    compatible = "cdns,i2c-r1p10";
    status = "okay";
    clocks = <0x1 0x26>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x19 0x4>;
    reg = <0xe0004000 0x1000>;
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    clock-frequency = <0x61a80>;
    };

    i2c@e0005000 {
    compatible = "cdns,i2c-r1p10";
    status = "okay";
    clocks = <0x1 0x27>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x30 0x4>;
    reg = <0xe0005000 0x1000>;
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    clock-frequency = <0x61a80>;
    };

    interrupt-controller@f8f01000 {
    compatible = "arm,cortex-a9-gic";
    #interrupt-cells = <0x3>;
    interrupt-controller;
    reg = <0xf8f01000 0x1000 0xf8f00100 0x100>;
    num_cpus = <0x2>;
    num_interrupts = <0x60>;
    linux,phandle = <0x4>;
    phandle = <0x4>;
    };

    cache-controller@f8f02000 {
    compatible = "arm,pl310-cache";
    reg = <0xf8f02000 0x1000>;
    interrupts = <0x0 0x2 0x4>;
    arm,data-latency = <0x3 0x2 0x2>;
    arm,tag-latency = <0x2 0x2 0x2>;
    cache-unified;
    cache-level = <0x2>;
    };

    memory-controller@f8006000 {
    compatible = "xlnx,zynq-ddrc-a05";
    reg = <0xf8006000 0x1000>;
    };

    ocmc@f800c000 {
    compatible = "xlnx,zynq-ocmc-1.0";
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x3 0x4>;
    reg = <0xf800c000 0x1000>;
    };

    serial@e0000000 {
    compatible = "xlnx,xuartps", "cdns,uart-r1p8";
    status = "okay";
    clocks = <0x1 0x17 0x1 0x28>;
    clock-names = "uart_clk", "pclk";
    reg = <0xe0000000 0x1000>;
    interrupts = <0x0 0x1b 0x4>;
    device_type = "serial";
    port-number = <0x0>;
    };

    serial@e0001000 {
    compatible = "xlnx,xuartps", "cdns,uart-r1p8";
    status = "disabled";
    clocks = <0x1 0x18 0x1 0x29>;
    clock-names = "uart_clk", "pclk";
    reg = <0xe0001000 0x1000>;
    interrupts = <0x0 0x32 0x4>;
    };

    spi@e0006000 {
    compatible = "xlnx,zynq-spi-r1p6";
    reg = <0xe0006000 0x1000>;
    status = "okay";
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x1a 0x4>;
    clocks = <0x1 0x19 0x1 0x22>;
    clock-names = "ref_clk", "pclk";
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    is-decoded-cs = <0x0>;
    num-cs = <0x3>;
    };

    spi@e0007000 {
    compatible = "xlnx,zynq-spi-r1p6";
    reg = <0xe0007000 0x1000>;
    status = "disabled";
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x31 0x4>;
    clocks = <0x1 0x1a 0x1 0x23>;
    clock-names = "ref_clk", "pclk";
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    };

    spi@e000d000 {
    clock-names = "ref_clk", "pclk";
    clocks = <0x1 0xa 0x1 0x2b>;
    compatible = "xlnx,zynq-qspi-1.0";
    status = "okay";
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x13 0x4>;
    reg = <0xe000d000 0x1000>;
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    is-dual = <0x0>;
    num-cs = <0x1>;
    };

    memory-controller@e000e000 {
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    status = "disabled";
    clock-names = "memclk", "aclk";
    clocks = <0x1 0xb 0x1 0x2c>;
    compatible = "arm,pl353-smc-r2p1";
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x12 0x4>;
    ranges;
    reg = <0xe000e000 0x1000>;

    flash@e1000000 {
    status = "disabled";
    compatible = "arm,pl353-nand-r2p1";
    reg = <0xe1000000 0x1000000>;
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    };

    flash@e2000000 {
    status = "disabled";
    compatible = "cfi-flash";
    reg = <0xe2000000 0x2000000>;
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    };
    };

    ethernet@e000b000 {
    compatible = "xlnx,ps7-ethernet-1.00.a";
    reg = <0xe000b000 0x1000>;
    status = "okay";
    interrupts = <0x0 0x16 0x4>;
    clocks = <0x1 0xd 0x1 0x1e>;
    clock-names = "ref_clk", "aper_clk";
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    enet-reset = <0x4 0x2f 0x0>;
    local-mac-address = [00 0a 35 00 00 00];
    phy-mode = "rgmii";
    phy-handle = <0x7>;
    xlnx,eth-mode = <0x1>;
    xlnx,has-mdio = <0x1>;
    xlnx,ptp-enet-clock = <0x69f6bcb>;

    mdio {
    #address-cells = <0x1>;
    #size-cells = <0x0>;

    phy@0 {
    compatible = "marvell,88e1111";
    device_type = "ethernet-phy";
    reg = <0x0>;
    linux,phandle = <0x7>;
    phandle = <0x7>;
    };
    };
    };

    ethernet@e000c000 {
    compatible = "cdns,zynq-gem", "cdns,gem";
    reg = <0xe000c000 0x1000>;
    status = "disabled";
    interrupts = <0x0 0x2d 0x4>;
    clocks = <0x1 0x1f 0x1 0x1f 0x1 0xe>;
    clock-names = "pclk", "hclk", "tx_clk";
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    };

    sdhci@e0100000 {
    compatible = "arasan,sdhci-8.9a";
    status = "disabled";
    clock-names = "clk_xin", "clk_ahb";
    clocks = <0x1 0x15 0x1 0x20>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x18 0x4>;
    reg = <0xe0100000 0x1000>;
    };

    sdhci@e0101000 {
    compatible = "arasan,sdhci-8.9a";
    status = "disabled";
    clock-names = "clk_xin", "clk_ahb";
    clocks = <0x1 0x16 0x1 0x21>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x2f 0x4>;
    reg = <0xe0101000 0x1000>;
    };

    slcr@f8000000 {
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    compatible = "xlnx,zynq-slcr", "syscon", "simple-mfd";
    reg = <0xf8000000 0x1000>;
    ranges;
    linux,phandle = <0x5>;
    phandle = <0x5>;

    clkc@100 {
    #clock-cells = <0x1>;
    compatible = "xlnx,ps7-clkc";
    fclk-enable = <0x1>;
    clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x", "cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x", "dci", "lqspi", "smc", "pcap", "gem0", "gem1", "fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1", "sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1", "dma", "usb0_aper", "usb1_aper", "gem0_aper", "gem1_aper", "sdio0_aper", "sdio1_aper", "spi0_aper", "spi1_aper", "can0_aper", "can1_aper", "i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper", "gpio_aper", "lqspi_aper", "smc_aper", "swdt", "dbg_trc", "dbg_apb";
    reg = <0x100 0x100>;
    ps-clk-frequency = <0x2faf080>;
    linux,phandle = <0x1>;
    phandle = <0x1>;
    };

    rstc@200 {
    compatible = "xlnx,zynq-reset";
    reg = <0x200 0x48>;
    #reset-cells = <0x1>;
    syscon = <0x5>;
    };

    pinctrl@700 {
    compatible = "xlnx,pinctrl-zynq";
    reg = <0x700 0x200>;
    syscon = <0x5>;
    };
    };

    dmac@f8003000 {
    compatible = "arm,pl330", "arm,primecell";
    reg = <0xf8003000 0x1000>;
    interrupt-parent = <0x4>;
    interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3", "dma4", "dma5", "dma6", "dma7";
    interrupts = <0x0 0xd 0x4 0x0 0xe 0x4 0x0 0xf 0x4 0x0 0x10 0x4 0x0 0x11 0x4 0x0 0x28 0x4 0x0 0x29 0x4 0x0 0x2a 0x4 0x0 0x2b 0x4>;
    #dma-cells = <0x1>;
    #dma-channels = <0x8>;
    #dma-requests = <0x4>;
    clocks = <0x1 0x1b>;
    clock-names = "apb_pclk";
    };

    devcfg@f8007000 {
    compatible = "xlnx,zynq-devcfg-1.0";
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x8 0x4>;
    reg = <0xf8007000 0x100>;
    clocks = <0x1 0xc 0x1 0xf 0x1 0x10 0x1 0x11 0x1 0x12>;
    clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";
    syscon = <0x5>;
    linux,phandle = <0x3>;
    phandle = <0x3>;
    };

    efuse@f800d000 {
    compatible = "xlnx,zynq-efuse";
    reg = <0xf800d000 0x20>;
    };

    timer@f8f00200 {
    compatible = "arm,cortex-a9-global-timer";
    reg = <0xf8f00200 0x20>;
    interrupts = <0x1 0xb 0x301>;
    interrupt-parent = <0x4>;
    clocks = <0x1 0x4>;
    };

    timer@f8001000 {
    interrupt-parent = <0x4>;
    interrupts = <0x0 0xa 0x4 0x0 0xb 0x4 0x0 0xc 0x4>;
    compatible = "cdns,ttc";
    clocks = <0x1 0x6>;
    reg = <0xf8001000 0x1000>;
    };

    timer@f8002000 {
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x25 0x4 0x0 0x26 0x4 0x0 0x27 0x4>;
    compatible = "cdns,ttc";
    clocks = <0x1 0x6>;
    reg = <0xf8002000 0x1000>;
    };

    timer@f8f00600 {
    interrupt-parent = <0x4>;
    interrupts = <0x1 0xd 0x301>;
    compatible = "arm,cortex-a9-twd-timer";
    reg = <0xf8f00600 0x20>;
    clocks = <0x1 0x4>;
    };

    usb@e0002000 {
    compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";
    status = "disabled";
    clocks = <0x1 0x1c>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x15 0x4>;
    reg = <0xe0002000 0x1000>;
    phy_type = "ulpi";
    };

    usb@e0003000 {
    compatible = "xlnx,zynq-usb-2.20a", "chipidea,usb2";
    status = "disabled";
    clocks = <0x1 0x1d>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x2c 0x4>;
    reg = <0xe0003000 0x1000>;
    phy_type = "ulpi";
    };

    watchdog@f8005000 {
    clocks = <0x1 0x2d>;
    compatible = "cdns,wdt-r1p2";
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x9 0x1>;
    reg = <0xf8005000 0x1000>;
    timeout-sec = <0xa>;
    };
    };

    amba_pl {
    #address-cells = <0x1>;
    #size-cells = <0x1>;
    compatible = "simple-bus";
    ranges;

    gpio@41200000 {
    #gpio-cells = <0x2>;
    compatible = "xlnx,xps-gpio-1.00.a";
    gpio-controller;
    reg = <0x41200000 0x10000>;
    xlnx,all-inputs = <0x0>;
    xlnx,all-inputs-2 = <0x0>;
    xlnx,all-outputs = <0x1>;
    xlnx,all-outputs-2 = <0x0>;
    xlnx,dout-default = <0x0>;
    xlnx,dout-default-2 = <0x0>;
    xlnx,gpio-width = <0x8>;
    xlnx,gpio2-width = <0x20>;
    xlnx,interrupt-present = <0x0>;
    xlnx,is-dual = <0x0>;
    xlnx,tri-default = <0xffffffff>;
    xlnx,tri-default-2 = <0xffffffff>;
    };

    uio@0{
    compatible="generic-uio";
    status="okay";
    interrupt-controller;
    interrupt-parent=<0x4>;
    interrupts=<0x0 0x1e 0x4>;
    };

    uio@1{
    compatible="generic-uio";
    status="okay";
    interrupt-controller;
    interrupt-parent=<0x4>;
    interrupts=<0x0 0x1f 0x4>;
    };

    uio@2{
    compatible="generic-uio";
    status="okay";
    interrupt-controller;
    interrupt-parent=<0x4>;
    interrupts=<0x0 0x20 0x4>;
    };

    uio@3{
    compatible="generic-uio";
    status="okay";
    interrupt-controller;
    interrupt-parent=<0x4>;
    interrupts=<0x0 0x21 0x4>;
    };

    axi-pcie@90000000 {
    #address-cells = <0x3>;
    #interrupt-cells = <0x1>;
    #size-cells = <0x2>;
    compatible = "xlnx,axi-pcie-host-1.00.a";
    device_type = "pci";
    interrupt-map = <0x0 0x0 0x0 0x1 0x6 0x1 0x0 0x0 0x0 0x2 0x6 0x2 0x0 0x0 0x0 0x3 0x6 0x3 0x0 0x0 0x0 0x4 0x6 0x4>;
    interrupt-map-mask = <0x0 0x0 0x0 0x7>;
    interrupt-parent = <0x4>;
    interrupts = <0x0 0x1d 0x4>;
    ranges = <0x2000000 0x0 0xa0000000 0xa0000000 0x0 0x20000000>;
    reg = <0x90000000 0x4000000>;

    interrupt-controller {
    #address-cells = <0x0>;
    #interrupt-cells = <0x1>;
    interrupt-controller;
    linux,phandle = <0x6>;
    phandle = <0x6>;
    };
    };
    };

    chosen {
    bootargs = "earlycon uio_pdrv_genirq.of_id=generic-uio";
    stdout-path = "serial0:115200n8";
    };

    aliases {
    ethernet0 = "/amba/ethernet@e000b000";
    serial0 = "/amba/serial@e0000000";
    spi0 = "/amba/spi@e000d000";
    spi1 = "/amba/spi@e0006000";
    };

    memory {
    device_type = "memory";
    reg = <0x0 0x40000000>;
    };
    };

    4、  kernel配置
    在kernel中需要把UIO驱动编译进内核,这里使用的版本是XILINX 2017.4(内核版本4.9)。

    5、  UIO测试
    修改完devicetree和kernel,就可以启动linux对UIO进行测试了。这里通过cat /proc/interrupts看到的信息如下。

    为了测试中断,需要在vivado中引入VIO机制,模拟四根线电平拉高拉低。示例代码如下:

    使用vio修改value值即可模拟

    此时能发现确实收到了中断

    不过UIO存在一个问题,当中断到来时,驱动处理函数中将该中断disable,导致每次只能响应一次。需要用户启用该中断shell中输入echo 0x1 > /dev/uioX 或者调用write()函数。

    再次启用中断后,计数值增加了。

    6、  用户自定义中断
    UIO机制中提供了中断响应函数,不过该处理函数只是禁中断,比较简单,用户完全可以对该函数进行修改,增加自己的处理过程,比如像下面这样

    其中intrX_handler函数定义如下

    这样在中断来临时,就能触发自定义函数的执行。

    注意,在执行完自定义中断处理函数结束后enable对应的irq,否则就要像上面那样手动打开了。

    uio驱动代码如下:

    /*
    * drivers/uio/uio_pdrv_genirq.c
    *
    * Userspace I/O platform driver with generic IRQ handling code.
    *
    * Copyright (C) 2008 Magnus Damm
    *
    * Based on uio_pdrv.c by Uwe Kleine-Koenig,
    * Copyright (C) 2008 by Digi International Inc.
    * All rights reserved.
    *
    * This program is free software; you can redistribute it and/or modify it
    * under the terms of the GNU General Public License version 2 as published by
    * the Free Software Foundation.
    */

    #include <linux/platform_device.h>
    #include <linux/uio_driver.h>
    #include <linux/spinlock.h>
    #include <linux/bitops.h>
    #include <linux/module.h>
    #include <linux/interrupt.h>
    #include <linux/stringify.h>
    #include <linux/pm_runtime.h>
    #include <linux/slab.h>

    #include <linux/of.h>
    #include <linux/of_platform.h>
    #include <linux/of_address.h>

    #include "dal_kernel.h"

    //extern static irqreturn_t intr0_handler(int irq,void* dev_id);
    //extern static irqreturn_t intr1_handler(int irq,void* dev_id);
    //extern static irqreturn_t intr2_handler(int irq,void* dev_id);
    //extern static irqreturn_t intr3_handler(int irq,void* dev_id);

    #define DRIVER_NAME "uio_pdrv_genirq"

    struct uio_pdrv_genirq_platdata {
    struct uio_info *uioinfo;
    spinlock_t lock;
    unsigned long flags;
    struct platform_device *pdev;
    };

    /* Bits in uio_pdrv_genirq_platdata.flags */
    enum {
    UIO_IRQ_DISABLED = 0,
    };

    static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode)
    {
    struct uio_pdrv_genirq_platdata *priv = info->priv;

    /* Wait until the Runtime PM code has woken up the device */
    pm_runtime_get_sync(&priv->pdev->dev);
    return 0;
    }

    static int uio_pdrv_genirq_release(struct uio_info *info, struct inode *inode)
    {
    struct uio_pdrv_genirq_platdata *priv = info->priv;

    /* Tell the Runtime PM code that the device has become idle */
    pm_runtime_put_sync(&priv->pdev->dev);
    return 0;
    }

    static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info)
    {
    struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
    /* Just disable the interrupt in the interrupt controller, and
    * remember the state so we can allow user space to enable it later.
    */
    #if 0
    spin_lock(&priv->lock);
    if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
    {
    disable_irq_nosync(irq);
    }
    spin_unlock(&priv->lock);
    #endif
    printk(">>>>>>handle UIO Interrupt now,irq number is %d ",irq);
    #if 1
    //get irq related handler
    if(irq==47)
    {
    printk("Trigger interrupts 47 ");
    intr0_handler(47,NULL);
    }
    if(irq==48)
    {
    printk("Trigger interrupts 48 ");
    intr1_handler(48,NULL);
    }
    if(irq==49)
    {
    printk("Trigger interrupts 49 ");
    intr2_handler(49,NULL);
    }
    if(irq==50)
    {
    printk("Trigger interrupts 50 ");
    intr3_handler(50,NULL);
    }
    #endif
    return IRQ_HANDLED;
    }

    static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
    {
    struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
    unsigned long flags;

    /* Allow user space to enable and disable the interrupt
    * in the interrupt controller, but keep track of the
    * state to prevent per-irq depth damage.
    *
    * Serialize this operation to support multiple tasks and concurrency
    * with irq handler on SMP systems.
    */

    spin_lock_irqsave(&priv->lock, flags);
    if (irq_on) {
    if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags))
    enable_irq(dev_info->irq);
    } else {
    if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
    disable_irq_nosync(dev_info->irq);
    }
    spin_unlock_irqrestore(&priv->lock, flags);

    return 0;
    }

    static int uio_pdrv_genirq_probe(struct platform_device *pdev)
    {
    struct uio_info *uioinfo = dev_get_platdata(&pdev->dev);
    struct uio_pdrv_genirq_platdata *priv;
    struct uio_mem *uiomem;
    int ret = -EINVAL;
    int i;

    if (pdev->dev.of_node) {
    /* alloc uioinfo for one device */
    uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo),
    GFP_KERNEL);
    if (!uioinfo) {
    dev_err(&pdev->dev, "unable to kmalloc ");
    return -ENOMEM;
    }
    uioinfo->name = pdev->dev.of_node->name;
    uioinfo->version = "devicetree";
    /* Multiple IRQs are not supported */
    }

    if (!uioinfo || !uioinfo->name || !uioinfo->version) {
    dev_err(&pdev->dev, "missing platform_data ");
    return ret;
    }

    if (uioinfo->handler || uioinfo->irqcontrol ||
    uioinfo->irq_flags & IRQF_SHARED) {
    dev_err(&pdev->dev, "interrupt configuration error ");
    return ret;
    }

    priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv) {
    dev_err(&pdev->dev, "unable to kmalloc ");
    return -ENOMEM;
    }

    priv->uioinfo = uioinfo;
    spin_lock_init(&priv->lock);
    priv->flags = 0; /* interrupt is enabled to begin with */
    priv->pdev = pdev;

    if (!uioinfo->irq) {
    ret = platform_get_irq(pdev, 0);
    uioinfo->irq = ret;
    if (ret == -ENXIO && pdev->dev.of_node)
    uioinfo->irq = UIO_IRQ_NONE;
    else if (ret < 0) {
    dev_err(&pdev->dev, "failed to get IRQ ");
    return ret;
    }
    }

    uiomem = &uioinfo->mem[0];

    for (i = 0; i < pdev->num_resources; ++i) {
    struct resource *r = &pdev->resource[i];

    if (r->flags != IORESOURCE_MEM)
    continue;

    if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
    dev_warn(&pdev->dev, "device has more than "
    __stringify(MAX_UIO_MAPS)
    " I/O memory resources. ");
    break;
    }

    uiomem->memtype = UIO_MEM_PHYS;
    uiomem->addr = r->start;
    uiomem->size = resource_size(r);
    uiomem->name = r->name;
    ++uiomem;
    }

    while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
    uiomem->size = 0;
    ++uiomem;
    }

    /* This driver requires no hardware specific kernel code to handle
    * interrupts. Instead, the interrupt handler simply disables the
    * interrupt in the interrupt controller. User space is responsible
    * for performing hardware specific acknowledge and re-enabling of
    * the interrupt in the interrupt controller.
    *
    * Interrupt sharing is not supported.
    */

    uioinfo->handler = uio_pdrv_genirq_handler;
    uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol;
    uioinfo->open = uio_pdrv_genirq_open;
    uioinfo->release = uio_pdrv_genirq_release;
    uioinfo->priv = priv;

    /* Enable Runtime PM for this device:
    * The device starts in suspended state to allow the hardware to be
    * turned off by default. The Runtime PM bus code should power on the
    * hardware and enable clocks at open().
    */
    pm_runtime_enable(&pdev->dev);

    ret = uio_register_device(&pdev->dev, priv->uioinfo);
    if (ret) {
    dev_err(&pdev->dev, "unable to register uio device ");
    pm_runtime_disable(&pdev->dev);
    return ret;
    }

    platform_set_drvdata(pdev, priv);
    return 0;
    }

    static int uio_pdrv_genirq_remove(struct platform_device *pdev)
    {
    struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev);

    uio_unregister_device(priv->uioinfo);
    pm_runtime_disable(&pdev->dev);

    priv->uioinfo->handler = NULL;
    priv->uioinfo->irqcontrol = NULL;

    return 0;
    }

    static int uio_pdrv_genirq_runtime_nop(struct device *dev)
    {
    /* Runtime PM callback shared between ->runtime_suspend()
    * and ->runtime_resume(). Simply returns success.
    *
    * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
    * are used at open() and release() time. This allows the
    * Runtime PM code to turn off power to the device while the
    * device is unused, ie before open() and after release().
    *
    * This Runtime PM callback does not need to save or restore
    * any registers since user space is responsbile for hardware
    * register reinitialization after open().
    */
    return 0;
    }

    static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
    .runtime_suspend = uio_pdrv_genirq_runtime_nop,
    .runtime_resume = uio_pdrv_genirq_runtime_nop,
    };

    #ifdef CONFIG_OF
    static struct of_device_id uio_of_genirq_match[] = {
    { /* This is filled with module_parm */ },
    { /* Sentinel */ },
    };
    MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
    module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0);
    MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");
    #endif

    static struct platform_driver uio_pdrv_genirq = {
    .probe = uio_pdrv_genirq_probe,
    .remove = uio_pdrv_genirq_remove,
    .driver = {
    .name = DRIVER_NAME,
    .pm = &uio_pdrv_genirq_dev_pm_ops,
    .of_match_table = of_match_ptr(uio_of_genirq_match),
    },
    };

    module_platform_driver(uio_pdrv_genirq);

    MODULE_AUTHOR("Magnus Damm");
    MODULE_DESCRIPTION("Userspace I/O platform driver with generic IRQ handling");
    MODULE_LICENSE("GPL v2");
    MODULE_ALIAS("platform:" DRIVER_NAME);

    7、  一点补充
    a)UIO机制中虽然包含四种中断方式(高电平、低电平、上升沿、下降沿),但在zynq中只支持2种(高电平、上升沿),中断方式的配置位于devicetree中,示例如下

    Devicetree中大多数设备的interrupts最后参数都是0x4,从cat/proc/interrupts中能看到只有watchdog几个为上升沿触发。

    b)使用request_irq申请中断的时候irq的值不是devicetree中设定的值,而是操作系统启动后分配的值,所以在UIO中打印出来irq值不是62-65,而是47-50。通过在irq-gic.c中和uio_pdrv_genirq.c中增加打印信息,发现中断触发时过程如下。

    需要注意的是,在gic中该值为62,这和devicetree中设置的一样。而从打印信息能够看到,中断是先送到gic,然后再送给内核,内核再调用uio的中断处理函数。所以如果需要在uio中增加自己的代码处理相应的中断,需要先启动操作系统,确认好每根线对应的中断号才行。

    注意:在这个例子中,由于UIO已经注册了47-50号中断,且UIO不支持中断共享,所以其他程序再次注册47-50号中断时会出错。
    ---------------------
    作者:Felven
    来源:CSDN
    原文:https://blog.csdn.net/zhaoxinfan/article/details/80285150
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    虚基类练习 动物1
    UVa 10820
    hdu1027 Ignatius and the Princess II (全排列 &amp; STL中的神器)
    在windows下安装redmine及相关问题
    批量导出表数据到CSV文件
    轻松学习Ionic (二) 为Android项目集成Crosswalk(更新官方命令行工具)
    swift第一章
    socket编程演示样例(多线程)
    谋哥:玩App怎么赚钱(三)
    Oracle database wrc运行报错ORA-15557
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/9937931.html
Copyright © 2020-2023  润新知