linux内核多级虚拟中断控制器驱动

❓ 问题描述:

编写一个多级虚拟中断控制器

日志

添加打印日志信息

分析步骤

第1步:
第2步:
...

代码片段

设备树

vic: virtual_irq_controller {
    compatible = "mycorp,virtual-irq-controller";
    interrupt-controller;
    // 使用两个 cell: 第一个用于 HWIRQ,第二个用于中断类型
    #interrupt-cells = <2>;
    
    // 自定义属性,用于告知驱动硬件中断号的起始值
    mycorp,hwirq-base = <0>; 
};

virtual_gpio_controller: vgpio-controller@0 {
    compatible = "my-company,virtual-gpio-controller";
    gpio-controller; 
    #gpio-cells = <2>;
    ngpios = <32>;
    interrupt-controller;
    #interrupt-cells = <2>;
    interrupt-parent = <&vic>; 
    interrupts = <10 4>;
};

gpio_irq_tester: gpio-irq-tester {
    compatible = "my-company,gpio-irq-tester";
    //获取中断号有下面2种方式:
    //第一种:下面这种方式使用platform_get_irq方式获取中断号
    // interrupt-parent = <&virtual_gpio_controller>; 
    // interrupts = <5 0>;
    
    //第二种:gpiod_to_irq
    red-gpios = <&virtual_gpio_controller 5 0>;
};

一级虚拟控制器代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_irq.h>

#define VIC_MAX_IRQS 100 // 支持32个中断

// 私有数据结构
struct vic_chip_data {
    struct irq_domain *domain;
    u32 hwirq_base;
};

// 中断控制器操作集 (irq_chip)
static void vic_irq_mask(struct irq_data *d) {
    pr_info("VIC: Masking public hwirq %lu, irq %u,domain %p
", d->hwirq, d->irq,d->domain);
}

static void vic_irq_unmask(struct irq_data *d) {
    pr_info("VIC: Unmasking public hwirq %lu, irq %u,domain %p
", d->hwirq, d->irq,d->domain);
}

static int vic_irq_set_type(struct irq_data *d, unsigned int type) {

    pr_info("VIC: Setting type %u for hwirq %lu, irq %u,domain %p
", type, d->hwirq, d->irq,d->domain);

    return 0;
}

static struct irq_chip vic_irq_chip = {
    .name       = "VIC",
    .irq_mask   = vic_irq_mask,
    .irq_unmask = vic_irq_unmask,
    .irq_set_type = vic_irq_set_type,
};

// IRQ Domain 的 .map 回调
static int vic_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) {
    // 使用 irq_domain_add_simple 后,这里的 hwirq 就是 public hwirq (e.g., 102)
    pr_info("VIC: Mapping public hwirq %lu to Linux irq %u
", hwirq, irq);

    irq_set_chip_and_handler(irq, &vic_irq_chip, handle_level_irq);
    irq_set_chip_data(irq, d->host_data);
    irq_set_probe(irq);
    return 0;
}

static int vic_domain_xlate(struct irq_domain *d, struct device_node *ctrl,
                           const u32 *intspec, unsigned int intsize,
                           irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
    u32 public_hwirq = intspec[0];

    //input: intspec[0] = hwirq(硬件中断号), intspec[1] = irq_type(中断类型)
    printk("VIC: xlate: intspec[0] %u, intspec[1] %u, intspec[2] %u
", intspec[0], intspec[1]);
    
    // 核心转换,这里也可以自己进行转换,最终实际的硬件中断号,在proc/interrupts中显示的硬件中断号,在实际触发时也必须是这个
    *out_hwirq = public_hwirq;
    *out_type = intspec[1];

    pr_info("VIC: xlate: public %u -> internal %lu
", public_hwirq, *out_hwirq);

    return 0;
}

// IRQ Domain 操作集
static const struct irq_domain_ops vic_domain_ops = {
    .map    = vic_irq_map,
    // 直接使用内核提供的 twocell 翻译函数,无需自定义
    .xlate  = irq_domain_xlate_twocell,
    // .xlate  = vic_domain_xlate,
};

// Sysfs 触发函数
static ssize_t trigger_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
    struct platform_device *pdev = to_platform_device(dev);
    struct vic_chip_data *data = platform_get_drvdata(pdev);
    unsigned long public_hwirq;
    int linux_irq;

    if (kstrtoul(buf, 0, &public_hwirq) < 0) return -EINVAL;

    // 检查范围
    if (public_hwirq < data->hwirq_base || public_hwirq >= (data->hwirq_base + VIC_MAX_IRQS)) {
        dev_err(dev, "Public hwirq %lu is out of range [%u, %u)
", 
                public_hwirq, data->hwirq_base, data->hwirq_base + VIC_MAX_IRQS);
        return -EINVAL;
    }

    // 使用 public_hwirq 直接查找映射
    linux_irq = irq_find_mapping(data->domain, public_hwirq);
    if (linux_irq <= 0) {
        dev_err(dev, "Failed to find mapping for public hwirq %lu. Is client driver loaded?
", public_hwirq);
        return -ENXIO;
    }

    pr_info("VIC: Triggering public hwirq %lu (Linux irq %d)
", public_hwirq, linux_irq);

    // 1. 禁用本地CPU中断,模拟硬件中断的自动行为
    local_irq_disable();

    generic_handle_irq(linux_irq);

    // 3. 重新使能本地CPU中断
    local_irq_enable();

    return count;
}
static DEVICE_ATTR_WO(trigger);

// 平台驱动 probe 函数
static int vic_probe(struct platform_device *pdev) {
    struct device_node *node = pdev->dev.of_node;
    struct vic_chip_data *data;
    u32 first_irq;

    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data) return -ENOMEM;
    platform_set_drvdata(pdev, data);

    if (of_property_read_u32(node, "mycorp,hwirq-base", &first_irq)) {
        dev_warn(&pdev->dev, "No 'mycorp,hwirq-base' found, assuming 0
");
        first_irq = 0;
    }
    data->hwirq_base = first_irq;

    // 使用 irq_domain_add_simple 创建域
 //   data->domain = irq_domain_add_simple(node, VIC_MAX_IRQS, first_irq, &vic_domain_ops, data);
   data->domain = irq_domain_add_linear(node, VIC_MAX_IRQS, &vic_domain_ops, data);

   printk("VIC: domain %p
", data->domain);
   if (!data->domain) {
        dev_err(&pdev->dev, "Failed to create simple IRQ domain
");
        return -ENOMEM;
    }

    if (device_create_file(&pdev->dev, &dev_attr_trigger)) {
        dev_err(&pdev->dev, "Failed to create sysfs 'trigger' file
");
        irq_domain_remove(data->domain);
        return -EINVAL;
    }

    dev_info(&pdev->dev, "Simple Virtual IRQ Controller initialized, range %u-%u
", 
             data->hwirq_base, data->hwirq_base + VIC_MAX_IRQS - 1);
    return 0;
}

// 平台驱动 remove 函数
static int vic_remove(struct platform_device *pdev) {
    struct vic_chip_data *data = platform_get_drvdata(pdev);
    device_remove_file(&pdev->dev, &dev_attr_trigger);
    irq_domain_remove(data->domain);
    pr_info("VIC: Virtual IRQ Controller removed
");
    return 0;
}

static const struct of_device_id vic_of_match[] = {
    { .compatible = "mycorp,virtual-irq-controller" },
    { /* sentinel */ }
};

static struct platform_driver vic_driver = {
    .driver = {
        .name = "virtual-irq-controller",
        .of_match_table = vic_of_match,
    },
    .probe  = vic_probe,
    .remove = vic_remove,
};

module_platform_driver(vic_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Virtual Interrupt Controller using irq_domain_add_simple");

二级虚拟控制器代码

/**
 * @file 2_vgpio_driver.c
 * @brief A complete and final virtual GPIO controller driver, acting as a cascaded IRQ controller.
 *
 * This version is specifically written to be compatible with older Linux kernels (e.g., v4.4)
 * by using the classic method of integrating gpiolib with irqdomain. This involves:
 * 1. A custom .to_irq callback on the gpio_chip.
 * 2. A custom .map callback on the irq_domain_ops.
 * 3. Manual resource management with gpiochip_add/remove and irq_domain_add/remove.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio/driver.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>

/**
 * @struct virtual_gpio_chip
 * @brief Private data structure for our virtual GPIO controller.
 */
struct virtual_gpio_chip {
    /** The core gpio_chip structure provided to gpiolib. */
    struct gpio_chip chip;
    /** A spinlock to protect shared data from concurrent access. */
    spinlock_t lock;

    /** Simulates the direction register (0=input, 1=output). */
    unsigned int *directions;
    /** Simulates the data register (0=low, 1=high). */
    unsigned int *values;

    /** Pointer to the IRQ domain for managing GPIO-to-IRQ mapping. */
    struct irq_domain *domain;
    /** The Linux IRQ number of the line connecting to the parent controller. */
    int parent_irq;
    /** Simulates the interrupt pending/status register. */
    unsigned long *irq_pending_mask;
    /** Simulates the interrupt enable/mask register. */
    unsigned long *irq_enabled_mask;
};

/**
 * @brief Helper to get our private data from a generic gpio_chip pointer.
 */
static inline struct virtual_gpio_chip *to_virtual_gpio_chip(struct gpio_chip *chip)
{
    return container_of(chip, struct virtual_gpio_chip, chip);
}


//==================================================================================
//  1. GPIO Chip Callback Implementations
//==================================================================================

static int vgpio_request(struct gpio_chip *chip, unsigned int offset)
{
    pr_info("vgpio: Requested GPIO %u
", offset);
    return 0;
}

static void vgpio_free(struct gpio_chip *chip, unsigned int offset)
{
    pr_info("vgpio: Freed GPIO %u
", offset);
}

static int vgpio_get_direction(struct gpio_chip *chip, unsigned int offset)
{
    return to_virtual_gpio_chip(chip)->directions[offset] == 0;
}

static int vgpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
    to_virtual_gpio_chip(chip)->directions[offset] = 0;
    pr_info("vgpio: Set GPIO %u to INPUT
", offset);
    return 0;
}

static int vgpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value)
{
    struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(chip);
    vgpio->directions[offset] = 1;
    vgpio->values[offset] = !!value;
    pr_info("vgpio: Set GPIO %u to OUTPUT, value: %d
", offset, !!value);
    return 0;
}

static int vgpio_get(struct gpio_chip *chip, unsigned int offset)
{
    return to_virtual_gpio_chip(chip)->values[offset];
}

static void vgpio_set(struct gpio_chip *chip, unsigned int offset, int value)
{
    struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(chip);
    if (vgpio->directions[offset] == 1) {
        vgpio->values[offset] = !!value;
    }
}


//==================================================================================
//  2. IRQ Chip and Cascaded Handler Implementations
//==================================================================================

static void vgpio_irq_mask(struct irq_data *d)
{
    struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
    struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(gc);

    printk("vgpio: Masking hwirq %lu, irq %u, domain %p
",d->hwirq, d->irq, d->domain);
    // clear_bit(d->hwirq, vgpio->irq_enabled_mask);
}

static void vgpio_irq_unmask(struct irq_data *d)
{
    struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
    struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(gc);

    printk("vgpio: Unmasking hwirq %lu, irq %u, domain %p
", d->hwirq, d->irq, d->domain);
    // set_bit(d->hwirq, vgpio->irq_enabled_mask);
}

static int vgpio_irq_set_type(struct irq_data *d, unsigned int type)
{
    // irq_set_handler_locked(d, handle_level_irq);

    printk("vgpio: Setting type %u hwirq %lu, irq %u, domain %p
", type,  d->hwirq, d->irq, d->domain);
    return 0;
}

static struct irq_chip vgpio_irq_chip = {
    .name       = "vgpio-irq",
    .irq_mask   = vgpio_irq_mask,
    .irq_unmask = vgpio_irq_unmask,
    .irq_set_type = vgpio_irq_set_type,
};

static void vgpio_cascade_irq_handler(struct irq_desc *desc)
{
    struct gpio_chip *gc = irq_desc_get_handler_data(desc);
    struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(gc);
    struct irq_chip *parent_chip = irq_desc_get_chip(desc);
    unsigned long pending;
    unsigned int gpio;

    printk("vgpio: Cascading IRQ handler
");

    if (parent_chip->irq_mask)
        parent_chip->irq_mask(irq_desc_get_irq_data(desc));

    pending = *vgpio->irq_pending_mask;

    printk("vgpio: Pending IRQs: %x
",pending);

    for_each_set_bit(gpio, &pending, vgpio->chip.ngpio) {
        int nested_irq = irq_find_mapping(vgpio->domain, gpio);
        if (nested_irq > 0) {
            generic_handle_irq(nested_irq);
        }
    }
    
    *vgpio->irq_pending_mask = 0;

    if (parent_chip->irq_unmask)
        parent_chip->irq_unmask(irq_desc_get_irq_data(desc));
}


//==================================================================================
//  3. Sysfs Trigger for Debugging
//==================================================================================

static ssize_t trigger_gpio_irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    struct virtual_gpio_chip *vgpio = dev_get_drvdata(dev);
    unsigned long gpio_offset;

    if (kstrtoul(buf, 0, &gpio_offset) < 0 || gpio_offset >= vgpio->chip.ngpio)
        return -EINVAL;

    printk("vgpio: Triggering IRQ for GPIO %lu
", gpio_offset);

    set_bit(gpio_offset, vgpio->irq_pending_mask);
    
    printk("vgpio: Pending IRQs: %*pb
", (int)vgpio->chip.ngpio, vgpio->irq_pending_mask);

    return count;
}
static DEVICE_ATTR_WO(trigger_gpio_irq);

static struct attribute *vgpio_attrs[] = {
    &dev_attr_trigger_gpio_irq.attr,
    NULL,
};

static const struct attribute_group vgpio_attr_group = {
    .attrs = vgpio_attrs,
};


//==================================================================================
//  4. Kernel 4.4 Compatibility Functions (IRQ Integration)
//==================================================================================

static int vgpio_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq)
{
    struct virtual_gpio_chip *vgpio = d->host_data;

    printk("vgpio: Mapping IRQ %u hwirq %lu
", irq,  hwirq);

    irq_set_chip_and_handler(irq, &vgpio_irq_chip, handle_level_irq);
    irq_set_chip_data(irq, &vgpio->chip);
    irq_set_probe(irq);
    return 0;
}

static int vic_domain_xlate(struct irq_domain *d, struct device_node *ctrl,
                           const u32 *intspec, unsigned int intsize,
                           irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
    const u32 hwirq_base = 0; 
    u32 public_hwirq = intspec[0];

    //input: intspec[0] = hwirq(硬件中断号), intspec[1] = irq_type(中断类型)
    printk("VIC: xlate: intspec[0] %u, intspec[1] %u, intspec[2] %u
", intspec[0], intspec[1]);

    // 核心转换,这里也可以自己进行转换,最终实际的硬件中断号,在proc/interrupts中显示的硬件中断号,在实际触发时也必须是这个
    *out_hwirq = public_hwirq - hwirq_base;
    *out_type = intspec[1];

    pr_info("VIC: xlate: public %u -> internal %lu
", public_hwirq, *out_hwirq);

    return 0;
}

static const struct irq_domain_ops vgpio_domain_ops = {
    .map    = vgpio_irq_map,
    // 在此示例中,.xlate 回调并不会调用
    // .xlate  = irq_domain_xlate_twocell,
    // .xlate  = vic_domain_xlate,
};

static int vgpio_to_irq(struct gpio_chip *chip, unsigned int offset)
{
    int irq;
    struct virtual_gpio_chip *vgpio = to_virtual_gpio_chip(chip);

    printk("offset = %u
",offset);
    //传入的硬件编号,在这里如果要改变映射关系,可以改变offset的值,offset的值即在设备树中配置的值
    irq = irq_create_mapping(vgpio->domain, offset);
    printk("irq = %d
", irq);

    return irq;
}


//==================================================================================
//  5. Platform Driver Probe and Remove Functions
//==================================================================================

static int vgpio_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct virtual_gpio_chip *vgpio;
    u32 ngpios;
    int ret;

    vgpio = devm_kzalloc(dev, sizeof(*vgpio), GFP_KERNEL);
    if (!vgpio) return -ENOMEM;

    ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios);
    if (ret < 0 || ngpios == 0) return -EINVAL;
    vgpio->chip.ngpio = ngpios;

    vgpio->directions = devm_kcalloc(dev, ngpios, sizeof(*vgpio->directions), GFP_KERNEL);
    vgpio->values = devm_kcalloc(dev, ngpios, sizeof(*vgpio->values), GFP_KERNEL);
    vgpio->irq_pending_mask = devm_kzalloc(dev, BITS_TO_LONGS(ngpios) * sizeof(long), GFP_KERNEL);
    vgpio->irq_enabled_mask = devm_kzalloc(dev, BITS_TO_LONGS(ngpios) * sizeof(long), GFP_KERNEL);
    if (!vgpio->directions || !vgpio->values || !vgpio->irq_pending_mask || !vgpio->irq_enabled_mask)
        return -ENOMEM;

    spin_lock_init(&vgpio->lock);
    vgpio->chip.label = dev_name(dev);
    vgpio->chip.dev = dev;
    vgpio->chip.owner = THIS_MODULE;
    vgpio->chip.request = vgpio_request;
    vgpio->chip.free = vgpio_free;
    vgpio->chip.get_direction = vgpio_get_direction;
    vgpio->chip.direction_input = vgpio_direction_input;
    vgpio->chip.direction_output = vgpio_direction_output;
    vgpio->chip.get = vgpio_get;
    vgpio->chip.set = vgpio_set;
    vgpio->chip.base = -1;

    // --- K4.4 IRQ Integration ---
    vgpio->chip.to_irq = vgpio_to_irq;

    vgpio->parent_irq = platform_get_irq(pdev, 0);
    if (vgpio->parent_irq < 0) return vgpio->parent_irq;

    vgpio->domain = irq_domain_add_linear(dev->of_node, ngpios, &vgpio_domain_ops, vgpio);
    if (!vgpio->domain) {
        dev_err(dev, "Failed to create IRQ domain
");
        return -ENOMEM;
    }

    ret = gpiochip_add(&vgpio->chip);
    if (ret < 0) {
        dev_err(dev, "Failed to add gpiochip: %d
", ret);
        goto err_remove_domain;
    }

    irq_set_chained_handler_and_data(vgpio->parent_irq, vgpio_cascade_irq_handler, &vgpio->chip);
    platform_set_drvdata(pdev, vgpio);
    sysfs_create_group(&dev->kobj, &vgpio_attr_group);

    dev_info(dev, "Virtual GPIO cascaded IRQ controller initialized (FINAL K4.4 compatible)
");
    return 0;

err_remove_domain:
    irq_domain_remove(vgpio->domain);
    return ret;
}

static int vgpio_remove(struct platform_device *pdev)
{
    struct virtual_gpio_chip *vgpio = platform_get_drvdata(pdev);

    sysfs_remove_group(&pdev->dev.kobj, &vgpio_attr_group);
    
    gpiochip_remove(&vgpio->chip);
    irq_domain_remove(vgpio->domain);

    pr_info("vgpio: Controller unregistered.
");
    return 0;
}


//==================================================================================
//  6. Module Boilerplate
//==================================================================================

static const struct of_device_id vgpio_of_match[] = {
    { .compatible = "my-company,virtual-gpio-controller" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vgpio_of_match);

static struct platform_driver vgpio_driver = {
    .driver = {
        .name = "vgpio-driver",
        .of_match_table = vgpio_of_match,
    },
    .probe = vgpio_probe,
    .remove = vgpio_remove,
};

module_platform_driver(vgpio_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Demo");
MODULE_DESCRIPTION("Cascaded GPIO/IRQ controller (Final K4.4 version)");

测试程序代码

/*
 * @Author: your name
 * @Date: 2025-10-11 15:48:01
 * @LastEditTime: 2025-10-11 16:30:25
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: linux-4.4.159driversgpudrm	estgpio_irq_tester.c
 */
// 3_gpio_irq_tester.c (gpio_to_irq version)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h> // **需要包含这个新的头文件**

// 中断服务例程 (ISR) 保持不变
static irqreturn_t gpio_tester_isr(int irq, void *dev_id)
{
    pr_info("GPIO_IRQ_TESTER: ===> Interrupt received on IRQ %d! (manual conversion) <===
", irq);
    return IRQ_HANDLED;
}

/**
 * @brief 平台驱动的 probe 函数 (gpio_to_irq 版本)
 */
static int gpio_tester_probe(struct platform_device *pdev)
{
    struct gpio_desc *gpiod;
    int irq;
    int ret;

    // --- 步骤 1: 获取 GPIO 描述符 ---
    // devm_gpiod_get() 会解析设备树中的 'gpios' 属性。
    // GPIOD_IN 表明我们希望将这个 GPIO 用作输入。
    // 'devm_' 版本会自动在驱动卸载时释放 GPIO。
    gpiod = devm_gpiod_get(&pdev->dev, "red", GPIOD_IN);
    if (IS_ERR(gpiod)) {
        ret = PTR_ERR(gpiod);
        // EPROBE_DEFER 表明 GPIO 控制器驱动可能还未加载,我们的驱动应该稍后重试。
        if (ret != -EPROBE_DEFER)
            dev_err(&pdev->dev, "Failed to get GPIO descriptor: %d
", ret);
        return ret;
    }
    dev_info(&pdev->dev, "Successfully got GPIO descriptor.
");


    // --- 步骤 2: 将 GPIO 描述符转换为 IRQ 号 ---
    // 这就是手动转换的关键!
    // gpiod_to_irq() 会在内部调用我们 vgpio_driver 中实现的 to_irq 回调。
    irq = gpiod_to_irq(gpiod);
    // irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        dev_err(&pdev->dev, "Failed to translate GPIO to IRQ: %d
", irq);
        // 如果 GPIO 控制器不支持中断,这里会返回错误。
        return irq;
    }
    dev_info(&pdev->dev, "Manually translated GPIO to IRQ %d.
", irq);


    // --- 步骤 3: 请求中断 ---
    // 注意:由于我们没有从设备树的 'interrupts' 属性获取中断,
    // 内核无法自动推断触发类型。因此,在 request_irq 中我们必须明确指定它。
    // IRQF_TRIGGER_FALLING 对应设备树中的 <... 2> (下降沿)。
    ret = devm_request_irq(&pdev->dev, irq, gpio_tester_isr,
                           IRQF_TRIGGER_FALLING, "gpio-tester", pdev);
    if (ret) {
        dev_err(&pdev->dev, "Failed to request IRQ %d: %d
", irq, ret);
        return ret;
    }

    dev_info(&pdev->dev, "GPIO IRQ tester (manual) is ready.
");
    return 0;
}

// remove 函数和模块定义部分保持不变
static int gpio_tester_remove(struct platform_device *pdev)
{
    pr_info("GPIO_IRQ_TESTER: Client driver (manual) removed.
");
    return 0;
}

static const struct of_device_id gpio_tester_of_match[] = {
    { .compatible = "my-company,gpio-irq-tester" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, gpio_tester_of_match);

static struct platform_driver gpio_tester_driver = {
    .driver = { 
        .name = "gpio-tester-driver", 
        .of_match_table = gpio_tester_of_match, 
    },
    .probe = gpio_tester_probe, 
    .remove = gpio_tester_remove,
};
module_platform_driver(gpio_tester_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Demo");
MODULE_DESCRIPTION("Test client for cascaded GPIO IRQ (using gpiod_to_irq)");

日志

/ko # insmod vic_driver.ko
VIC: domain bc003e00
virtual-irq-controller virtual_irq_controller: Simple Virtual IRQ Controller initialized, range 0-99

//可以看到分配的硬件中断号是10,与设备树中配置的一样
/ko # insmod virtual-gpio-controller1.ko
VIC: Mapping public hwirq 10 to Linux irq 46
VIC: Setting type 4 for hwirq 10, irq 46,domain bc003e00
VIC: Unmasking public hwirq 10, irq 46,domain bc003e00
vgpio-driver vgpio-controller@0: Virtual GPIO cascaded IRQ controller initialized (FINAL K4.4 compatible)

//可以看到分配的硬件中断号是5,与设备树中配置的一样
/ko # insmod gpio_irq_tester.ko
con_id = red,gpio_suffixes[i]=gpios
vgpio: Requested GPIO 5
vgpio: Set GPIO 5 to INPUT
gpio-tester-driver gpio-irq-tester: Successfully got GPIO descriptor.
offset = 5
vgpio: Mapping IRQ 47 hwirq 5
irq = 47
gpio-tester-driver gpio-irq-tester: Manually translated GPIO to IRQ 47.
vgpio: Setting type 2 hwirq 5, irq 47, domain bb9c69c0
vgpio: Unmasking hwirq 5, irq 47, domain bb9c69c0
gpio-tester-driver gpio-irq-tester: GPIO IRQ tester (manual) is ready.

//查看中断
/ko # cat /proc/interrupts
           CPU0       CPU1

 38:         67          0       GIC  37 Level     uart-pl011
 47:          0          0  vgpio-irq   5 Edge      gpio-tester

//先模拟GPIO中有一个中断发生
/ko # echo 5 > /sys/devices/platform/vgpio-controller@0/trigger_gpio_irq
vgpio: Triggering IRQ for GPIO 5
vgpio: Pending IRQs: 00000020

//模拟中断发生
/ko # echo 10 > /sys/devices/platform/virtual_irq_controller/trigger
VIC: Triggering public hwirq 10 (Linux irq 46)
vgpio: Cascading IRQ handler
VIC: Masking public hwirq 10, irq 46,domain bc003e00
vgpio: Pending IRQs: 20
vgpio: Masking hwirq 5, irq 47, domain bb9c69c0
GPIO_IRQ_TESTER: ===> Interrupt received on IRQ 47! (manual conversion) <===
vgpio: Unmasking hwirq 5, irq 47, domain bb9c69c0
VIC: Unmasking public hwirq 10, irq 46,domain bc003e00

图片

✅ 结论

输出结论

待查资料问题

  • ❓ 问题 1:?
  • ❓ 问题 2:?

参考链接

  • 官方文档
© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...