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