Re: [PATCH v2 2/4] ACPI: LoongArch: Add IOVT device entry table scanning

Bibo Mao <[email protected]> Thu, 23 Jul 2026 20:44:59 +0800
Newsgroups dev.linux.lists.acpica-devel,dev.linux.lists.loongarch,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>

On 2026/7/22 上午3:09, Rafael J. Wysocki (Intel) wrote:
> On Fri, Jul 17, 2026 at 8:44 AM Bibo Mao <[email protected]> wrote:
>>
>> Similar with acpi_arch_init()/acpi_arch_late_init(), here function
>> acpi_iovt_init()/acpi_iovt_late_init() is added. Function
>> acpi_iovt_init() is to enable PCI ACS function if IOMMU device exists
>> in IOVT table, and function acpi_iovt_late_init() is to scan IOVT
>> table, add IOMMU devices.
>>
>> Signed-off-by: Bibo Mao <[email protected]>
>> ---
>>   drivers/acpi/Kconfig            |   4 +
>>   drivers/acpi/loongarch/Kconfig  |   7 +
>>   drivers/acpi/loongarch/Makefile |   1 +
>>   drivers/acpi/loongarch/init.c   |   5 +
>>   drivers/acpi/loongarch/init.h   |   5 +
>>   drivers/acpi/loongarch/iovt.c   | 219 ++++++++++++++++++++++++++++++++
>>   include/acpi/actbl2.h           |   3 +
> 
> The actbl2.h change should go through upstream ACPICA, shouldn't it?
yes, will update these macro definitions in upstream ACPICA firstly.

Regards
Bibo Mao
> 
>>   7 files changed, 244 insertions(+)
>>   create mode 100644 drivers/acpi/loongarch/Kconfig
>>   create mode 100644 drivers/acpi/loongarch/init.h
>>   create mode 100644 drivers/acpi/loongarch/iovt.c
>>
>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
>> index f165d14cf61a..192e6f18eda1 100644
>> --- a/drivers/acpi/Kconfig
>> +++ b/drivers/acpi/Kconfig
>> @@ -550,6 +550,10 @@ if ARM64
>>   source "drivers/acpi/arm64/Kconfig"
>>   endif
>>
>> +if LOONGARCH
>> +source "drivers/acpi/loongarch/Kconfig"
>> +endif
>> +
>>   if RISCV
>>   source "drivers/acpi/riscv/Kconfig"
>>   endif
>> diff --git a/drivers/acpi/loongarch/Kconfig b/drivers/acpi/loongarch/Kconfig
>> new file mode 100644
>> index 000000000000..91ba3c35b9bf
>> --- /dev/null
>> +++ b/drivers/acpi/loongarch/Kconfig
>> @@ -0,0 +1,7 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +#
>> +# ACPI Configuration for LOONGARCH
>> +#
>> +
>> +config ACPI_IOVT
>> +       bool
>> diff --git a/drivers/acpi/loongarch/Makefile b/drivers/acpi/loongarch/Makefile
>> index d3764139dfaf..c338d3102fc1 100644
>> --- a/drivers/acpi/loongarch/Makefile
>> +++ b/drivers/acpi/loongarch/Makefile
>> @@ -1,2 +1,3 @@
>>   # SPDX-License-Identifier: GPL-2.0-only
>>   obj-y                                  += init.o
>> +obj-$(CONFIG_ACPI_IOVT)                        += iovt.o
>> diff --git a/drivers/acpi/loongarch/init.c b/drivers/acpi/loongarch/init.c
>> index b11aa5c7d928..e7272ee197c7 100644
>> --- a/drivers/acpi/loongarch/init.c
>> +++ b/drivers/acpi/loongarch/init.c
>> @@ -1,11 +1,16 @@
>>   // SPDX-License-Identifier: GPL-2.0-only
>>
>>   #include <linux/acpi.h>
>> +#include "init.h"
>>
>>   void __init acpi_arch_init(void)
>>   {
>> +       if (IS_ENABLED(CONFIG_ACPI_IOVT))
>> +               acpi_iovt_init();
>>   }
>>
>>   void __init acpi_arch_late_init(void)
>>   {
>> +       if (IS_ENABLED(CONFIG_ACPI_IOVT))
>> +               acpi_iovt_late_init();
>>   }
>> diff --git a/drivers/acpi/loongarch/init.h b/drivers/acpi/loongarch/init.h
>> new file mode 100644
>> index 000000000000..85e5197553ad
>> --- /dev/null
>> +++ b/drivers/acpi/loongarch/init.h
>> @@ -0,0 +1,5 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +#include <linux/init.h>
>> +
>> +void __init acpi_iovt_init(void);
>> +void __init acpi_iovt_late_init(void);
>> diff --git a/drivers/acpi/loongarch/iovt.c b/drivers/acpi/loongarch/iovt.c
>> new file mode 100644
>> index 000000000000..fb384664ba78
>> --- /dev/null
>> +++ b/drivers/acpi/loongarch/iovt.c
>> @@ -0,0 +1,219 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/pci.h>
>> +#include "init.h"
>> +
>> +struct iovt_device_entry {
>> +       struct list_head list;
>> +       int start_devid;
>> +       int end_devid;
>> +};
>> +
>> +struct iovt_fwnode {
>> +       struct list_head list;
>> +       struct fwnode_handle *fwnode;
>> +       int flag;
>> +       int segment;
>> +       int devid;
>> +       int nid;
>> +       struct list_head ep_list;
>> +};
>> +
>> +/* Root pointer to the mapped IOVT table */
>> +static LIST_HEAD(iovt_fwnode_list);
>> +static DEFINE_SPINLOCK(iovt_fwnode_lock);
>> +
>> +#ifdef CONFIG_PCI
>> +static void __init iovt_enable_acs(struct acpi_iovt_iommu *iommu)
>> +{
>> +       static bool acs_enabled __initdata;
>> +
>> +       if (acs_enabled)
>> +               return;
>> +
>> +       /* IOMMU V1 only supports PCI device management */
>> +       if ((iommu->header.type == ACPI_IOVT_IOMMU_V1) ||
>> +               (iommu->flags & (ACPI_IOVT_PCI_DEVICE | ACPI_IOVT_MAGAGE_BY_SEGMENT))) {
>> +               pci_request_acs();
>> +               acs_enabled = true;
>> +       }
>> +}
>> +#else
>> +static inline void iovt_enable_acs(struct acpi_iovt_iommu *iommu) { }
>> +#endif
>> +
>> +static int __init iovt_get_pci_iommu_fwnode(struct iovt_fwnode *np, u16 segment, u16 bdf)
>> +{
>> +       struct pci_dev *pdev;
>> +       struct fwnode_handle *fwnode;
>> +
>> +       pdev = pci_get_domain_bus_and_slot(segment, PCI_BUS_NUM(bdf), bdf & 0xff);
>> +       if (!pdev) {
>> +               pr_err("No PCI IOMMU found for segment 0x%x bdf 0x%x\n", segment, bdf);
>> +               return -ENODEV;
>> +       }
>> +
>> +       fwnode = dev_fwnode(&pdev->dev);
>> +       if (!fwnode) {
>> +               /*
>> +                * PCI devices aren't necessarily described by ACPI. Create a
>> +                * fwnode so the IOMMU subsystem can identify this device.
>> +                */
>> +               fwnode = acpi_alloc_fwnode_static();
>> +               if (!fwnode) {
>> +                       pci_dev_put(pdev);
>> +                       return -ENOMEM;
>> +               }
>> +               set_primary_fwnode(&pdev->dev, fwnode);
>> +       }
>> +
>> +       np->fwnode = dev_fwnode(&pdev->dev);
>> +       if (np->flag & ACPI_IOVT_PXM_VALID)
>> +               set_dev_node(&pdev->dev, np->nid);
>> +       pci_dev_put(pdev);
>> +       return 0;
>> +}
>> +
>> +static int __init iovt_add_iommu(struct acpi_iovt_iommu *iommu)
>> +{
>> +       struct iovt_fwnode *np;
>> +       struct fwnode_handle *fwnode;
>> +       struct acpi_iovt_device_entry *ep;
>> +       struct iovt_device_entry *entry;
>> +       int i, ret, start_devid;
>> +       bool is_start = false;
>> +
>> +       np = kzalloc_obj(struct iovt_fwnode, GFP_ATOMIC);
>> +       if (WARN_ON(!np))
>> +               return -ENOMEM;
>> +
>> +       INIT_LIST_HEAD(&np->list);
>> +       np->flag = iommu->flags;
>> +       np->segment = iommu->segment;
>> +       if (np->flag & ACPI_IOVT_PXM_VALID)
>> +               np->nid = pxm_to_node(iommu->proximity_domain);
>> +
>> +       if (np->flag & ACPI_IOVT_PCI_DEVICE) {
>> +               np->devid = iommu->device_id;
>> +               ret = iovt_get_pci_iommu_fwnode(np, np->segment, np->devid);
>> +               if (ret) {
>> +                       kfree(np);
>> +                       return ret;
>> +               }
>> +
>> +       } else {
>> +               fwnode = acpi_alloc_fwnode_static();
>> +               if (!fwnode) {
>> +                       kfree(np);
>> +                       return -ENOMEM;
>> +               }
>> +
>> +               np->fwnode = fwnode;
>> +       }
>> +
>> +       /* All devices in the segment are managed by this IOMMU */
>> +       if (np->flag & ACPI_IOVT_MAGAGE_BY_SEGMENT)
>> +               goto skip;
>> +
>> +       INIT_LIST_HEAD(&np->ep_list);
>> +       ep = ACPI_ADD_PTR(struct acpi_iovt_device_entry, iommu, iommu->device_entry_offset);
>> +       for (i = 0; i < iommu->device_entry_num; i++) {
>> +               switch (ep->type) {
>> +               case ACPI_IOVT_DEVICE_ENTRY_START:
>> +                       is_start = true;
>> +                       start_devid = ep->device_id;
>> +                       break;
>> +               case ACPI_IOVT_DEVICE_ENTRY_END:
>> +                       if (!is_start)
>> +                               break;
>> +
>> +                       entry = kzalloc_obj(struct iovt_device_entry, GFP_ATOMIC);
>> +                       if (!entry)
>> +                               return -ENOMEM;
>> +
>> +                       entry->start_devid = start_devid;
>> +                       entry->end_devid = ep->device_id;
>> +                       list_add_tail(&entry->list, &np->ep_list);
>> +                       is_start = false;
>> +                       break;
>> +               case ACPI_IOVT_DEVICE_ENTRY_SINGLE:
>> +                       entry = kzalloc_obj(struct iovt_device_entry, GFP_ATOMIC);
>> +                       if (!entry)
>> +                               return -ENOMEM;
>> +
>> +                       entry->start_devid = ep->device_id;
>> +                       entry->end_devid = ep->device_id;
>> +                       list_add_tail(&entry->list, &np->ep_list);
>> +                       is_start = false;
>> +                       break;
>> +               default:
>> +                       break;
>> +               }
>> +               ep = ACPI_ADD_PTR(struct acpi_iovt_device_entry, ep, ep->length);
>> +       }
>> +
>> +skip:
>> +       spin_lock(&iovt_fwnode_lock);
>> +       list_add_tail(&np->list, &iovt_fwnode_list);
>> +       spin_unlock(&iovt_fwnode_lock);
>> +       return 0;
>> +}
>> +
>> +static void __init iovt_init_devices(struct acpi_table_header *header)
>> +{
>> +       struct acpi_iovt_iommu *iommu;
>> +       struct acpi_table_iovt *iovt;
>> +       int i;
>> +
>> +       /* Get the first IOVT node */
>> +       iovt = (struct acpi_table_iovt *)header;
>> +       iommu = ACPI_ADD_PTR(struct acpi_iovt_iommu, iovt, iovt->iommu_offset);
>> +       for (i = 0; i < iovt->iommu_count; i++) {
>> +               iovt_add_iommu(iommu);
>> +               iommu = ACPI_ADD_PTR(struct acpi_iovt_iommu, iommu, iommu->header.length);
>> +       }
>> +}
>> +
>> +void __init acpi_iovt_init(void)
>> +{
>> +       acpi_status status;
>> +       struct acpi_table_header *hdr;
>> +       struct acpi_table_iovt *iovt;
>> +       struct acpi_iovt_iommu *iommu;
>> +       int i;
>> +
>> +       status = acpi_get_table(ACPI_SIG_IOVT, 0, &hdr);
>> +       if (ACPI_FAILURE(status)) {
>> +               if (status != AE_NOT_FOUND)
>> +                       pr_err("Failed to get table, %s\n", acpi_format_exception(status));
>> +
>> +               return;
>> +       }
>> +
>> +       iovt = (struct acpi_table_iovt *)&hdr;
>> +       iommu = ACPI_ADD_PTR(struct acpi_iovt_iommu, iovt, iovt->iommu_offset);
>> +       for (i = 0; i < iovt->iommu_count; i++) {
>> +               iovt_enable_acs(iommu);
>> +               iommu = ACPI_ADD_PTR(struct acpi_iovt_iommu, iommu, iommu->header.length);
>> +       }
>> +
>> +       acpi_put_table(hdr);
>> +}
>> +
>> +void __init acpi_iovt_late_init(void)
>> +{
>> +       acpi_status status;
>> +       struct acpi_table_header *hdr;
>> +
>> +       status = acpi_get_table(ACPI_SIG_IOVT, 0, &hdr);
>> +       if (ACPI_FAILURE(status)) {
>> +               if (status != AE_NOT_FOUND)
>> +                       pr_err("Failed to get table, %s\n", acpi_format_exception(status));
>> +
>> +               return;
>> +       }
>> +
>> +       iovt_init_devices(hdr);
>> +       acpi_put_table(hdr);
>> +}
>> diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
>> index baef525367b5..acc51a42c3ed 100644
>> --- a/include/acpi/actbl2.h
>> +++ b/include/acpi/actbl2.h
>> @@ -892,6 +892,9 @@ struct acpi_iovt_header {
>>
>>   /* Values for Type field above */
>>
>> +#define ACPI_IOVT_PCI_DEVICE           BIT(0)
>> +#define ACPI_IOVT_PXM_VALID            BIT(1)
>> +#define ACPI_IOVT_MAGAGE_BY_SEGMENT    BIT(2)
>>   enum acpi_iovt_iommu_type {
>>          ACPI_IOVT_IOMMU_V1 = 0x00,
>>          ACPI_IOVT_IOMMU_RESERVED = 0x01 /* 1 and greater are reserved */
>> --
>> 2.39.3
>>