在使能了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
#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