前面的文章《GIC-V3 ITS介绍》对ITS的模块硬件进行了介绍。本文主要介绍一下linux里ITS的驱动,为后面介绍PCIE如果通过ITS发送MSI/MSIX中断做铺垫。本文着重介绍代码的梗概,不会陷入具体的细节中。kernel版本为6.6。
its_init为整个its驱动的入口,gic-v3驱动会调用到这里。这里只介绍dts的方式,5667调用its_of_probe来初始ITS。5676用来创建Redistribution使用的两个LPI table。
5653 int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
5654 struct irq_domain *parent_domain)
5655 {
5656 struct device_node *of_node;
5657 struct its_node *its;
5658 bool has_v4 = false;
5659 bool has_v4_1 = false;
5660 int err;
5661
5662 gic_rdists = rdists;
5663
5664 its_parent = parent_domain;
5665 of_node = to_of_node(handle);
5666 if (of_node)
5667 its_of_probe(of_node);
5668 else
5669 its_acpi_probe();
5670
5671 if (list_empty(&its_nodes)) {
5672 pr_warn("ITS: No ITS available, not enabling LPIs\n");
5673 return -ENXIO;
5674 }
5675
5676 err = allocate_lpi_tables();
5677 if (err)
5678 return err;
5679
5680 list_for_each_entry(its, &its_nodes, entry) {
5681 has_v4 |= is_v4(its);
5682 has_v4_1 |= is_v4_1(its);
5683 }
5684
5685 /* Don't bother with inconsistent systems */
5686 if (WARN_ON(!has_v4_1 && rdists->has_rvpeid))
5687 rdists->has_rvpeid = false;
5688
5689 if (has_v4 & rdists->has_vlpis) {
5690 const struct irq_domain_ops *sgi_ops;
5691
5692 if (has_v4_1)
5693 sgi_ops = &its_sgi_domain_ops;
5694 else
5695 sgi_ops = NULL;
5696
5697 if (its_init_vpe_domain() ||
5698 its_init_v4(parent_domain, &its_vpe_domain_ops, sgi_ops)) {
5699 rdists->has_vlpis = false;
5700 pr_err("ITS: Disabling GICv4 support\n");
5701 }
5702 }
5703
5704 register_syscore_ops(&its_syscore_ops);
5705
5706 return 0;
5707 }
下面看一下its_of_probe,5432行主要是根据dts创建并初始化一个struct its_node。5436行调用its_probe_one,probe一个its。
5390 static int __init its_of_probe(struct device_node *node)
5391 {
5392 struct device_node *np;
5393 struct resource res;
5394 int err;
5395
5396 /*
5397 * Make sure *all* the ITS are reset before we probe any, as
5398 * they may be sharing memory. If any of the ITS fails to
5399 * reset, don't even try to go any further, as this could
5400 * result in something even worse.
5401 */
5402 for (np = of_find_matching_node(node, its_device_id); np;
5403 np = of_find_matching_node(np, its_device_id)) {
5404 if (!of_device_is_available(np) ||
5405 !of_property_read_bool(np, "msi-controller") ||
5406 of_address_to_resource(np, 0, &res))
5407 continue;
5408
5409 err = its_reset_one(&res);
5410 if (err)
5411 return err;
5412 }
5413
5414 for (np = of_find_matching_node(node, its_device_id); np;
5415 np = of_find_matching_node(np, its_device_id)) {
5416 struct its_node *its;
5417
5418 if (!of_device_is_available(np))
5419 continue;
5420 if (!of_property_read_bool(np, "msi-controller")) {
5421 pr_warn("%pOF: no msi-controller property, ITS ignored\n",
5422 np);
5423 continue;
5424 }
5425
5426 if (of_address_to_resource(np, 0, &res)) {
5427 pr_warn("%pOF: no regs?\n", np);
5428 continue;
5429 }
5430
5431
5432 its = its_node_init(&res, &np->fwnode, of_node_to_nid(np));
5433 if (!its)
5434 return -ENOMEM;
5435
5436 err = its_probe_one(its);
5437 if (err) {
5438 its_node_destroy(its);
5439 return err;
5440 }
5441 }
5442 return 0;
5443 }
再继续看its_probe_one,5115-5122申请command queue内存,5124-5131申请ITS使用的table内存。5167行调用its_init_domain,主要是将EventId和deviceId转换为INTID,所以这里要为它创建一个domain。
5076 static int __init its_probe_one(struct its_node *its)
5077 {
5078 u64 baser, tmp;
5079 struct page *page;
5080 u32 ctlr;
5081 int err;
5082
5083 its_enable_quirks(its);
5084
5085 if (is_v4(its)) {
5086 if (!(its->typer & GITS_TYPER_VMOVP)) {
5087 err = its_compute_its_list_map(its);
5088 if (err < 0)
5089 goto out;
5090
5091 its->list_nr = err;
5092
5093 pr_info("ITS@%pa: Using ITS number %d\n",
5094 &its->phys_base, err);
5095 } else {
5096 pr_info("ITS@%pa: Single VMOVP capable\n", &its->phys_base);
5097 }
5098
5099 if (is_v4_1(its)) {
5100 u32 svpet = FIELD_GET(GITS_TYPER_SVPET, its->typer);
5101
5102 its->sgir_base = ioremap(its->phys_base + SZ_128K, SZ_64K);
5103 if (!its->sgir_base) {
5104 err = -ENOMEM;
5105 goto out;
5106 }
5107
5108 its->mpidr = readl_relaxed(its->base + GITS_MPIDR);
5109
5110 pr_info("ITS@%pa: Using GICv4.1 mode %08x %08x\n",
5111 &its->phys_base, its->mpidr, svpet);
5112 }
5113 }
5114
5115 page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO,
5116 get_order(ITS_CMD_QUEUE_SZ));
5117 if (!page) {
5118 err = -ENOMEM;
5119 goto out_unmap_sgir;
5120 }
5121 its->cmd_base = (void *)page_address(page);
5122 its->cmd_write = its->cmd_base;
5123
5124 err = its_alloc_tables(its);
5125 if (err)
5126 goto out_free_cmd;
5127
5128 err = its_alloc_collections(its);
5129 if (err)
5130 goto out_free_tables;
5131
5132 baser = (virt_to_phys(its->cmd_base) |
5133 GITS_CBASER_RaWaWb |
5134 GITS_CBASER_InnerShareable |
5135 (ITS_CMD_QUEUE_SZ / SZ_4K - 1) |
5136 GITS_CBASER_VALID);
5137
5138 gits_write_cbaser(baser, its->base + GITS_CBASER);
5139 tmp = gits_read_cbaser(its->base + GITS_CBASER);
5140
5141 if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE)
5142 tmp &= ~GITS_CBASER_SHAREABILITY_MASK;
5143
5144 if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) {
5145 if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) {
5146 /*
5147 * The HW reports non-shareable, we must
5148 * remove the cacheability attributes as
5149 * well.
5150 */
5151 baser &= ~(GITS_CBASER_SHAREABILITY_MASK |
5152 GITS_CBASER_CACHEABILITY_MASK);
5153 baser |= GITS_CBASER_nC;
5154 gits_write_cbaser(baser, its->base + GITS_CBASER);
5155 }
5156 pr_info("ITS: using cache flushing for cmd queue\n");
5157 its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING;
5158 }
5159
5160 gits_write_cwriter(0, its->base + GITS_CWRITER);
5161 ctlr = readl_relaxed(its->base + GITS_CTLR);
5162 ctlr |= GITS_CTLR_ENABLE;
5163 if (is_v4(its))
5164 ctlr |= GITS_CTLR_ImDe;
5165 writel_relaxed(ctlr, its->base + GITS_CTLR);
5166
5167 err = its_init_domain(its);
5168 if (err)
5169 goto out_free_tables;
5170
5171 raw_spin_lock(&its_lock);
5172 list_add(&its->entry, &its_nodes);
5173 raw_spin_unlock(&its_lock);
5174
5175 return 0;
5176
5177 out_free_tables:
5178 its_free_tables(its);
5179 out_free_cmd:
5180 free_pages((unsigned long)its->cmd_base, get_order(ITS_CMD_QUEUE_SZ));
5181 out_unmap_sgir:
5182 if (its->sgir_base)
5183 iounmap(its->sgir_base);
5184 out:
5185 pr_err("ITS@%pa: failed probing (%d)\n", &its->phys_base, err);
5186 return err;
5187 }
介绍its_init_domain之前我们先看一下外设发送的MSI中断是如何发送到cpu的。

那么kernel是如何抽象并管理这些中断控制器的呢, 首先kernel会抽象了3层hierarchy irq_domain, 这些domain都是将INTID转化为virq, MSI/MSIX的INTID是将EventId通过dts配置的msi-map转换过来的。我们PCI设备驱动申请的中断是通过MSI/MSIX domain申请的,其对应的irq_data会指向MSI/MSIXdomain,同时为该irq_data申请parent irq_data,并各自指向自己的domain。每个domain都会有一个irq_chip用来定义对中断的mask等一系列操作。每层的irq_data会分别指向自己的irq_chip。

下面我们再看一下its_init_domain,主要就是调用irq_domain_create_hierarchy创建了一个irq_domain。
4974 static int its_init_domain(struct its_node *its)
4975 {
4976 struct irq_domain *inner_domain;
4977 struct msi_domain_info *info;
4978
4979 info = kzalloc(sizeof(*info), GFP_KERNEL);
4980 if (!info)
4981 return -ENOMEM;
4982
4983 info->ops = &its_msi_domain_ops;
4984 info->data = its;
4985
4986 inner_domain = irq_domain_create_hierarchy(its_parent,
4987 its->msi_domain_flags, 0,
4988 its->fwnode_handle, &its_domain_ops,
4989 info);
4990 if (!inner_domain) {
4991 kfree(info);
4992 return -ENOMEM;
4993 }
4994
4995 irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
4996
4997 return 0;
4998 }
我们就先介绍到这里,irq_domain里的回调函数等用到的时候再介绍。

512

被折叠的 条评论
为什么被折叠?



