中斷映射的大體過程如下:
irq_of_parse_and_map
static int bcm2835_mbox_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret = 0; struct resource *iomem; struct bcm2835_mbox *mbox; mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); if (mbox == NULL) return -ENOMEM; spin_lock_init(&mbox->lock); ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0), bcm2835_mbox_irq, 0, dev_name(dev), mbox); if (ret) { dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", ret); return -ENODEV; }
irq_of_parse_and_map(dev->of_node, 0)這是我們比較常用的方法。 dev->of_node這是dts解析生成的結構體數據,0 這是中斷數組下標表示你要映射的第幾個中斷。現在來看源碼
1.irq_of_parse_and_map
unsigned int irq_of_parse_and_map(struct device_node *dev, int index) { struct of_phandle_args oirq; //解析一個irq,讀取其配置值 if (of_irq_parse_one(dev, index, &oirq)) return 0; //獲取映射后的irq return irq_create_of_mapping(&oirq); }
2.of_irq_parse_one int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) { struct device_node *p; const __be32 *intspec, *tmp, *addr; u32 intsize, intlen; int i, res; pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); /* OldWorld mac stuff is "special", handle out of line */ if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC) return of_irq_parse_oldworld(device, index, out_irq); /* Get the reg property (if any) */ addr = of_get_property(device, "reg", NULL); /* Try the new-style interrupts-extended first */ res = of_parse_phandle_with_args(device, "interrupts-extended", "#interrupt-cells", index, out_irq); if (!res) return of_irq_parse_raw(addr, out_irq); /* Get the interrupts property */ //獲取該設備的interrupts屬性,反正屬性值的地址,及數據大小 intspec = of_get_property(device, "interrupts", &intlen); if (intspec == NULL) return -EINVAL; //獲得數據個數 intlen /= sizeof(*intspec); pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen); /* Look for the interrupt parent. */ p = of_irq_find_parent(device); if (p == NULL) return -EINVAL; /* Get size of interrupt specifier */ //解析interrupt-cells 屬性,取得一個interrupt有幾個成員 tmp = of_get_property(p, "#interrupt-cells", NULL); if (tmp == NULL) { res = -EINVAL; goto out; } intsize = be32_to_cpu(*tmp); pr_debug(" intsize=%d intlen=%d\n", intsize, intlen); /* Check index */ //如果下標過大超過數據大小,將出錯 if ((index + 1) * intsize > intlen) { res = -EINVAL; goto out; } /* Copy intspec into irq structure */ 這里根據我們傳遞的數組下標作位移,index是具體下票,intsize為每個interrupt成員數據個數,也就是整數個數。指針按這個offset進行位移后,就會指向我們需要的中斷數據 intspec += index * intsize; out_irq->np = p; out_irq->args_count = intsize; for (i = 0; i < intsize; i++) //將這個具體的中斷屬性值保存到out_irq中 out_irq->args[i] = be32_to_cpup(intspec++); /* Check if there are any interrupt-map translations to process */ res = of_irq_parse_raw(addr, out_irq); out: of_node_put(p); return res; }
上面代碼主要目地就是從dts配置中獲取具體的中斷配置信息,獲取信息后下一步就是映射了
3.irq_create_of_mapping unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) { struct irq_fwspec fwspec; //下面這個函數就是數據轉移而已,都現成的數據保存到fwspec of_phandle_args_to_fwspec(irq_data, &fwspec); //映射 return irq_create_fwspec_mapping(&fwspec); } 4.irq_create_fwspec_mapping unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec) { struct irq_domain *domain; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; int virq; if (fwspec->fwnode) domain = irq_find_matching_fwnode(fwspec->fwnode, DOMAIN_BUS_ANY); else domain = irq_default_domain; //這里用的是irq_default_domain,這個是由外部進行注冊得到的。主要用來解析中斷配置 if (!domain) { pr_warn("no irq domain found for %s !\n", of_node_full_name(to_of_node(fwspec->fwnode))); return 0; } //調用注冊的irq_default_domain的回調函數得到硬件中斷和中斷的類型 if (irq_domain_translate(domain, fwspec, &hwirq, &type)) return 0; if (irq_domain_is_hierarchy(domain)) { /* * If we've already configured this interrupt, * don't do it again, or hell will break loose. */ virq = irq_find_mapping(domain, hwirq); if (virq) return virq; virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec); if (virq <= 0) return 0; } else { /* Create mapping */ //創建映射,下面具體來看看這個函數 virq = irq_create_mapping(domain, hwirq); if (!virq) return virq; } /* Set type if specified and different than the current one */ if (type != IRQ_TYPE_NONE && type != irq_get_trigger_type(virq)) //直譯中斷類型信息 irq_set_irq_type(virq, type); return virq; }
irq_find_mapping
當中斷經過中斷控制器到達CPU后,Linux會首先通過irq_find_mapping()函數,根據物理中斷號"hwirq"的值,查找上文講到的包含映射關系的radix tree或者線性數組,得到"hwirq"對應的虛擬中斷號"irq"。
unsigned int irq_find_mapping(struct irq_domain *domain, irq_hw_number_t hwirq)
{
struct irq_data *data;
//線性映射的查找
if (hwirq < domain->revmap_size)
return domain->linear_revmap[hwirq];
//radix tree映射的查找
rcu_read_lock();
data = radix_tree_lookup(&domain->revmap_tree, hwirq);
rcu_read_unlock();
return data ? data->irq : 0;
}
__irq_alloc_descs
irq_create_mapping
->irq_domain_alloc_descs
->irq_alloc_descs_from
->irq_alloc_descs
->__irq_alloc_descs
上面是具體的調用過程,只看最后是如何進行映射的
int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node, struct module *owner) { int start, ret; if (!cnt) return -EINVAL; if (irq >= 0) { if (from > irq) return -EINVAL; from = irq; } else { /* * For interrupts which are freely allocated the * architecture can force a lower bound to the @from * argument. x86 uses this to exclude the GSI space. */ from = arch_dynirq_lower_bound(from); } mutex_lock(&sparse_irq_lock); //這里就是從數組allocated_irqs中獲取一個沒有使用的下標作為映射的中斷號返回給驅動使用 start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS, from, cnt, 0); ret = -EEXIST; if (irq >=0 && start != irq) goto err; if (start + cnt > nr_irqs) { ret = irq_expand_nr_irqs(start + cnt); if (ret) goto err; } bitmap_set(allocated_irqs, start, cnt); mutex_unlock(&sparse_irq_lock); return alloc_descs(start, cnt, node, owner); err: mutex_unlock(&sparse_irq_lock); return ret; } 下面來看看allocated_irqs的定義 static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS); 宏定義位置:include/linux/types.h #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)]
根據代碼得知道,這個數組大小為129
通過以上分析得知,中斷映射就是將硬件中斷號與allocated_irqs數組下標建立對應關系,下面來看看DTS中中斷屬性的定義interrupts
gic: interrupt-controller@8d000000 {
compatible = "arm,gic-v3";
#interrupt-cells = <3>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
interrupt-controller;
#redistributor-regions = <1>;
redistributor-stride = <0x0 0x30000>;
reg = <0x0 0x8d000000 0 0x10000>, /* GICD */
<0x0 0x8d100000 0 0x300000>, /* GICR */
<0x0 0xfe000000 0 0x10000>, /* GICC */
<0x0 0xfe010000 0 0x10000>, /* GICH */
<0x0 0xfe020000 0 0x10000>; /* GICV */
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
its_totems: interrupt-controller@8c000000 {
compatible = "arm,gic-v3-its";
msi-controller;
reg = <0x0 0x8c000000 0x0 0x40000>;
};
};
根據驅動源碼里面的文檔描述來看
1.一般中斷必須包含一個interrupts屬性或者一個interrupts-extended屬性。或者兩者都有,如果兩者都有在解析的時候會優先解析。interrupts-extended這個屬性僅僅只能用於設備有多個中斷父節點
2.interrupt-parent 中斷父類,一般是interrupt-controller
3.interrupt-controller標記設備是一個中斷控制者
4.#interrupt-cells定義每個中斷配置有幾個成員
只有一個成員:
Example:
vic: intc@10140000 { compatible = "arm,versatile-vic";
interrupt-controller;
#interrupt-cells = <1>;
reg = <0x10140000 0x1000>; };
sic: intc@10003000 { compatible = "arm,versatile-sic";
interrupt-controller;
#interrupt-cells = <1>;
reg = <0x10003000 0x1000>;
interrupt-parent = <&vic>;
interrupts = <31>; /* Cascaded to vic */ };
代表硬件中斷號
兩個成員:
成員一代表:硬件中斷號 成員二代表中斷的類型
- bits[3:0] trigger type and level flags
- 1 = low-to-high edge triggered
- 2 = high-to-low edge triggered
- 4 = active high level-sensitive
- 8 = active low level-sensitive
Example:
i2c@7000c000 { gpioext: gpio-adnp@41 { compatible = "ad,gpio-adnp";
reg = <0x41>;
interrupt-parent = <&gpio>;
interrupts = <160 1>;
gpio-controller;
#gpio-cells = <1>;
interrupt-controller;
#interrupt-cells = <2>;
nr-gpios = <64>; };
sx8634@2b { compatible = "smtc,sx8634";
reg = <0x2b>;
interrupt-parent = <&gpioext>;
interrupts = <3 0x8>;
#address-cells = <1>;
#size-cells = <0>;
threshold = <0x40>;
sensitivity = <7>; }; };
三個成員的要看上面irq_default_domain注冊的函數具體是怎么解析的,對於gic來說。
成員一代表使用GIC的方式,成員二代表硬件中斷號,成員三代表中斷類型
if (intspec[0] == GIC_SHARED)
*out_hwirq = GIC_SHARED_TO_HWIRQ(intspec[1]);
else if (intspec[0] == GIC_LOCAL)
*out_hwirq = GIC_LOCAL_TO_HWIRQ(intspec[1]);
else
return -EINVAL;
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
當然還有其它的定義,比如下面的
第一個值:中斷號
第二個值:觸發的類型
第三個值:優先級,0級是最高的,7級是最低的;其中0級的中斷系統當做 FIQ處理。
具體是怎么定義的,要看驅動MakeFile文件參與編譯是哪個,根據宏開關來確定注冊的domain源文件,然后看去解析方式來確認具體的含義
