// SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2013-2015 ARM Limited, All Rights Reserved. // Author: Marc Zyngier // Copyright (C) 2022 Linutronix GmbH // Copyright (C) 2022 Intel #include #include #include "irq-gic-common.h" #include "irq-msi-lib.h" #define ITS_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ MSI_FLAG_USE_DEF_CHIP_OPS | \ MSI_FLAG_PCI_MSI_MASK_PARENT) #define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ MSI_FLAG_PCI_MSIX | \ MSI_FLAG_MULTI_PCI_MSI) #ifdef CONFIG_PCI_MSI static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data) { int msi, msix, *count = data; msi = max(pci_msi_vec_count(pdev), 0); msix = max(pci_msix_vec_count(pdev), 0); *count += max(msi, msix); return 0; } static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) { struct pci_dev **alias_dev = data; *alias_dev = pdev; return 0; } static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *info) { struct pci_dev *pdev, *alias_dev; struct msi_domain_info *msi_info; int alias_count = 0, minnvec = 1; if (!dev_is_pci(dev)) return -EINVAL; pdev = to_pci_dev(dev); /* * If pdev is downstream of any aliasing bridges, take an upper * bound of how many other vectors could map to the same DevID. * Also tell the ITS that the signalling will come from a proxy * device, and that special allocation rules apply. */ pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev); if (alias_dev != pdev) { if (alias_dev->subordinate) pci_walk_bus(alias_dev->subordinate, its_pci_msi_vec_count, &alias_count); info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE; } /* ITS specific DeviceID, as the core ITS ignores dev. */ info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain->parent, pdev); /* * @domain->msi_domain_info->hwsize contains the size of the * MSI[-X] domain, but vector allocation happens one by one. This * needs some thought when MSI comes into play as the size of MSI * might be unknown at domain creation time and therefore set to * MSI_MAX_INDEX. */ msi_info = msi_get_domain_info(domain); if (msi_info->hwsize > nvec) nvec = msi_info->hwsize; /* * Always allocate a power of 2, and special case device 0 for * broken systems where the DevID is not wired (and all devices * appear as DevID 0). For that reason, we generously allocate a * minimum of 32 MSIs for DevID 0. If you want more because all * your devices are aliasing to DevID 0, consider fixing your HW. */ nvec = max(nvec, alias_count); if (!info->scratchpad[0].ul) minnvec = 32; nvec = max_t(int, minnvec, roundup_pow_of_two(nvec)); msi_info = msi_get_domain_info(domain->parent); return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info); } #else /* CONFIG_PCI_MSI */ #define its_pci_msi_prepare NULL #endif /* !CONFIG_PCI_MSI */ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, u32 *dev_id) { int ret, index = 0; /* Suck the DeviceID out of the msi-parent property */ do { struct of_phandle_args args; ret = of_parse_phandle_with_args(dev->of_node, "msi-parent", "#msi-cells", index, &args); if (args.np == irq_domain_get_of_node(domain)) { if (WARN_ON(args.args_count != 1)) return -EINVAL; *dev_id = args.args[0]; break; } index++; } while (!ret); return ret; } int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) { return -1; } static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *info) { struct msi_domain_info *msi_info; u32 dev_id; int ret; if (dev->of_node) ret = of_pmsi_get_dev_id(domain->parent, dev, &dev_id); else ret = iort_pmsi_get_dev_id(dev, &dev_id); if (ret) return ret; /* ITS specific DeviceID, as the core ITS ignores dev. */ info->scratchpad[0].ul = dev_id; /* * @domain->msi_domain_info->hwsize contains the size of the device * domain, but vector allocation happens one by one. */ msi_info = msi_get_domain_info(domain); if (msi_info->hwsize > nvec) nvec = msi_info->hwsize; /* Allocate at least 32 MSIs, and always as a power of 2 */ nvec = max_t(int, 32, roundup_pow_of_two(nvec)); msi_info = msi_get_domain_info(domain->parent); return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info); } static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain, struct irq_domain *real_parent, struct msi_domain_info *info) { if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info)) return false; switch(info->bus_token) { case DOMAIN_BUS_PCI_DEVICE_MSI: case DOMAIN_BUS_PCI_DEVICE_MSIX: /* * FIXME: This probably should be done after a (not yet * existing) post domain creation callback once to make * support for dynamic post-enable MSI-X allocations * work without having to reevaluate the domain size * over and over. It is known already at allocation * time via info->hwsize. * * That should work perfectly fine for MSI/MSI-X but needs * some thoughts for purely software managed MSI domains * where the index space is only limited artificially via * %MSI_MAX_INDEX. */ info->ops->msi_prepare = its_pci_msi_prepare; break; case DOMAIN_BUS_DEVICE_MSI: case DOMAIN_BUS_WIRED_TO_MSI: /* * FIXME: See the above PCI prepare comment. The domain * size is also known at domain creation time. */ info->ops->msi_prepare = its_pmsi_prepare; break; default: /* Confused. How did the lib return true? */ WARN_ON_ONCE(1); return false; } return true; } const struct msi_parent_ops gic_v3_its_msi_parent_ops = { .supported_flags = ITS_MSI_FLAGS_SUPPORTED, .required_flags = ITS_MSI_FLAGS_REQUIRED, .bus_select_token = DOMAIN_BUS_NEXUS, .bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI, .prefix = "ITS-", .init_dev_msi_info = its_init_dev_msi_info, };