Commit | Line | Data |
---|---|---|
fd58e55f MM |
1 | /* |
2 | * MSI hooks for standard x86 apic | |
3 | */ | |
4 | ||
5 | #include <linux/pci.h> | |
6 | #include <linux/irq.h> | |
3b7d1921 | 7 | #include <linux/msi.h> |
a4cffb64 | 8 | #include <asm/smp.h> |
fd58e55f | 9 | |
fd58e55f MM |
10 | /* |
11 | * Shifts for APIC-based data | |
12 | */ | |
13 | ||
14 | #define MSI_DATA_VECTOR_SHIFT 0 | |
15 | #define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) | |
16 | ||
17 | #define MSI_DATA_DELIVERY_SHIFT 8 | |
18 | #define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) | |
19 | #define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) | |
20 | ||
21 | #define MSI_DATA_LEVEL_SHIFT 14 | |
22 | #define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) | |
23 | #define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) | |
24 | ||
25 | #define MSI_DATA_TRIGGER_SHIFT 15 | |
26 | #define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) | |
27 | #define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) | |
28 | ||
29 | /* | |
30 | * Shift/mask fields for APIC-based bus address | |
31 | */ | |
32 | ||
3b7d1921 | 33 | #define MSI_TARGET_CPU_SHIFT 4 |
fd58e55f MM |
34 | #define MSI_ADDR_HEADER 0xfee00000 |
35 | ||
36 | #define MSI_ADDR_DESTID_MASK 0xfff0000f | |
37 | #define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) | |
38 | ||
39 | #define MSI_ADDR_DESTMODE_SHIFT 2 | |
40 | #define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) | |
41 | #define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) | |
42 | ||
43 | #define MSI_ADDR_REDIRECTION_SHIFT 3 | |
44 | #define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) | |
45 | #define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) | |
46 | ||
3b7d1921 | 47 | static struct irq_chip ia64_msi_chip; |
fd58e55f | 48 | |
3b7d1921 EB |
49 | #ifdef CONFIG_SMP |
50 | static void ia64_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask) | |
fd58e55f | 51 | { |
3b7d1921 EB |
52 | struct msi_msg msg; |
53 | u32 addr; | |
54 | ||
55 | read_msi_msg(irq, &msg); | |
fd58e55f | 56 | |
3b7d1921 | 57 | addr = msg.address_lo; |
fd58e55f | 58 | addr &= MSI_ADDR_DESTID_MASK; |
38bc0361 | 59 | addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(first_cpu(cpu_mask))); |
3b7d1921 | 60 | msg.address_lo = addr; |
fd58e55f | 61 | |
3b7d1921 | 62 | write_msi_msg(irq, &msg); |
9f0a5ba5 | 63 | irq_desc[irq].affinity = cpu_mask; |
fd58e55f | 64 | } |
3b7d1921 | 65 | #endif /* CONFIG_SMP */ |
fd58e55f | 66 | |
f7feaca7 | 67 | int ia64_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) |
fd58e55f | 68 | { |
3b7d1921 | 69 | struct msi_msg msg; |
fd58e55f | 70 | unsigned long dest_phys_id; |
8a3a0ee7 | 71 | int irq, vector; |
fd58e55f | 72 | |
f7feaca7 EB |
73 | irq = create_irq(); |
74 | if (irq < 0) | |
75 | return irq; | |
76 | ||
77 | set_irq_msi(irq, desc); | |
fd58e55f | 78 | dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map)); |
38bc0361 | 79 | vector = irq; |
fd58e55f | 80 | |
3b7d1921 EB |
81 | msg.address_hi = 0; |
82 | msg.address_lo = | |
38bc0361 EB |
83 | MSI_ADDR_HEADER | |
84 | MSI_ADDR_DESTMODE_PHYS | | |
85 | MSI_ADDR_REDIRECTION_CPU | | |
86 | MSI_ADDR_DESTID_CPU(dest_phys_id); | |
fd58e55f | 87 | |
3b7d1921 | 88 | msg.data = |
38bc0361 | 89 | MSI_DATA_TRIGGER_EDGE | |
fd58e55f MM |
90 | MSI_DATA_LEVEL_ASSERT | |
91 | MSI_DATA_DELIVERY_FIXED | | |
92 | MSI_DATA_VECTOR(vector); | |
93 | ||
3b7d1921 EB |
94 | write_msi_msg(irq, &msg); |
95 | set_irq_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq); | |
96 | ||
f7feaca7 | 97 | return irq; |
fd58e55f MM |
98 | } |
99 | ||
3b7d1921 | 100 | void ia64_teardown_msi_irq(unsigned int irq) |
fd58e55f | 101 | { |
f7feaca7 | 102 | destroy_irq(irq); |
fd58e55f MM |
103 | } |
104 | ||
3b7d1921 EB |
105 | static void ia64_ack_msi_irq(unsigned int irq) |
106 | { | |
107 | move_native_irq(irq); | |
108 | ia64_eoi(); | |
109 | } | |
110 | ||
111 | static int ia64_msi_retrigger_irq(unsigned int irq) | |
112 | { | |
113 | unsigned int vector = irq; | |
114 | ia64_resend_irq(vector); | |
115 | ||
116 | return 1; | |
117 | } | |
118 | ||
fd58e55f | 119 | /* |
3b7d1921 | 120 | * Generic ops used on most IA64 platforms. |
fd58e55f | 121 | */ |
3b7d1921 EB |
122 | static struct irq_chip ia64_msi_chip = { |
123 | .name = "PCI-MSI", | |
124 | .mask = mask_msi_irq, | |
125 | .unmask = unmask_msi_irq, | |
126 | .ack = ia64_ack_msi_irq, | |
127 | #ifdef CONFIG_SMP | |
128 | .set_affinity = ia64_set_msi_irq_affinity, | |
129 | #endif | |
130 | .retrigger = ia64_msi_retrigger_irq, | |
fd58e55f | 131 | }; |
3b7d1921 EB |
132 | |
133 | ||
f7feaca7 | 134 | int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) |
3b7d1921 EB |
135 | { |
136 | if (platform_setup_msi_irq) | |
f7feaca7 | 137 | return platform_setup_msi_irq(pdev, desc); |
3b7d1921 | 138 | |
f7feaca7 | 139 | return ia64_setup_msi_irq(pdev, desc); |
3b7d1921 EB |
140 | } |
141 | ||
142 | void arch_teardown_msi_irq(unsigned int irq) | |
143 | { | |
144 | if (platform_teardown_msi_irq) | |
145 | return platform_teardown_msi_irq(irq); | |
146 | ||
147 | return ia64_teardown_msi_irq(irq); | |
148 | } |