直接貼代碼了:
虛擬中斷控制器代碼,chained_virt.c
#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>static struct irq_domain *virt_intc_domain;static void virt_intc_irq_ack(struct irq_data *d)
{//ffff800080b68be0, ffff000101d37100, ffff800080717500printk("irq = %d, hwirq = %ld, %px, %px, %px\n", d->irq, d->hwirq, d->chip, d->domain, d->domain->ops);printk("%s %d\n", __FUNCTION__, __LINE__);
}static void virt_intc_irq_mask(struct irq_data *d)
{printk("irq = %d, hwirq = %ld, %px, %px, %px\n", d->irq, d->hwirq, d->chip, d->domain, d->domain->ops);printk("%s %d\n", __FUNCTION__, __LINE__);
}static void virt_intc_irq_mask_ack(struct irq_data *d)
{printk("irq = %d, hwirq = %ld, %px, %px, %px\n", d->irq, d->hwirq, d->chip, d->domain, d->domain->ops);printk("%s %d\n", __FUNCTION__, __LINE__);
}static void virt_intc_unmask(struct irq_data *d)
{//ffff800080b68be0, ffff000101d37100, ffff800080717500printk("irq = %d, hwirq = %ld, %px, %px, %px\n", d->irq, d->hwirq, d->chip, d->domain, d->domain->ops);printk("%s %d\n", __FUNCTION__, __LINE__);
}/*在回調函數里加打印觀察調用順序,理解調用過程*/
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,
};/*
為virq(irq_desc)設置irq chip, highlevel irq-events handle和irq chip data*/
static int virt_intc_irq_map(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq)
{irq_set_chip_data(virq, domain->host_data);/*給virq提供處理函數,給irq_chip提供ops處理集,handle_level_irq里會調用request_irq注冊的函數()*/irq_set_chip_and_handler(virq, &virt_intc_irq_chip, handle_edge_irq);/* 調用過程如下:handle_level_irq->handle_irq_event->handle_irq_event_percpu->__handle_irq_event_percpu->action->handler(irq, action->dev_id);*///irq_set_nested_thread(virq, 1);//irq_set_noprobe(virq); //domain -> ffff000101d37100printk("%s %d, virq = %d, hwirq = %ld, doamin = %px\n", __FUNCTION__, __LINE__, virq, hwirq, domain);return 0;
}static const struct irq_domain_ops virt_intc_domain_ops = {.xlate = irq_domain_xlate_onecell, //解析設備樹,得到硬件中斷號和觸發類型.map = virt_intc_irq_map, //調用時機。創建或者更新hw_irq和virq關系時(創建irq_domain)
};static u8 virt_intc_get_hwirq(void)
{return get_random_u8() % 0x3;
}static void virt_intc_irq_handler(struct irq_desc *desc) //發生200號中斷時對應的15->irq_desc
{int hwirq;struct irq_chip *chip = irq_desc_get_chip(desc);printk("desc = %px, chip = %px\n", desc, chip);//gic mask/ack 200號中斷 調用gic的chip提供的回調函數處理chained_irq_enter(chip, desc); //鏈式處理函數的入口函數//分便是哪一個中斷(隨機數產生)hwirq = virt_intc_get_hwirq(); //下一級的設備樹解析得到的是0,1,2,3//ffff80008070e810printk("%s %d, hwirq = %d, %px\n", __FUNCTION__, __LINE__, hwirq, chip);/*根據gpio hwirq找到對應的virq, 依靠注冊的域virt_intc_domain調用其對應的handle_irq(handleC), 這里的handle應該是下一級提供的*/generic_handle_irq(irq_find_mapping(virt_intc_domain, hwirq)); //這里調用的就是virt_intc_irq_map函數里設置的handle_edge_irq//gic unmask 200號中斷 調用gic的chip提供的回調函數處理chained_irq_exit(chip, desc); //鏈式處理函數的退出函數
}static int virt_intc_probe(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node;int irq_to_parent;/*上一級是gic, 根據gic里irq_domain的ops解析設備樹得到hwirq和type并分配/找到irq_desc得到virq*/irq_to_parent = platform_get_irq(pdev, 0); //上一級是gic所以用gic的irq_domain_ops函數解析printk("irq_to_parent = %d\n", irq_to_parent); //15 hwirq = 200//為irq_desc[irq_to_parent].handle_irq設置回調函數(handleB)irq_set_chained_handler_and_data(irq_to_parent, virt_intc_irq_handler, NULL); //highlevel irq-events handler -> virt_intc_irq_handler#if 0//注冊virt_intc_domain時需要irq_base參數,這里先進行分配,返回的是描述符的下標(連續的4個)int irq_base = irq_alloc_descs(-1, 0, 4, numa_node_id());//為virt_intc_domain,irq_base(相當于virq和desc),hwirq(np里面有硬件中斷號描述信息)形成映射關系virt_intc_domain = irq_domain_add_legacy(np, 4, irq_base, 0, &virt_intc_domain_ops, NULL); //ffff000101d37100printk("%s %d virt_intc_domain = %px, irq_base = %d\n", __FUNCTION__, __LINE__, virt_intc_domain, irq_base);
#else/*Usage:a. dts:定義使用那個hwirqb. 內核解析設備樹時分配irq_des, 得到virqc. (hwirq, virq) =>存入domain下一級使用到interrupt-parent = <&virt_intc>時,使用virt_intc_domain_ops里的xlate函數解析設備樹*/virt_intc_domain = irq_domain_add_linear(np, 4, &virt_intc_domain_ops, NULL);
#endifreturn 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 = "virt_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");
//devmem 0xf0001218 w 0x00000100
/*
//虛擬中斷控制器設備樹(鏈式)
virt_intc:virt_intc {compatible = "virt_intc";interrupt-controller;#interrupt-cells = <2>;interrupt-parent = <&gic>; //intc有irq_domaininterrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>; //對應的gic中斷號就是200
};產生中斷的外設需定義:
1:interrupt-parent
2:interrupts中斷控制器需定義:
1:interrupt-controller
2:#interrupt-cells
*//*
對應關系:
irq_desc[15].handle_irq = virt_intc_irq_handler;
irq_desc[15].irq_data.chip = gic_irq_chip;
irq_desc[15].irq_data.domain = gic_domain;
irq_desc[15].irq_data.domain.ops = gic_domain_ops;
irq_desc[15].irq_data.hwirq = 200;
irq_desc[15].irq_data.irq = 15;irq_desc[16].handle_irq = handle_edge_irq;
irq_desc[16].irq_data.chip = virt_intc_irq_chip;
irq_desc[16].irq_data.domain = virt_intc_domain;
irq_desc[16].irq_data.domain.ops = virt_intc_domain_ops;
irq_desc[16].irq_data.hwirq = 0;
irq_desc[16].irq_data.irq = 16;irq_desc[17].handle_irq = handle_edge_irq;
irq_desc[17].irq_data.chip = virt_intc_irq_chip;
irq_desc[17].irq_data.domain = virt_intc_domain;
irq_desc[17].irq_data.domain.ops = virt_intc_domain_ops;
irq_desc[17].irq_data.hwirq = 1;
irq_desc[17].irq_data.irq = 17;irq_desc[18].handle_irq = handle_edge_irq;
irq_desc[18].irq_data.chip = virt_intc_irq_chip;
irq_desc[18].irq_data.domain = virt_intc_domain;
irq_desc[18].irq_data.domain.ops = virt_intc_domain_ops;
irq_desc[18].irq_data.hwirq = 2;
irq_desc[18].irq_data.irq = 18;irq_desc[19].handle_irq = handle_edge_irq;
irq_desc[19].irq_data.chip = virt_intc_irq_chip;
irq_desc[19].irq_data.domain = virt_intc_domain;
irq_desc[19].irq_data.domain.ops = virt_intc_domain_ops;
irq_desc[19].irq_data.hwirq = 3;
irq_desc[19].irq_data.irq = 19;
*/
gpio按鍵代碼:gpio_key.c
#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