diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2017-10-20 09:48:06 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-10-20 12:08:28 -0400 |
commit | 3321eafd2a79f15126ebaa82f1b5d7fce89c02cb (patch) | |
tree | e2072b7a5cc10eb2e61a086d53f7866175fee558 | |
parent | b8acfd7c0f88c49dc0089cd40e02040187160f6a (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.c | 110 |
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 | */ | ||
51 | static 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, ®16); | ||
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 | ®32); | ||
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 | ®16); | ||
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 | */ |
55 | static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) | 102 | static 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, ®16); | ||
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, ®32); | ||
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, ®16); | ||
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 | |||
126 | out_free_irqs: | ||
127 | pci_free_irq_vectors(dev); | ||
128 | return -EIO; | ||
129 | } | 143 | } |
130 | 144 | ||
131 | /** | 145 | /** |