|
1 | 1 | // SPDX-License-Identifier: GPL-2.0-only
|
| 2 | +// Copyright (C) 2013-2015 ARM Limited, All Rights Reserved. |
| 3 | +// Author: Marc Zyngier <[email protected]> |
2 | 4 | // Copyright (C) 2022 Linutronix GmbH
|
3 | 5 | // Copyright (C) 2022 Intel
|
4 | 6 |
|
| 7 | +#include <linux/pci.h> |
| 8 | + |
5 | 9 | #include "irq-gic-common.h"
|
6 | 10 | #include "irq-msi-lib.h"
|
7 | 11 |
|
8 | 12 | #define ITS_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
|
9 |
| - MSI_FLAG_USE_DEF_CHIP_OPS) |
| 13 | + MSI_FLAG_USE_DEF_CHIP_OPS | \ |
| 14 | + MSI_FLAG_PCI_MSI_MASK_PARENT) |
10 | 15 |
|
11 | 16 | #define ITS_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
|
12 | 17 | MSI_FLAG_PCI_MSIX | \
|
13 |
| - MSI_FLAG_MULTI_PCI_MSI | \ |
14 |
| - MSI_FLAG_PCI_MSI_MASK_PARENT) |
| 18 | + MSI_FLAG_MULTI_PCI_MSI) |
| 19 | + |
| 20 | +#ifdef CONFIG_PCI_MSI |
| 21 | +static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data) |
| 22 | +{ |
| 23 | + int msi, msix, *count = data; |
| 24 | + |
| 25 | + msi = max(pci_msi_vec_count(pdev), 0); |
| 26 | + msix = max(pci_msix_vec_count(pdev), 0); |
| 27 | + *count += max(msi, msix); |
| 28 | + |
| 29 | + return 0; |
| 30 | +} |
| 31 | + |
| 32 | +static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) |
| 33 | +{ |
| 34 | + struct pci_dev **alias_dev = data; |
| 35 | + |
| 36 | + *alias_dev = pdev; |
| 37 | + |
| 38 | + return 0; |
| 39 | +} |
| 40 | + |
| 41 | +static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev, |
| 42 | + int nvec, msi_alloc_info_t *info) |
| 43 | +{ |
| 44 | + struct pci_dev *pdev, *alias_dev; |
| 45 | + struct msi_domain_info *msi_info; |
| 46 | + int alias_count = 0, minnvec = 1; |
| 47 | + |
| 48 | + if (!dev_is_pci(dev)) |
| 49 | + return -EINVAL; |
| 50 | + |
| 51 | + pdev = to_pci_dev(dev); |
| 52 | + /* |
| 53 | + * If pdev is downstream of any aliasing bridges, take an upper |
| 54 | + * bound of how many other vectors could map to the same DevID. |
| 55 | + * Also tell the ITS that the signalling will come from a proxy |
| 56 | + * device, and that special allocation rules apply. |
| 57 | + */ |
| 58 | + pci_for_each_dma_alias(pdev, its_get_pci_alias, &alias_dev); |
| 59 | + if (alias_dev != pdev) { |
| 60 | + if (alias_dev->subordinate) |
| 61 | + pci_walk_bus(alias_dev->subordinate, |
| 62 | + its_pci_msi_vec_count, &alias_count); |
| 63 | + info->flags |= MSI_ALLOC_FLAGS_PROXY_DEVICE; |
| 64 | + } |
| 65 | + |
| 66 | + /* ITS specific DeviceID, as the core ITS ignores dev. */ |
| 67 | + info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev); |
| 68 | + |
| 69 | + /* |
| 70 | + * @domain->msi_domain_info->hwsize contains the size of the |
| 71 | + * MSI[-X] domain, but vector allocation happens one by one. This |
| 72 | + * needs some thought when MSI comes into play as the size of MSI |
| 73 | + * might be unknown at domain creation time and therefore set to |
| 74 | + * MSI_MAX_INDEX. |
| 75 | + */ |
| 76 | + msi_info = msi_get_domain_info(domain); |
| 77 | + if (msi_info->hwsize > nvec) |
| 78 | + nvec = msi_info->hwsize; |
| 79 | + |
| 80 | + /* |
| 81 | + * Always allocate a power of 2, and special case device 0 for |
| 82 | + * broken systems where the DevID is not wired (and all devices |
| 83 | + * appear as DevID 0). For that reason, we generously allocate a |
| 84 | + * minimum of 32 MSIs for DevID 0. If you want more because all |
| 85 | + * your devices are aliasing to DevID 0, consider fixing your HW. |
| 86 | + */ |
| 87 | + nvec = max(nvec, alias_count); |
| 88 | + if (!info->scratchpad[0].ul) |
| 89 | + minnvec = 32; |
| 90 | + nvec = max_t(int, minnvec, roundup_pow_of_two(nvec)); |
| 91 | + |
| 92 | + msi_info = msi_get_domain_info(domain->parent); |
| 93 | + return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info); |
| 94 | +} |
| 95 | +#else /* CONFIG_PCI_MSI */ |
| 96 | +#define its_pci_msi_prepare NULL |
| 97 | +#endif /* !CONFIG_PCI_MSI */ |
15 | 98 |
|
16 | 99 | static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
|
17 | 100 | struct irq_domain *real_parent, struct msi_domain_info *info)
|
18 | 101 | {
|
19 | 102 | if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
|
20 | 103 | return false;
|
21 | 104 |
|
| 105 | + switch(info->bus_token) { |
| 106 | + case DOMAIN_BUS_PCI_DEVICE_MSI: |
| 107 | + case DOMAIN_BUS_PCI_DEVICE_MSIX: |
| 108 | + /* |
| 109 | + * FIXME: This probably should be done after a (not yet |
| 110 | + * existing) post domain creation callback once to make |
| 111 | + * support for dynamic post-enable MSI-X allocations |
| 112 | + * work without having to reevaluate the domain size |
| 113 | + * over and over. It is known already at allocation |
| 114 | + * time via info->hwsize. |
| 115 | + * |
| 116 | + * That should work perfectly fine for MSI/MSI-X but needs |
| 117 | + * some thoughts for purely software managed MSI domains |
| 118 | + * where the index space is only limited artificially via |
| 119 | + * %MSI_MAX_INDEX. |
| 120 | + */ |
| 121 | + info->ops->msi_prepare = its_pci_msi_prepare; |
| 122 | + break; |
| 123 | + default: |
| 124 | + /* Confused. How did the lib return true? */ |
| 125 | + WARN_ON_ONCE(1); |
| 126 | + return false; |
| 127 | + } |
| 128 | + |
22 | 129 | return true;
|
23 | 130 | }
|
24 | 131 |
|
25 | 132 | const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
|
26 | 133 | .supported_flags = ITS_MSI_FLAGS_SUPPORTED,
|
27 | 134 | .required_flags = ITS_MSI_FLAGS_REQUIRED,
|
28 | 135 | .bus_select_token = DOMAIN_BUS_NEXUS,
|
| 136 | + .bus_select_mask = MATCH_PCI_MSI, |
29 | 137 | .prefix = "ITS-",
|
30 | 138 | .init_dev_msi_info = its_init_dev_msi_info,
|
31 | 139 | };
|
0 commit comments