2 * Support of MSI, HPET and DMAR interrupts.
4 * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
5 * Moved from arch/x86/kernel/apic/io_apic.c.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/interrupt.h>
13 #include <linux/pci.h>
14 #include <linux/dmar.h>
15 #include <linux/hpet.h>
16 #include <linux/msi.h>
17 #include <linux/irqdomain.h>
18 #include <asm/msidef.h>
20 #include <asm/hw_irq.h>
22 #include <asm/irq_remapping.h>
24 void native_compose_msi_msg(struct pci_dev
*pdev
,
25 unsigned int irq
, unsigned int dest
,
26 struct msi_msg
*msg
, u8 hpet_id
)
28 struct irq_cfg
*cfg
= irq_cfg(irq
);
30 msg
->address_hi
= MSI_ADDR_BASE_HI
;
33 msg
->address_hi
|= MSI_ADDR_EXT_DEST_ID(dest
);
37 ((apic
->irq_dest_mode
== 0) ?
38 MSI_ADDR_DEST_MODE_PHYSICAL
:
39 MSI_ADDR_DEST_MODE_LOGICAL
) |
40 ((apic
->irq_delivery_mode
!= dest_LowestPrio
) ?
41 MSI_ADDR_REDIRECTION_CPU
:
42 MSI_ADDR_REDIRECTION_LOWPRI
) |
43 MSI_ADDR_DEST_ID(dest
);
46 MSI_DATA_TRIGGER_EDGE
|
47 MSI_DATA_LEVEL_ASSERT
|
48 ((apic
->irq_delivery_mode
!= dest_LowestPrio
) ?
49 MSI_DATA_DELIVERY_FIXED
:
50 MSI_DATA_DELIVERY_LOWPRI
) |
51 MSI_DATA_VECTOR(cfg
->vector
);
54 static int msi_compose_msg(struct pci_dev
*pdev
, unsigned int irq
,
55 struct msi_msg
*msg
, u8 hpet_id
)
65 err
= assign_irq_vector(irq
, cfg
, apic
->target_cpus());
69 err
= apic
->cpu_mask_to_apicid_and(cfg
->domain
,
70 apic
->target_cpus(), &dest
);
74 x86_msi
.compose_msi_msg(pdev
, irq
, dest
, msg
, hpet_id
);
80 msi_set_affinity(struct irq_data
*data
, const struct cpumask
*mask
, bool force
)
82 struct irq_cfg
*cfg
= irqd_cfg(data
);
87 ret
= apic_set_affinity(data
, mask
, &dest
);
91 __get_cached_msi_msg(data
->msi_desc
, &msg
);
93 msg
.data
&= ~MSI_DATA_VECTOR_MASK
;
94 msg
.data
|= MSI_DATA_VECTOR(cfg
->vector
);
95 msg
.address_lo
&= ~MSI_ADDR_DEST_ID_MASK
;
96 msg
.address_lo
|= MSI_ADDR_DEST_ID(dest
);
98 __pci_write_msi_msg(data
->msi_desc
, &msg
);
100 return IRQ_SET_MASK_OK_NOCOPY
;
104 * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
105 * which implement the MSI or MSI-X Capability Structure.
107 static struct irq_chip msi_chip
= {
109 .irq_unmask
= pci_msi_unmask_irq
,
110 .irq_mask
= pci_msi_mask_irq
,
111 .irq_ack
= apic_ack_edge
,
112 .irq_set_affinity
= msi_set_affinity
,
113 .irq_retrigger
= apic_retrigger_irq
,
114 .flags
= IRQCHIP_SKIP_SET_WAKE
,
117 int setup_msi_irq(struct pci_dev
*dev
, struct msi_desc
*msidesc
,
118 unsigned int irq_base
, unsigned int irq_offset
)
120 struct irq_chip
*chip
= &msi_chip
;
122 unsigned int irq
= irq_base
+ irq_offset
;
125 ret
= msi_compose_msg(dev
, irq
, &msg
, -1);
129 irq_set_msi_desc_off(irq_base
, irq_offset
, msidesc
);
132 * MSI-X message is written per-IRQ, the offset is always 0.
133 * MSI message denotes a contiguous group of IRQs, written for 0th IRQ.
136 pci_write_msi_msg(irq
, &msg
);
138 setup_remapped_irq(irq
, irq_cfg(irq
), chip
);
140 irq_set_chip_and_handler_name(irq
, chip
, handle_edge_irq
, "edge");
142 dev_dbg(&dev
->dev
, "irq %d for MSI/MSI-X\n", irq
);
147 int native_setup_msi_irqs(struct pci_dev
*dev
, int nvec
, int type
)
149 struct msi_desc
*msidesc
;
152 /* Multiple MSI vectors only supported with interrupt remapping */
153 if (type
== PCI_CAP_ID_MSI
&& nvec
> 1)
156 list_for_each_entry(msidesc
, &dev
->msi_list
, list
) {
157 irq
= irq_domain_alloc_irqs(NULL
, 1, NUMA_NO_NODE
, NULL
);
161 ret
= setup_msi_irq(dev
, msidesc
, irq
, 0);
163 irq_domain_free_irqs(irq
, 1);
171 void native_teardown_msi_irq(unsigned int irq
)
173 irq_domain_free_irqs(irq
, 1);
176 #ifdef CONFIG_DMAR_TABLE
178 dmar_msi_set_affinity(struct irq_data
*data
, const struct cpumask
*mask
,
181 struct irq_cfg
*cfg
= irqd_cfg(data
);
182 unsigned int dest
, irq
= data
->irq
;
186 ret
= apic_set_affinity(data
, mask
, &dest
);
190 dmar_msi_read(irq
, &msg
);
192 msg
.data
&= ~MSI_DATA_VECTOR_MASK
;
193 msg
.data
|= MSI_DATA_VECTOR(cfg
->vector
);
194 msg
.address_lo
&= ~MSI_ADDR_DEST_ID_MASK
;
195 msg
.address_lo
|= MSI_ADDR_DEST_ID(dest
);
196 msg
.address_hi
= MSI_ADDR_BASE_HI
| MSI_ADDR_EXT_DEST_ID(dest
);
198 dmar_msi_write(irq
, &msg
);
200 return IRQ_SET_MASK_OK_NOCOPY
;
203 static struct irq_chip dmar_msi_type
= {
205 .irq_unmask
= dmar_msi_unmask
,
206 .irq_mask
= dmar_msi_mask
,
207 .irq_ack
= apic_ack_edge
,
208 .irq_set_affinity
= dmar_msi_set_affinity
,
209 .irq_retrigger
= apic_retrigger_irq
,
210 .flags
= IRQCHIP_SKIP_SET_WAKE
,
213 int arch_setup_dmar_msi(unsigned int irq
)
218 ret
= msi_compose_msg(NULL
, irq
, &msg
, -1);
221 dmar_msi_write(irq
, &msg
);
222 irq_set_chip_and_handler_name(irq
, &dmar_msi_type
, handle_edge_irq
,
227 int dmar_alloc_hwirq(void)
229 return irq_domain_alloc_irqs(NULL
, 1, NUMA_NO_NODE
, NULL
);
232 void dmar_free_hwirq(int irq
)
234 irq_domain_free_irqs(irq
, 1);
239 * MSI message composition
241 #ifdef CONFIG_HPET_TIMER
243 static int hpet_msi_set_affinity(struct irq_data
*data
,
244 const struct cpumask
*mask
, bool force
)
246 struct irq_cfg
*cfg
= irqd_cfg(data
);
251 ret
= apic_set_affinity(data
, mask
, &dest
);
255 hpet_msi_read(data
->handler_data
, &msg
);
257 msg
.data
&= ~MSI_DATA_VECTOR_MASK
;
258 msg
.data
|= MSI_DATA_VECTOR(cfg
->vector
);
259 msg
.address_lo
&= ~MSI_ADDR_DEST_ID_MASK
;
260 msg
.address_lo
|= MSI_ADDR_DEST_ID(dest
);
262 hpet_msi_write(data
->handler_data
, &msg
);
264 return IRQ_SET_MASK_OK_NOCOPY
;
267 static struct irq_chip hpet_msi_type
= {
269 .irq_unmask
= hpet_msi_unmask
,
270 .irq_mask
= hpet_msi_mask
,
271 .irq_ack
= apic_ack_edge
,
272 .irq_set_affinity
= hpet_msi_set_affinity
,
273 .irq_retrigger
= apic_retrigger_irq
,
274 .flags
= IRQCHIP_SKIP_SET_WAKE
,
277 int default_setup_hpet_msi(unsigned int irq
, unsigned int id
)
279 struct irq_chip
*chip
= &hpet_msi_type
;
283 ret
= msi_compose_msg(NULL
, irq
, &msg
, id
);
287 hpet_msi_write(irq_get_handler_data(irq
), &msg
);
288 irq_set_status_flags(irq
, IRQ_MOVE_PCNTXT
);
289 setup_remapped_irq(irq
, irq_cfg(irq
), chip
);
291 irq_set_chip_and_handler_name(irq
, chip
, handle_edge_irq
, "edge");