|
3 | 3 | * |
4 | 4 | * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo |
5 | 5 | * Moved from arch/x86/kernel/apic/io_apic.c. |
| 6 | + |
| 7 | + * Convert to hierarchical irqdomain |
6 | 8 | * |
7 | 9 | * This program is free software; you can redistribute it and/or modify |
8 | 10 | * it under the terms of the GNU General Public License version 2 as |
|
21 | 23 | #include <asm/apic.h> |
22 | 24 | #include <asm/irq_remapping.h> |
23 | 25 |
|
| 26 | +static struct irq_domain *msi_default_domain; |
| 27 | + |
24 | 28 | void native_compose_msi_msg(struct pci_dev *pdev, |
25 | 29 | unsigned int irq, unsigned int dest, |
26 | 30 | struct msi_msg *msg, u8 hpet_id) |
@@ -114,102 +118,107 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, |
114 | 118 | return 0; |
115 | 119 | } |
116 | 120 |
|
117 | | -static int |
118 | | -msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force) |
119 | | -{ |
120 | | - struct irq_cfg *cfg = irqd_cfg(data); |
121 | | - struct msi_msg msg; |
122 | | - unsigned int dest; |
123 | | - int ret; |
124 | | - |
125 | | - ret = apic_set_affinity(data, mask, &dest); |
126 | | - if (ret) |
127 | | - return ret; |
128 | | - |
129 | | - __get_cached_msi_msg(data->msi_desc, &msg); |
130 | | - |
131 | | - msg.data &= ~MSI_DATA_VECTOR_MASK; |
132 | | - msg.data |= MSI_DATA_VECTOR(cfg->vector); |
133 | | - msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; |
134 | | - msg.address_lo |= MSI_ADDR_DEST_ID(dest); |
135 | | - |
136 | | - __pci_write_msi_msg(data->msi_desc, &msg); |
137 | | - |
138 | | - return IRQ_SET_MASK_OK_NOCOPY; |
139 | | -} |
140 | | - |
141 | 121 | /* |
142 | 122 | * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, |
143 | 123 | * which implement the MSI or MSI-X Capability Structure. |
144 | 124 | */ |
145 | | -static struct irq_chip msi_chip = { |
| 125 | +static struct irq_chip pci_msi_controller = { |
146 | 126 | .name = "PCI-MSI", |
147 | 127 | .irq_unmask = pci_msi_unmask_irq, |
148 | 128 | .irq_mask = pci_msi_mask_irq, |
149 | | - .irq_ack = apic_ack_edge, |
150 | | - .irq_set_affinity = msi_set_affinity, |
151 | | - .irq_retrigger = apic_retrigger_irq, |
| 129 | + .irq_ack = irq_chip_ack_parent, |
| 130 | + .irq_set_affinity = msi_domain_set_affinity, |
| 131 | + .irq_retrigger = irq_chip_retrigger_hierarchy, |
| 132 | + .irq_print_chip = irq_remapping_print_chip, |
| 133 | + .irq_compose_msi_msg = irq_msi_compose_msg, |
| 134 | + .irq_write_msi_msg = pci_msi_domain_write_msg, |
152 | 135 | .flags = IRQCHIP_SKIP_SET_WAKE, |
153 | 136 | }; |
154 | 137 |
|
155 | | -int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, |
156 | | - unsigned int irq_base, unsigned int irq_offset) |
| 138 | +int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) |
157 | 139 | { |
158 | | - struct irq_chip *chip = &msi_chip; |
159 | | - struct msi_msg msg; |
160 | | - unsigned int irq = irq_base + irq_offset; |
161 | | - int ret; |
| 140 | + struct irq_domain *domain; |
| 141 | + struct irq_alloc_info info; |
162 | 142 |
|
163 | | - ret = msi_compose_msg(dev, irq, &msg, -1); |
164 | | - if (ret < 0) |
165 | | - return ret; |
| 143 | + init_irq_alloc_info(&info, NULL); |
| 144 | + info.type = X86_IRQ_ALLOC_TYPE_MSI; |
| 145 | + info.msi_dev = dev; |
166 | 146 |
|
167 | | - irq_set_msi_desc_off(irq_base, irq_offset, msidesc); |
| 147 | + domain = irq_remapping_get_irq_domain(&info); |
| 148 | + if (domain == NULL) |
| 149 | + domain = msi_default_domain; |
| 150 | + if (domain == NULL) |
| 151 | + return -ENOSYS; |
168 | 152 |
|
169 | | - /* |
170 | | - * MSI-X message is written per-IRQ, the offset is always 0. |
171 | | - * MSI message denotes a contiguous group of IRQs, written for 0th IRQ. |
172 | | - */ |
173 | | - if (!irq_offset) |
174 | | - pci_write_msi_msg(irq, &msg); |
| 153 | + return pci_msi_domain_alloc_irqs(domain, dev, nvec, type); |
| 154 | +} |
175 | 155 |
|
176 | | - setup_remapped_irq(irq, irq_cfg(irq), chip); |
| 156 | +void native_teardown_msi_irq(unsigned int irq) |
| 157 | +{ |
| 158 | + irq_domain_free_irqs(irq, 1); |
| 159 | +} |
177 | 160 |
|
178 | | - irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge"); |
| 161 | +static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info, |
| 162 | + msi_alloc_info_t *arg) |
| 163 | +{ |
| 164 | + return arg->msi_hwirq; |
| 165 | +} |
179 | 166 |
|
180 | | - dev_dbg(&dev->dev, "irq %d for MSI/MSI-X\n", irq); |
| 167 | +static int pci_msi_prepare(struct irq_domain *domain, struct device *dev, |
| 168 | + int nvec, msi_alloc_info_t *arg) |
| 169 | +{ |
| 170 | + struct pci_dev *pdev = to_pci_dev(dev); |
| 171 | + struct msi_desc *desc = first_pci_msi_entry(pdev); |
| 172 | + |
| 173 | + init_irq_alloc_info(arg, NULL); |
| 174 | + arg->msi_dev = pdev; |
| 175 | + if (desc->msi_attrib.is_msix) { |
| 176 | + arg->type = X86_IRQ_ALLOC_TYPE_MSIX; |
| 177 | + } else { |
| 178 | + arg->type = X86_IRQ_ALLOC_TYPE_MSI; |
| 179 | + arg->flags |= X86_IRQ_ALLOC_CONTIGUOUS_VECTORS; |
| 180 | + } |
181 | 181 |
|
182 | 182 | return 0; |
183 | 183 | } |
184 | 184 |
|
185 | | -int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) |
| 185 | +static void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc) |
186 | 186 | { |
187 | | - struct msi_desc *msidesc; |
188 | | - int irq, ret; |
| 187 | + arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc); |
| 188 | +} |
189 | 189 |
|
190 | | - /* Multiple MSI vectors only supported with interrupt remapping */ |
191 | | - if (type == PCI_CAP_ID_MSI && nvec > 1) |
192 | | - return 1; |
| 190 | +static struct msi_domain_ops pci_msi_domain_ops = { |
| 191 | + .get_hwirq = pci_msi_get_hwirq, |
| 192 | + .msi_prepare = pci_msi_prepare, |
| 193 | + .set_desc = pci_msi_set_desc, |
| 194 | +}; |
193 | 195 |
|
194 | | - list_for_each_entry(msidesc, &dev->msi_list, list) { |
195 | | - irq = irq_domain_alloc_irqs(NULL, 1, NUMA_NO_NODE, NULL); |
196 | | - if (irq <= 0) |
197 | | - return -ENOSPC; |
| 196 | +static struct msi_domain_info pci_msi_domain_info = { |
| 197 | + .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | |
| 198 | + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, |
| 199 | + .ops = &pci_msi_domain_ops, |
| 200 | + .chip = &pci_msi_controller, |
| 201 | + .handler = handle_edge_irq, |
| 202 | + .handler_name = "edge", |
| 203 | +}; |
198 | 204 |
|
199 | | - ret = setup_msi_irq(dev, msidesc, irq, 0); |
200 | | - if (ret < 0) { |
201 | | - irq_domain_free_irqs(irq, 1); |
202 | | - return ret; |
203 | | - } |
| 205 | +void arch_init_msi_domain(struct irq_domain *parent) |
| 206 | +{ |
| 207 | + if (disable_apic) |
| 208 | + return; |
204 | 209 |
|
205 | | - } |
206 | | - return 0; |
| 210 | + msi_default_domain = pci_msi_create_irq_domain(NULL, |
| 211 | + &pci_msi_domain_info, parent); |
| 212 | + if (!msi_default_domain) |
| 213 | + pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n"); |
207 | 214 | } |
208 | 215 |
|
209 | | -void native_teardown_msi_irq(unsigned int irq) |
| 216 | +#ifdef CONFIG_IRQ_REMAP |
| 217 | +struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent) |
210 | 218 | { |
211 | | - irq_domain_free_irqs(irq, 1); |
| 219 | + return msi_create_irq_domain(NULL, &pci_msi_domain_info, parent); |
212 | 220 | } |
| 221 | +#endif |
213 | 222 |
|
214 | 223 | #ifdef CONFIG_DMAR_TABLE |
215 | 224 | static int |
|
0 commit comments