Linux pcie【8】GIC-V3 ITS驱动

前面的文章《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里的回调函数等用到的时候再介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值