首页 > 学院 > 开发设计 > 正文

smmu 学习笔记 之初始化

2019-11-07 23:43:49
字体:
来源:转载
供稿:网友
在使能了ACPI的情况下,会调用acpi_smmu_v3_init来初始化#ifdef CONFIG_ACPIstatic 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_V3bool 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_V3static 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.这个callbackstatic acpi_statusiort_match_type_callback(struct acpi_iort_node *node, void *context){    return AE_OK;}仅仅是返回ok而已。如果iort_node_match 返回非NULL的话,就调用arm_smmu_initstatic 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_probestatic 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_probestatic 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
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表