Re: [PATCH v2 2/4] ACPI: LoongArch: Add IOVT device entry table scanning
"Rafael J. Wysocki (Intel)" <[email protected]> Tue, 21 Jul 2026 21:09:07 +0200
| Newsgroups | dev.linux.lists.acpica-devel,dev.linux.lists.loongarch,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAJZ5v0iN6qP0WMXRoC5AXTN8RLG=ZQpnen_2HbbAXvTx=0jfVQ@mail.gmail.com> |
On Fri, Jul 17, 2026 at 8:44=E2=80=AFAM Bibo Mao <[email protected]> wrot= e: > > 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? > 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/Kcon= fig > 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/Mak= efile > 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 +=3D init.o > +obj-$(CONFIG_ACPI_IOVT) +=3D 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 =3D=3D ACPI_IOVT_IOMMU_V1) || > + (iommu->flags & (ACPI_IOVT_PCI_DEVICE | ACPI_IOVT_MAGAGE_= BY_SEGMENT))) { > + pci_request_acs(); > + acs_enabled =3D 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 =3D pci_get_domain_bus_and_slot(segment, PCI_BUS_NUM(bdf), b= df & 0xff); > + if (!pdev) { > + pr_err("No PCI IOMMU found for segment 0x%x bdf 0x%x\n", = segment, bdf); > + return -ENODEV; > + } > + > + fwnode =3D dev_fwnode(&pdev->dev); > + if (!fwnode) { > + /* > + * PCI devices aren't necessarily described by ACPI. Crea= te a > + * fwnode so the IOMMU subsystem can identify this device= . > + */ > + fwnode =3D acpi_alloc_fwnode_static(); > + if (!fwnode) { > + pci_dev_put(pdev); > + return -ENOMEM; > + } > + set_primary_fwnode(&pdev->dev, fwnode); > + } > + > + np->fwnode =3D 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 =3D false; > + > + np =3D kzalloc_obj(struct iovt_fwnode, GFP_ATOMIC); > + if (WARN_ON(!np)) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&np->list); > + np->flag =3D iommu->flags; > + np->segment =3D iommu->segment; > + if (np->flag & ACPI_IOVT_PXM_VALID) > + np->nid =3D pxm_to_node(iommu->proximity_domain); > + > + if (np->flag & ACPI_IOVT_PCI_DEVICE) { > + np->devid =3D iommu->device_id; > + ret =3D iovt_get_pci_iommu_fwnode(np, np->segment, np->de= vid); > + if (ret) { > + kfree(np); > + return ret; > + } > + > + } else { > + fwnode =3D acpi_alloc_fwnode_static(); > + if (!fwnode) { > + kfree(np); > + return -ENOMEM; > + } > + > + np->fwnode =3D 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 =3D ACPI_ADD_PTR(struct acpi_iovt_device_entry, iommu, iommu->= device_entry_offset); > + for (i =3D 0; i < iommu->device_entry_num; i++) { > + switch (ep->type) { > + case ACPI_IOVT_DEVICE_ENTRY_START: > + is_start =3D true; > + start_devid =3D ep->device_id; > + break; > + case ACPI_IOVT_DEVICE_ENTRY_END: > + if (!is_start) > + break; > + > + entry =3D kzalloc_obj(struct iovt_device_entry, G= FP_ATOMIC); > + if (!entry) > + return -ENOMEM; > + > + entry->start_devid =3D start_devid; > + entry->end_devid =3D ep->device_id; > + list_add_tail(&entry->list, &np->ep_list); > + is_start =3D false; > + break; > + case ACPI_IOVT_DEVICE_ENTRY_SINGLE: > + entry =3D kzalloc_obj(struct iovt_device_entry, G= FP_ATOMIC); > + if (!entry) > + return -ENOMEM; > + > + entry->start_devid =3D ep->device_id; > + entry->end_devid =3D ep->device_id; > + list_add_tail(&entry->list, &np->ep_list); > + is_start =3D false; > + break; > + default: > + break; > + } > + ep =3D 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 =3D (struct acpi_table_iovt *)header; > + iommu =3D ACPI_ADD_PTR(struct acpi_iovt_iommu, iovt, iovt->iommu_= offset); > + for (i =3D 0; i < iovt->iommu_count; i++) { > + iovt_add_iommu(iommu); > + iommu =3D ACPI_ADD_PTR(struct acpi_iovt_iommu, iommu, iom= mu->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 =3D acpi_get_table(ACPI_SIG_IOVT, 0, &hdr); > + if (ACPI_FAILURE(status)) { > + if (status !=3D AE_NOT_FOUND) > + pr_err("Failed to get table, %s\n", acpi_format_e= xception(status)); > + > + return; > + } > + > + iovt =3D (struct acpi_table_iovt *)&hdr; > + iommu =3D ACPI_ADD_PTR(struct acpi_iovt_iommu, iovt, iovt->iommu_= offset); > + for (i =3D 0; i < iovt->iommu_count; i++) { > + iovt_enable_acs(iommu); > + iommu =3D ACPI_ADD_PTR(struct acpi_iovt_iommu, iommu, iom= mu->header.length); > + } > + > + acpi_put_table(hdr); > +} > + > +void __init acpi_iovt_late_init(void) > +{ > + acpi_status status; > + struct acpi_table_header *hdr; > + > + status =3D acpi_get_table(ACPI_SIG_IOVT, 0, &hdr); > + if (ACPI_FAILURE(status)) { > + if (status !=3D AE_NOT_FOUND) > + pr_err("Failed to get table, %s\n", acpi_format_e= xception(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 =3D 0x00, > ACPI_IOVT_IOMMU_RESERVED =3D 0x01 /* 1 and greater are reserved *= / > -- > 2.39.3 >