Message ID | 20240623142234.840975799@linutronix.de |
---|---|
State | New |
Headers | show |
Series | genirq, irqchip: Convert ARM MSI handling to per device MSI domains | expand |
On Sun, Jun 23, 2024 at 05:18:34PM +0200, Thomas Gleixner wrote: [...] > diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c > new file mode 100644 > index 000000000000..acbccf8f7f5b > --- /dev/null > +++ b/drivers/irqchip/irq-msi-lib.c > @@ -0,0 +1,112 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (C) 2022 Linutronix GmbH > +// Copyright (C) 2022 Intel > + > +#include <linux/export.h> > + > +#include "irq-msi-lib.h" > + > +/** > + * msi_lib_init_dev_msi_info - Domain info setup for MSI domains > + * @dev: The device for which the domain is created for > + * @domain: The domain providing this callback > + * @real_parent: The real parent domain of the domain to be initialized > + * which might be a domain built on top of @domain or > + * @domain itself > + * @info: The domain info for the domain to be initialize > + * > + * This function is to be used for all types of MSI domains above the root > + * parent domain and any intermediates. The topmost parent domain specific > + * functionality is determined via @real_parent. > + * > + * All intermediate domains between the root and the device domain must > + * have either msi_parent_ops.init_dev_msi_info = msi_parent_init_dev_msi_info > + * or invoke it down the line. > + */ > +bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, > + struct irq_domain *real_parent, > + struct msi_domain_info *info) > +{ > + const struct msi_parent_ops *pops = real_parent->msi_parent_ops; > + > + /* > + * MSI parent domain specific settings. For now there is only the > + * root parent domain, e.g. NEXUS, acting as a MSI parent, but it is > + * possible to stack MSI parents. See x86 vector -> irq remapping > + */ > + if (domain->bus_token == pops->bus_select_token) { > + if (WARN_ON_ONCE(domain != real_parent)) > + return false; > + } else { > + WARN_ON_ONCE(1); > + return false; > + } > + > + /* Parent ops available? */ > + if (WARN_ON_ONCE(!pops)) We have already dereferenced pops above, we should move this warning before we dereference it (ie checked devmsi-arm-v4-2 too - branch same comment applies there too). Thanks Lorenzo > + return false; > + > + /* Is the target domain bus token supported? */ > + switch(info->bus_token) { > + default: > + /* > + * This should never be reached. See > + * msi_lib_irq_domain_select() > + */ > + WARN_ON_ONCE(1); > + return false; > + } > + > + /* > + * Mask out the domain specific MSI feature flags which are not > + * supported by the real parent. > + */ > + info->flags &= pops->supported_flags; > + /* Enforce the required flags */ > + info->flags |= pops->required_flags; > + > + /* Chip updates for all child bus types */ > + if (!info->chip->irq_eoi) > + info->chip->irq_eoi = irq_chip_eoi_parent; > + > + /* > + * The device MSI domain can never have a set affinity callback. It > + * always has to rely on the parent domain to handle affinity > + * settings. The device MSI domain just has to write the resulting > + * MSI message into the hardware which is the whole purpose of the > + * device MSI domain aside of mask/unmask which is provided e.g. by > + * PCI/MSI device domains. > + */ > + info->chip->irq_set_affinity = msi_domain_set_affinity; > + return true; > +} > +EXPORT_SYMBOL_GPL(msi_lib_init_dev_msi_info); > + > +/** > + * msi_lib_irq_domain_select - Shared select function for NEXUS domains > + * @d: Pointer to the irq domain on which select is invoked > + * @fwspec: Firmware spec describing what is searched > + * @bus_token: The bus token for which a matching irq domain is looked up > + * > + * Returns: %0 if @d is not what is being looked for > + * > + * %1 if @d is either the domain which is directly searched for or > + * if @d is providing the parent MSI domain for the functionality > + * requested with @bus_token. > + */ > +int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, > + enum irq_domain_bus_token bus_token) > +{ > + const struct msi_parent_ops *ops = d->msi_parent_ops; > + u32 busmask = BIT(bus_token); > + > + if (fwspec->fwnode != d->fwnode || fwspec->param_count != 0) > + return 0; > + > + /* Handle pure domain searches */ > + if (bus_token == ops->bus_select_token) > + return 1; > + > + return ops && !!(ops->bus_select_mask & busmask); > +} > +EXPORT_SYMBOL_GPL(msi_lib_irq_domain_select); > diff --git a/drivers/irqchip/irq-msi-lib.h b/drivers/irqchip/irq-msi-lib.h > new file mode 100644 > index 000000000000..f0706cc28264 > --- /dev/null > +++ b/drivers/irqchip/irq-msi-lib.h > @@ -0,0 +1,19 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (C) 2022 Linutronix GmbH > +// Copyright (C) 2022 Intel > + > +#ifndef _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H > +#define _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H > + > +#include <linux/bits.h> > +#include <linux/irqdomain.h> > +#include <linux/msi.h> > + > +int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, > + enum irq_domain_bus_token bus_token); > + > +bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, > + struct irq_domain *real_parent, > + struct msi_domain_info *info); > + > +#endif /* _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H */ > -- > 2.34.1 > >
On Mon, Jul 01 2024 at 12:18, Lorenzo Pieralisi wrote: > On Sun, Jun 23, 2024 at 05:18:34PM +0200, Thomas Gleixner wrote: >> +bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, >> + struct irq_domain *real_parent, >> + struct msi_domain_info *info) >> +{ >> + const struct msi_parent_ops *pops = real_parent->msi_parent_ops; >> + >> + /* >> + * MSI parent domain specific settings. For now there is only the >> + * root parent domain, e.g. NEXUS, acting as a MSI parent, but it is >> + * possible to stack MSI parents. See x86 vector -> irq remapping >> + */ >> + if (domain->bus_token == pops->bus_select_token) { >> + if (WARN_ON_ONCE(domain != real_parent)) >> + return false; >> + } else { >> + WARN_ON_ONCE(1); >> + return false; >> + } >> + >> + /* Parent ops available? */ >> + if (WARN_ON_ONCE(!pops)) > > We have already dereferenced pops above, we should move this warning > before we dereference it (ie checked devmsi-arm-v4-2 too - branch same > comment applies there too). Oops. Yes.
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 14464716bacb..2bf8d940504c 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -74,6 +74,9 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config IRQ_MSI_LIB + bool + config ARMADA_370_XP_IRQ bool select GENERIC_IRQ_CHIP diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index d9dc3d99aaa8..72c7f6289411 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_PM) += irq-gic-pm.o obj-$(CONFIG_ARCH_REALVIEW) += irq-gic-realview.o +obj-$(CONFIG_IRQ_MSI_LIB) += irq-msi-lib.o obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c new file mode 100644 index 000000000000..acbccf8f7f5b --- /dev/null +++ b/drivers/irqchip/irq-msi-lib.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2022 Linutronix GmbH +// Copyright (C) 2022 Intel + +#include <linux/export.h> + +#include "irq-msi-lib.h" + +/** + * msi_lib_init_dev_msi_info - Domain info setup for MSI domains + * @dev: The device for which the domain is created for + * @domain: The domain providing this callback + * @real_parent: The real parent domain of the domain to be initialized + * which might be a domain built on top of @domain or + * @domain itself + * @info: The domain info for the domain to be initialize + * + * This function is to be used for all types of MSI domains above the root + * parent domain and any intermediates. The topmost parent domain specific + * functionality is determined via @real_parent. + * + * All intermediate domains between the root and the device domain must + * have either msi_parent_ops.init_dev_msi_info = msi_parent_init_dev_msi_info + * or invoke it down the line. + */ +bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *real_parent, + struct msi_domain_info *info) +{ + const struct msi_parent_ops *pops = real_parent->msi_parent_ops; + + /* + * MSI parent domain specific settings. For now there is only the + * root parent domain, e.g. NEXUS, acting as a MSI parent, but it is + * possible to stack MSI parents. See x86 vector -> irq remapping + */ + if (domain->bus_token == pops->bus_select_token) { + if (WARN_ON_ONCE(domain != real_parent)) + return false; + } else { + WARN_ON_ONCE(1); + return false; + } + + /* Parent ops available? */ + if (WARN_ON_ONCE(!pops)) + return false; + + /* Is the target domain bus token supported? */ + switch(info->bus_token) { + default: + /* + * This should never be reached. See + * msi_lib_irq_domain_select() + */ + WARN_ON_ONCE(1); + return false; + } + + /* + * Mask out the domain specific MSI feature flags which are not + * supported by the real parent. + */ + info->flags &= pops->supported_flags; + /* Enforce the required flags */ + info->flags |= pops->required_flags; + + /* Chip updates for all child bus types */ + if (!info->chip->irq_eoi) + info->chip->irq_eoi = irq_chip_eoi_parent; + + /* + * The device MSI domain can never have a set affinity callback. It + * always has to rely on the parent domain to handle affinity + * settings. The device MSI domain just has to write the resulting + * MSI message into the hardware which is the whole purpose of the + * device MSI domain aside of mask/unmask which is provided e.g. by + * PCI/MSI device domains. + */ + info->chip->irq_set_affinity = msi_domain_set_affinity; + return true; +} +EXPORT_SYMBOL_GPL(msi_lib_init_dev_msi_info); + +/** + * msi_lib_irq_domain_select - Shared select function for NEXUS domains + * @d: Pointer to the irq domain on which select is invoked + * @fwspec: Firmware spec describing what is searched + * @bus_token: The bus token for which a matching irq domain is looked up + * + * Returns: %0 if @d is not what is being looked for + * + * %1 if @d is either the domain which is directly searched for or + * if @d is providing the parent MSI domain for the functionality + * requested with @bus_token. + */ +int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token) +{ + const struct msi_parent_ops *ops = d->msi_parent_ops; + u32 busmask = BIT(bus_token); + + if (fwspec->fwnode != d->fwnode || fwspec->param_count != 0) + return 0; + + /* Handle pure domain searches */ + if (bus_token == ops->bus_select_token) + return 1; + + return ops && !!(ops->bus_select_mask & busmask); +} +EXPORT_SYMBOL_GPL(msi_lib_irq_domain_select); diff --git a/drivers/irqchip/irq-msi-lib.h b/drivers/irqchip/irq-msi-lib.h new file mode 100644 index 000000000000..f0706cc28264 --- /dev/null +++ b/drivers/irqchip/irq-msi-lib.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2022 Linutronix GmbH +// Copyright (C) 2022 Intel + +#ifndef _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H +#define _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H + +#include <linux/bits.h> +#include <linux/irqdomain.h> +#include <linux/msi.h> + +int msi_lib_irq_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token); + +bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *real_parent, + struct msi_domain_info *info); + +#endif /* _DRIVERS_IRQCHIP_IRQ_MSI_LIB_H */