aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2017-10-20 09:48:06 -0400
committerBjorn Helgaas <bhelgaas@google.com>2017-10-20 12:08:28 -0400
commit3321eafd2a79f15126ebaa82f1b5d7fce89c02cb (patch)
treee2072b7a5cc10eb2e61a086d53f7866175fee558
parentb8acfd7c0f88c49dc0089cd40e02040187160f6a (diff)
PCI/portdrv: Factor out Interrupt Message Number lookup
Factor out Interrupt Message Number lookup from the MSI/MSI-X interrupt setup. One side effect is that we only have to check once to see if we have enough vectors for all the services. No functional change intended. Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--drivers/pci/pcie/portdrv_core.c110
1 files changed, 62 insertions, 48 deletions
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index accd16082348..102f5c5fcfe0 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -43,6 +43,53 @@ static void release_pcie_device(struct device *dev)
43 kfree(to_pcie_device(dev)); 43 kfree(to_pcie_device(dev));
44} 44}
45 45
46/*
47 * Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if
48 * services are enabled in "mask". Return the number of MSI/MSI-X vectors
49 * required to accommodate the largest Message Number.
50 */
51static int pcie_message_numbers(struct pci_dev *dev, int mask,
52 u32 *pme, u32 *aer, u32 *dpc)
53{
54 u32 nvec = 0, pos, reg32;
55 u16 reg16;
56
57 /*
58 * The Interrupt Message Number indicates which vector is used, i.e.,
59 * the MSI-X table entry or the MSI offset between the base Message
60 * Data and the generated interrupt message. See PCIe r3.1, sec
61 * 7.8.2, 7.10.10, 7.31.2.
62 */
63
64 if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
65 pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
66 *pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
67 nvec = *pme + 1;
68 }
69
70 if (mask & PCIE_PORT_SERVICE_AER) {
71 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
72 if (pos) {
73 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
74 &reg32);
75 *aer = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27;
76 nvec = max(nvec, *aer + 1);
77 }
78 }
79
80 if (mask & PCIE_PORT_SERVICE_DPC) {
81 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
82 if (pos) {
83 pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP,
84 &reg16);
85 *dpc = reg16 & PCI_EXP_DPC_IRQ;
86 nvec = max(nvec, *dpc + 1);
87 }
88 }
89
90 return nvec;
91}
92
46/** 93/**
47 * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode 94 * pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode
48 * for given port 95 * for given port
@@ -54,7 +101,8 @@ static void release_pcie_device(struct device *dev)
54 */ 101 */
55static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) 102static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
56{ 103{
57 int nr_entries, entry, nvec = 0; 104 int nr_entries, nvec;
105 u32 pme = 0, aer = 0, dpc = 0;
58 106
59 /* Allocate the maximum possible number of MSI/MSI-X vectors */ 107 /* Allocate the maximum possible number of MSI/MSI-X vectors */
60 nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES, 108 nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES,
@@ -62,54 +110,24 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
62 if (nr_entries < 0) 110 if (nr_entries < 0)
63 return nr_entries; 111 return nr_entries;
64 112
65 /* 113 /* See how many and which Interrupt Message Numbers we actually use */
66 * The Interrupt Message Number indicates which vector is used, i.e., 114 nvec = pcie_message_numbers(dev, mask, &pme, &aer, &dpc);
67 * the MSI-X table entry or the MSI offset between the base Message 115 if (nvec > nr_entries) {
68 * Data and the generated interrupt message. See PCIe r3.1, sec 116 pci_free_irq_vectors(dev);
69 * 7.8.2, 7.10.10, 7.31.2. 117 return -EIO;
70 */
71 if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
72 u16 reg16;
73
74 pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
75 entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
76 if (entry >= nr_entries)
77 goto out_free_irqs;
78
79 /* PME and hotplug share an MSI/MSI-X vector */
80 irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry);
81 irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry);
82
83 nvec = max(nvec, entry + 1);
84 } 118 }
85 119
86 if (mask & PCIE_PORT_SERVICE_AER) { 120 /* PME and hotplug share an MSI/MSI-X vector */
87 u32 reg32, pos; 121 if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
88 122 irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme);
89 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); 123 irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme);
90 pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
91 entry = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27;
92 if (entry >= nr_entries)
93 goto out_free_irqs;
94
95 irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry);
96
97 nvec = max(nvec, entry + 1);
98 } 124 }
99 125
100 if (mask & PCIE_PORT_SERVICE_DPC) { 126 if (mask & PCIE_PORT_SERVICE_AER)
101 u16 reg16, pos; 127 irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer);
102
103 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
104 pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, &reg16);
105 entry = reg16 & PCI_EXP_DPC_IRQ;
106 if (entry >= nr_entries)
107 goto out_free_irqs;
108
109 irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, entry);
110 128
111 nvec = max(nvec, entry + 1); 129 if (mask & PCIE_PORT_SERVICE_DPC)
112 } 130 irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, dpc);
113 131
114 /* If we allocated more than we need, free them and allocate fewer */ 132 /* If we allocated more than we need, free them and allocate fewer */
115 if (nvec != nr_entries) { 133 if (nvec != nr_entries) {
@@ -122,10 +140,6 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
122 } 140 }
123 141
124 return 0; 142 return 0;
125
126out_free_irqs:
127 pci_free_irq_vectors(dev);
128 return -EIO;
129} 143}
130 144
131/** 145/**