smmu学习笔记之从acpi中parse irq资源

在acpi_iort_init->iort_init_platform_devices 中会遍历iort_table 中的node,然后调用iort_add_smmu_platform_device 将irq 资源添加给device,最终会在smmu的probe函数中使用这里添加的irq资源.
static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)
{
    struct fwnode_handle *fwnode;
    struct platform_device *pdev;
    struct resource *r;
    enum dev_dma_attr attr;
    int ret, count;
    const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);

    if (!ops)
        return -ENODEV;

    pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
    if (!pdev)
        return PTR_ERR(pdev);

    count = ops->iommu_count_resources(node);

    r = kcalloc(count, sizeof(*r), GFP_KERNEL);
    if (!r) {
        ret = -ENOMEM;
        goto dev_put;
    }

    ops->iommu_init_resources(r, node);

    ret = platform_device_add_resources(pdev, r, count);
    /*
     * Resources are duplicated in platform_device_add_resources,
     * free their allocated memory
     */
    kfree(r);

    if (ret)
        goto dev_put;

    /*
     * Add a copy of IORT node pointer to platform_data to
     * be used to retrieve IORT data information.
     */
    ret = platform_device_add_data(pdev, &node, sizeof(node));
    if (ret)
        goto dev_put;

    /*
     * We expect the dma masks to be equivalent for
     * all SMMUs set-ups
     */
    pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;

    fwnode = iort_get_fwnode(node);

    if (!fwnode) {
        ret = -ENODEV;
        goto dev_put;
    }

    pdev->dev.fwnode = fwnode;

    attr = ops->iommu_is_coherent(node) ?
                 DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;

    /* Configure DMA for the page table walker */
    acpi_dma_configure(&pdev->dev, attr);

    ret = platform_device_add(pdev);
    if (ret)
        goto dma_deconfigure;

    return 0;

dma_deconfigure:
    acpi_dma_deconfigure(&pdev->dev);
dev_put:
    platform_device_put(pdev);

    return ret;
}
在iort_add_smmu_platform_device 中首先通过iort_get_iommu_cfg 得到iort_iommu_config *ops
static const struct iort_iommu_config iort_arm_smmu_v3_cfg __initconst = {
    .name = "arm-smmu-v3",
    .iommu_is_coherent = arm_smmu_v3_is_coherent,
    .iommu_count_resources = arm_smmu_v3_count_resources,
    .iommu_init_resources = arm_smmu_v3_init_resources
};


static __init
const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
{
    switch (node->type) {
    case ACPI_IORT_NODE_SMMU_V3:
        return &iort_arm_smmu_v3_cfg;
    case ACPI_IORT_NODE_SMMU:
        return &iort_arm_smmu_cfg;
    default:
        return NULL;
    }
}
根据node->type,决定用smmu还是smmuv3,我们这里以ACPI_IORT_NODE_SMMU_V3为例,所以返回iort_arm_smmu_v3_cfg
然后通过    pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
申请一个platform_device *pdev。
然后调用count = ops->iommu_count_resources(node);来计算resource的数量,由于是smmuv3,因此调用arm_smmu_v3_count_resources
static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node)
{
    struct acpi_iort_smmu_v3 *smmu;
    /* Always present mem resource */
    int num_res = 1;

    /* Retrieve SMMUv3 specific data */
    smmu = (struct acpi_iort_smmu_v3 *)node->node_data;

    if (smmu->event_gsiv)
        num_res++;

    if (smmu->pri_gsiv)
        num_res++;

    if (smmu->gerr_gsiv)
        num_res++;

    if (smmu->sync_gsiv)
        num_res++;

    return num_res;
}
从arm_smmu_v3_count_resources 中可以看出资源总共有四种event_gsiv/pri_gsiv/gerr_gsiv/sync_gsiv,这些信息是存在是存在platfrom_data中的,这个可以参考之前的博文.
回到iort_add_smmu_platform_device中继续调用ops->iommu_init_resources(r, node);来初始resource
static void __init arm_smmu_v3_init_resources(struct resource *res,
                          struct acpi_iort_node *node)
{
    struct acpi_iort_smmu_v3 *smmu;
    int num_res = 0;

    /* Retrieve SMMUv3 specific data */
    smmu = (struct acpi_iort_smmu_v3 *)node->node_data;

    res[num_res].start = smmu->base_address;
    res[num_res].end = smmu->base_address + SZ_128K - 1;
    res[num_res].flags = IORESOURCE_MEM;

    num_res++;

    if (smmu->event_gsiv)
        acpi_iort_register_irq(smmu->event_gsiv, "eventq",
                       ACPI_EDGE_SENSITIVE,
                       &res[num_res++]);

    if (smmu->pri_gsiv)
        acpi_iort_register_irq(smmu->pri_gsiv, "priq",
                       ACPI_EDGE_SENSITIVE,
                       &res[num_res++]);

    if (smmu->gerr_gsiv)
        acpi_iort_register_irq(smmu->gerr_gsiv, "gerror",
                       ACPI_EDGE_SENSITIVE,
                       &res[num_res++]);

    if (smmu->sync_gsiv)
        acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync",
                       ACPI_EDGE_SENSITIVE,
                       &res[num_res++]);
}
在arm_smmu_v3_init_resources 中会对res中的num_res 的start/end/flags 来赋值,这里的flag赋值的是IORESOURCE_MEM
然后调用acpi_iort_register_irq 来准备irq
static void __init acpi_iort_register_irq(int hwirq, const char *name,
                      int trigger,
                      struct resource *res)
{
    int irq = acpi_register_gsi(NULL, hwirq, trigger,
                    ACPI_ACTIVE_HIGH);

    if (irq <= 0) {
        pr_err("could not register gsi hwirq %d name [%s]\n", hwirq,
                                      name);
        return;
    }

    res->start = irq;
    res->end = irq;
    res->flags = IORESOURCE_IRQ;
    res->name = name;
}

这里继续对res赋值,注意这里的flags已经被换成IORESOURCE_IRQ了.
回到iort_add_smmu_platform_device中然后通过platform_device_add_resources 来添加资源
int platform_device_add_resources(struct platform_device *pdev,
                  const struct resource *res, unsigned int num)
{
    struct resource *r = NULL;

    if (res) {
        r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL);
        if (!r)
            return -ENOMEM;
    }

    kfree(pdev->resource);
    pdev->resource = r;
    pdev->num_resources = num;
    return 0;
}


这样我们在arm_smmu_device_probe 函数中就可以通过platform_get_irq_byname 来得到中断号
    irq = platform_get_irq_byname(pdev, "eventq");

int platform_get_irq_byname(struct platform_device *dev, const char *name)
{
    struct resource *r;

    if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
        int ret;

        ret = of_irq_get_byname(dev->dev.of_node, name);
        if (ret > 0 || ret == -EPROBE_DEFER)
            return ret;
    }

    r = platform_get_resource_byname(dev, IORESOURCE_IRQ, name);
    return r ? r->start : -ENXIO;
}

由于我们用的是acpi因此直接调用platform_get_resource_byname,注意这里的type是IORESOURCE_IRQ
struct resource *platform_get_resource_byname(struct platform_device *dev,
                          unsigned int type,
                          const char *name)
{
    int i;

    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];

        if (unlikely(!r->name))
            continue;

        if (type == resource_type(r) && !strcmp(r->name, name))
            return r;
    }
    return NULL;
}
从platform_get_resource_byname 中可见会遍历dev->resource,然后比较type和name相等的话,就认为已经匹配到了,而dev->resource 就是前面分析的是通过platform_device_add_resources 来添加的.


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值