smmu 学习笔记 之初始化

来源:互联网 发布:淘宝导购网站 编辑:程序博客网 时间:2024/04/29 17:33
在使能了ACPI的情况下,会调用acpi_smmu_v3_init来初始化
#ifdef CONFIG_ACPI
static int __init acpi_smmu_v3_init(struct acpi_table_header *table)
{
    if (iort_node_match(ACPI_IORT_NODE_SMMU_V3))
        return arm_smmu_init();

    return 0;
}
IORT_ACPI_DECLARE(arm_smmu_v3, ACPI_SIG_IORT, acpi_smmu_v3_init);
#endif
在acpi_smmu_v3_init 中首先调用iort_node_match来能否找到匹配的ACPI_IORT_NODE_SMMU_V3
bool iort_node_match(u8 type)
{
    struct acpi_iort_node *node;

    node = iort_scan_node(type, iort_match_type_callback, NULL);

    return node != NULL;
}
可见如果node 不是NULL 就返回true,也就是找到了ACPI_IORT_NODE_SMMU_V3
static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
                         iort_find_node_callback callback,
                         void *context)
{
    struct acpi_iort_node *iort_node, *iort_end;
    struct acpi_table_iort *iort;
    int i;

    if (!iort_table)
        return NULL;

    /* Get the first IORT node */
    iort = (struct acpi_table_iort *)iort_table;
    iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
                 iort->node_offset);
    iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
                iort_table->length);

    for (i = 0; i < iort->node_count; i++) {
        if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
                   "IORT node pointer overflows, bad table!\n"))
            return NULL;

        if (iort_node->type == type &&
            ACPI_SUCCESS(callback(iort_node, context)))
            return iort_node;

        iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
                     iort_node->length);
    }

    return NULL;
}
在iort_scan_node 中首先得到iort的起始和结束
    iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
                 iort->node_offset);
    iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
                iort_table->length);
然后通过for循环遍历这段memory,然后找到iort_node->type 是否等于ACPI_IORT_NODE_SMMU_V3,如果等于的话,就调用callback.这个callback
static acpi_status
iort_match_type_callback(struct acpi_iort_node *node, void *context)
{
    return AE_OK;
}
仅仅是返回ok而已。
如果iort_node_match 返回非NULL的话,就调用arm_smmu_init
static struct platform_driver arm_smmu_driver = {
    .driver    = {
        .name        = "arm-smmu-v3",
        .of_match_table    = of_match_ptr(arm_smmu_of_match),
    },
    .probe    = arm_smmu_device_probe,
    .remove    = arm_smmu_device_remove,
};

static int __init arm_smmu_init(void)
{
    static bool registered;
    int ret = 0;

    if (!registered) {
        ret = platform_driver_register(&arm_smmu_driver);
        registered = !ret;
    }
    return ret;
}
arm_smmu_init 调用platform_driver_register 注册driver,因为ACPI已经注册device了,然后就调用arm_smmu_device_probe
static int arm_smmu_device_probe(struct platform_device *pdev)
{
    int irq, ret;
    struct resource *res;
    struct arm_smmu_device *smmu;
    struct device *dev = &pdev->dev;
    bool bypass;

    smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
    if (!smmu) {
        dev_err(dev, "failed to allocate arm_smmu_device\n");
        return -ENOMEM;
    }
    smmu->dev = dev;

    /* Base address */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (resource_size(res) + 1 < SZ_128K) {
        dev_err(dev, "MMIO region too small (%pr)\n", res);
        return -EINVAL;
    }

    smmu->base = devm_ioremap_resource(dev, res);
    if (IS_ERR(smmu->base))
        return PTR_ERR(smmu->base);

    /* Interrupt lines */
    irq = platform_get_irq_byname(pdev, "eventq");
    if (irq > 0)
        smmu->evtq.q.irq = irq;

    irq = platform_get_irq_byname(pdev, "priq");
    if (irq > 0)
        smmu->priq.q.irq = irq;

    irq = platform_get_irq_byname(pdev, "cmdq-sync");
    if (irq > 0)
        smmu->cmdq.q.irq = irq;

    irq = platform_get_irq_byname(pdev, "gerror");
    if (irq > 0)
        smmu->gerr_irq = irq;

    if (dev->of_node)
        ret = arm_smmu_device_dt_probe(pdev, smmu, &bypass);
    else
        ret = arm_smmu_device_acpi_probe(pdev, smmu, &bypass);

    if (ret)
        return ret;

    /* Probe the h/w */
    ret = arm_smmu_device_hw_probe(smmu);
    if (ret)
        return ret;

    /* Initialise in-memory data structures */
    ret = arm_smmu_init_structures(smmu);
    if (ret)
        return ret;

    /* Record our private device structure */
    platform_set_drvdata(pdev, smmu);

    /* Reset the device */
    ret = arm_smmu_device_reset(smmu, bypass);
    if (ret)
        return ret;

    /* And we're up. Go go go! */
    fwnode_iommu_set_ops(dev->fwnode, &arm_smmu_ops);

#ifdef CONFIG_PCI
    if (pci_bus_type.iommu_ops != &arm_smmu_ops) {
        pci_request_acs();
        ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
        if (ret)
            return ret;
    }
#endif
#ifdef CONFIG_ARM_AMBA
    if (amba_bustype.iommu_ops != &arm_smmu_ops) {
        ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops);
        if (ret)
            return ret;
    }
#endif
    if (platform_bus_type.iommu_ops != &arm_smmu_ops) {
        ret = bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
        if (ret)
            return ret;
    }
    return 0;
}

首先申请arm_smmu_device 的空间
    smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
    if (!smmu) {
        dev_err(dev, "failed to allocate arm_smmu_device\n");
        return -ENOMEM;
    }
然后就得到四个中断号
    irq = platform_get_irq_byname(pdev, "eventq");
    if (irq > 0)
        smmu->evtq.q.irq = irq;

    irq = platform_get_irq_byname(pdev, "priq");
    if (irq > 0)
        smmu->priq.q.irq = irq;

    irq = platform_get_irq_byname(pdev, "cmdq-sync");
    if (irq > 0)
        smmu->cmdq.q.irq = irq;

    irq = platform_get_irq_byname(pdev, "gerror");
    if (irq > 0)
        smmu->gerr_irq = irq;

由于我们是APCI 模式,所以dev->of_node为null,因此调用arm_smmu_device_acpi_probe
static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
                      struct arm_smmu_device *smmu,
                      bool *bypass)
{
    struct acpi_iort_smmu_v3 *iort_smmu;
    struct device *dev = smmu->dev;
    struct acpi_iort_node *node;

    node = *(struct acpi_iort_node **)dev_get_platdata(dev);

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

    if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE)
        smmu->features |= ARM_SMMU_FEAT_COHERENCY;

    *bypass = false;

    return 0;
}

arm_smmu_device_acpi_probe 只是判定是否有enable ACPI_IORT_SMMU_V3_COHACC_OVERRIDE 这个feature而已。
arm_smmu_device_hw_probe 函数也是判断是否有enable相关hw,有的话,就置位相关flag。
arm_smmu_init_structures 中有分两个函数初始化
static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
{
    int ret;

    ret = arm_smmu_init_queues(smmu);
    if (ret)
        return ret;

    return arm_smmu_init_strtab(smmu);
}
static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
{
    int ret;

    /* cmdq */
    spin_lock_init(&smmu->cmdq.lock);
    ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD,
                      ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS);
    if (ret)
        return ret;

    /* evtq */
    ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD,
                      ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS);
    if (ret)
        return ret;

    /* priq */
    if (!(smmu->features & ARM_SMMU_FEAT_PRI))
        return 0;

    return arm_smmu_init_one_queue(smmu, &smmu->priq.q, ARM_SMMU_PRIQ_PROD,
                       ARM_SMMU_PRIQ_CONS, PRIQ_ENT_DWORDS);
}

arm_smmu_init_queues 中主要是调用arm_smmu_init_one_queue 来填充cmdq/evtq/priq 这三个数据结构.
arm_smmu_init_strtab 中也是根据ARM_SMMU_FEAT_2_LVL_STRTAB 来判断是用arm_smmu_init_strtab_2lvl 还是 arm_smmu_init_strtab_linear ,这两个函数都填充相关结构体。
fwnode_iommu_set_ops 则是申请iommu_fwentry 结构,其中iommu_fwentry中的一项就代表iommu操作的ops,然后将iommu_fwentry 保存到iommu_fwentry_list中.
最后分别初始化三个总线,pci_bus_type/amba_bustype/platform_bus_type
0 0