linux 中断子系统 层级中断编程
虚拟中断控制器代码:
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/clk.h>
#include<linux/err.h>
#include<linux/init.h>
#include<linux/interrupt.h>
#include<linux/io.h>
#include<linux/irq.h>
#include<linux/irqdomain.h>
#include<linux/of.h>
#include<linux/gpio.h>
#include<linux/platform_device.h>
#include<linux/of_device.h>
#include<linux/irqchip/chained_irq.h>
#include<linux/of_irq.h>static struct irq_domain *virt_intc_domain;
static u32 upper_hwirq_base;static void virt_intc_irq_ack(struct irq_data *d)
{printk("%s %d\n", __FUNCTION__, __LINE__);irq_chip_ack_parent(d);
}static void virt_intc_irq_mask(struct irq_data *d)
{printk("%s %d\n", __FUNCTION__, __LINE__);irq_chip_mask_parent(d);
}static void virt_intc_irq_mask_ack(struct irq_data *d)
{printk("%s %d\n", __FUNCTION__, __LINE__);
}static void virt_intc_unmask(struct irq_data *d)
{printk("%s %d\n", __FUNCTION__, __LINE__);irq_chip_unmask_parent(d);
}static void virt_intc_irq_eoi(struct irq_data *d)
{printk("%s %d\n", __FUNCTION__, __LINE__);irq_chip_eoi_parent(d);
}/*在回调函数里加打印观察调用顺序,理解调用过程*/
static struct irq_chip virt_intc_irq_chip = {.name = "virt_intc",.irq_ack = virt_intc_irq_ack,.irq_mask = virt_intc_irq_mask,.irq_mask_ack = virt_intc_irq_mask_ack,.irq_unmask = virt_intc_unmask,.irq_eoi = virt_intc_irq_eoi,
};//利用改函数解析设备树
static int virt_intc_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec,unsigned long *out_hwirq, unsigned int *out_type)
{if(is_of_node(fwspec->fwnode)) {if(fwspec->param_count != 2)return -EINVAL;*out_hwirq = fwspec->param[0]; //硬件中断号*out_type = fwspec->param[1]; //触发类型printk("param[0] = %d, param[1] = %d\n", fwspec->param[0], fwspec->param[1]);return 0;}return -EINVAL;
}//这个虚拟中断控制器的alloc函数主要设置irq_domain里的啥?
static int virt_intc_domain_alloc(struct irq_domain *domain, unsigned int virq,unsigned int nr_irqs, void *data)
{struct irq_fwspec *fwspec = data;struct irq_fwspec parent_fwspec;irq_hw_number_t hwirq;int i;//这里的参数domain指的是virt_intc_domainhwirq = fwspec->param[0];printk("virq = %d, hwirq = %ld, nr_irqs = %d, domain = %px\n", virq, hwirq, nr_irqs, domain);for(i = 0; i < nr_irqs; i++)//设置irq_desc[virq].irq_data的相关变量. handle_irq不设置吗?(不需要)irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, &virt_intc_irq_chip, NULL);//virt的domain的父domainparent_fwspec.fwnode = domain->parent->fwnode; //gic中断信息parent_fwspec.param_count = 3;//描述层级关系是如何对应的parent_fwspec.param[0] = GIC_SPI;parent_fwspec.param[1] = fwspec->param[0] + upper_hwirq_base;parent_fwspec.param[2] = fwspec->param[1];/*1:函数会根据传入的参数,找到对应的父中断控制器,并在父中断控制器中为这些中断分配irq号2:配置父控制器的irq_desc(包括handle_irq和irq_chip)3:完成中断链路的配置(子控制器中断能够通过父控制器进行处理)//调用domain->parent的alloc函数继续设置*/return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec);
}static const struct irq_domain_ops virt_intc_domain_ops = {.translate = virt_intc_domain_translate, .alloc = virt_intc_domain_alloc,
};static int virt_intc_probe(struct platform_device *pdev)
{struct device_node *parent_node;struct irq_domain *parent_domain;of_property_read_u32(node, "upper_hwirq_base", &upper_hwirq_base);printk("%s %d upper_hwirq_base = %d\n", __FUNCTION__, __LINE__, upper_hwirq_base);parent_node = of_irq_find_parent(pdev->dev.of_node);parent_domain = irq_find_host(parent_node);virt_intc_domain = irq_domain_add_hierarchy(parent_domain, 0, 4, pdev->dev.of_node, &virt_intc_domain_ops, NULL);printk("parent_domain = %px, virt_intc_domain = %px\n", parent_domain, virt_intc_domain);return 0;
}static void virt_intc_remove(struct platform_device *pdev)
{printk("%s %d\n", __FUNCTION__, __LINE__);
}static const struct of_device_id virt_intc_of_match[] = {{ .compatible = "hvirt_intc", },{ },
};static struct platform_driver virt_intc_driver = {.probe = virt_intc_probe,.remove = virt_intc_remove,.driver = {.name = "virt_intc",.of_match_table = of_match_ptr(virt_intc_of_match),}
};static int __init virt_intc_init(void)
{printk("%s %d\n", __FUNCTION__, __LINE__);return platform_driver_register(&virt_intc_driver);
}static void __exit virt_intc_exit(void)
{printk("%s %d\n", __FUNCTION__, __LINE__);platform_driver_unregister(&virt_intc_driver);
}module_init(virt_intc_init);
module_exit(virt_intc_exit);MODULE_AUTHOR("Tan xujia");
MODULE_LICENSE("Dual BSD/GPL");
/*
//虚拟中断控制器设备树(层级)
virt_intc:virt_intc {compatible = "hvirt_intc";interrupt-controller;#interrupt-cells = <2>;interrupt-parent = <&gic>; //intc有irq_domainupper_hwirq_base = <210>;
};参考:gpc.c arch\arm\mach-imx
*/
虚拟按键代码
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/init.h>
#include<linux/major.h>
#include<linux/irq.h>
#include<linux/interrupt.h>
#include<linux/kernel.h>
#include<linux/of_irq.h>
#include<linux/of_gpio.h>
#include<linux/gpio/consumer.h>
#include<linux/platform_device.h>
#include<linux/slab.h>struct gpio_key {int irq;int cnt;char name[20];
};static struct gpio_key gpio_keys[4];static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_key *g_key = dev_id;printk("%s %d name = %s, cnt = %d\n", __FUNCTION__, __LINE__, g_key->name, g_key->cnt++);return IRQ_HANDLED;
}static int gpio_key_probe(struct platform_device *pdev)
{//struct device_node *node = pdev->dev.of_node;int irq, err, i = 0;while(1) {//根据设备树这里能获取到4个虚拟中断号irq = platform_get_irq(pdev, i); //0-3, i等于4时退出if(irq < 0)break;//将虚拟中断号保存在这里gpio_keys[i].irq = irq;sprintf(gpio_keys[i].name, "gpio_key-%d", i);//注册中断err = devm_request_irq(&pdev->dev, gpio_keys[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpio_keys[i].name, &gpio_keys[i]);printk("err = %d, irq = %d\n", err, irq); /*16-19*/i++;}printk("%s %d\n", __FUNCTION__, __LINE__);return 0;
}static void gpio_key_remove(struct platform_device *pdev)
{printk("%s %d\n", __FUNCTION__, __LINE__);
}static const struct of_device_id gpio_key_of_match[] = {{ .compatible = "gpio_key", },{ },
};static struct platform_driver gpio_key_driver = {.probe = gpio_key_probe,.remove = gpio_key_remove,.driver = {.name = "gpio_key",.of_match_table = of_match_ptr(gpio_key_of_match),}
};static int __init gpio_key_init(void)
{printk("%s %d\n", __FUNCTION__, __LINE__);return platform_driver_register(&gpio_key_driver);
}static void __exit gpio_key_exit(void)
{printk("%s %d\n", __FUNCTION__, __LINE__);platform_driver_unregister(&gpio_key_driver);
}module_init(gpio_key_init);
module_exit(gpio_key_exit);MODULE_AUTHOR("Tan xujia");
MODULE_LICENSE("Dual BSD/GPL");//虚拟按键设备树(链式和层级)
/*
gpio_keys:gpio_keys {compatible = "gpio_key";interrupt-parent = <&virt_intc>; //virt_intc有irq_domaininterrupts = <0 IRQ_TYPE_LEVEL_HIGH>,<1 IRQ_TYPE_LEVEL_HIGH>,<2 IRQ_TYPE_LEVEL_HIGH>,<3 IRQ_TYPE_LEVEL_HIGH>;
};
*/
参考:https://blog.csdn.net/caiji0169/article/details/143862261