blob: 519d9432f407e264ca178749f1eaa4549add119a [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Mark Maulefd58e552006-04-10 21:17:48 -05002/*
3 * MSI hooks for standard x86 apic
4 */
5
6#include <linux/pci.h>
7#include <linux/irq.h>
Eric W. Biederman3b7d1922006-10-04 02:16:59 -07008#include <linux/msi.h>
Fenghua Yu62fdd762008-10-17 12:14:13 -07009#include <linux/dmar.h>
Christian Kujaua4cffb62006-06-26 14:00:02 +020010#include <asm/smp.h>
Xiantao Zhang2fa89372009-02-16 15:14:48 +080011#include <asm/msidef.h>
Mark Maulefd58e552006-04-10 21:17:48 -050012
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070013static struct irq_chip ia64_msi_chip;
Mark Maulefd58e552006-04-10 21:17:48 -050014
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070015#ifdef CONFIG_SMP
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +010016static int ia64_set_msi_irq_affinity(struct irq_data *idata,
17 const cpumask_t *cpu_mask, bool force)
Mark Maulefd58e552006-04-10 21:17:48 -050018{
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070019 struct msi_msg msg;
Yasuaki Ishimatsucd378f12007-07-17 21:22:48 +090020 u32 addr, data;
Thomas Gleixner785aebd2014-03-04 20:43:38 +000021 int cpu = cpumask_first_and(cpu_mask, cpu_online_mask);
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +010022 unsigned int irq = idata->irq;
Mark Maulefd58e552006-04-10 21:17:48 -050023
Kenji Kaneshigea6cd63222008-02-25 14:32:22 +090024 if (irq_prepare_move(irq, cpu))
Yinghai Lud5dedd42009-04-27 17:59:21 -070025 return -1;
Yasuaki Ishimatsu4994be12007-07-17 21:22:33 +090026
Jiang Liu507a8832015-06-01 16:05:42 +080027 __get_cached_msi_msg(irq_data_get_msi_desc(idata), &msg);
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070028
29 addr = msg.address_lo;
Xiantao Zhang2fa89372009-02-16 15:14:48 +080030 addr &= MSI_ADDR_DEST_ID_MASK;
31 addr |= MSI_ADDR_DEST_ID_CPU(cpu_physical_id(cpu));
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070032 msg.address_lo = addr;
Mark Maulefd58e552006-04-10 21:17:48 -050033
Yasuaki Ishimatsucd378f12007-07-17 21:22:48 +090034 data = msg.data;
35 data &= MSI_DATA_VECTOR_MASK;
36 data |= MSI_DATA_VECTOR(irq_to_vector(irq));
37 msg.data = data;
38
Jiang Liu83a18912014-11-09 23:10:34 +080039 pci_write_msi_msg(irq, &msg);
Jiang Liuc42574e2015-07-13 20:42:46 +000040 cpumask_copy(irq_data_get_affinity_mask(idata), cpumask_of(cpu));
Yinghai Lud5dedd42009-04-27 17:59:21 -070041
42 return 0;
Mark Maulefd58e552006-04-10 21:17:48 -050043}
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070044#endif /* CONFIG_SMP */
Mark Maulefd58e552006-04-10 21:17:48 -050045
Eric W. Biedermanf7feaca2007-01-28 12:56:37 -070046int ia64_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
Mark Maulefd58e552006-04-10 21:17:48 -050047{
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070048 struct msi_msg msg;
Mark Maulefd58e552006-04-10 21:17:48 -050049 unsigned long dest_phys_id;
Kenji Kaneshige8a3a0ee2007-03-26 09:38:42 +090050 int irq, vector;
Mark Maulefd58e552006-04-10 21:17:48 -050051
Eric W. Biedermanf7feaca2007-01-28 12:56:37 -070052 irq = create_irq();
53 if (irq < 0)
54 return irq;
55
Thomas Gleixner53c909c2011-03-25 21:06:09 +010056 irq_set_msi_desc(irq, desc);
Rusty Russell51f7bd82015-03-05 10:48:49 +103057 dest_phys_id = cpu_physical_id(cpumask_any_and(&(irq_to_domain(irq)),
58 cpu_online_mask));
Ishimatsu Yasuaki9438a122007-04-06 16:51:12 +090059 vector = irq_to_vector(irq);
Mark Maulefd58e552006-04-10 21:17:48 -050060
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070061 msg.address_hi = 0;
62 msg.address_lo =
Eric W. Biederman38bc0362006-10-04 02:16:34 -070063 MSI_ADDR_HEADER |
Xiantao Zhang2fa89372009-02-16 15:14:48 +080064 MSI_ADDR_DEST_MODE_PHYS |
Eric W. Biederman38bc0362006-10-04 02:16:34 -070065 MSI_ADDR_REDIRECTION_CPU |
Xiantao Zhang2fa89372009-02-16 15:14:48 +080066 MSI_ADDR_DEST_ID_CPU(dest_phys_id);
Mark Maulefd58e552006-04-10 21:17:48 -050067
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070068 msg.data =
Eric W. Biederman38bc0362006-10-04 02:16:34 -070069 MSI_DATA_TRIGGER_EDGE |
Mark Maulefd58e552006-04-10 21:17:48 -050070 MSI_DATA_LEVEL_ASSERT |
71 MSI_DATA_DELIVERY_FIXED |
72 MSI_DATA_VECTOR(vector);
73
Jiang Liu83a18912014-11-09 23:10:34 +080074 pci_write_msi_msg(irq, &msg);
Thomas Gleixner53c909c2011-03-25 21:06:09 +010075 irq_set_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq);
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070076
Kenji Kaneshige3aff0372007-10-30 16:01:49 +090077 return 0;
Mark Maulefd58e552006-04-10 21:17:48 -050078}
79
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070080void ia64_teardown_msi_irq(unsigned int irq)
Mark Maulefd58e552006-04-10 21:17:48 -050081{
Eric W. Biedermanf7feaca2007-01-28 12:56:37 -070082 destroy_irq(irq);
Mark Maulefd58e552006-04-10 21:17:48 -050083}
84
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +010085static void ia64_ack_msi_irq(struct irq_data *data)
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070086{
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +010087 irq_complete_move(data->irq);
Thomas Gleixner97499b2e2011-03-25 20:36:55 +010088 irq_move_irq(data);
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070089 ia64_eoi();
90}
Mark Maulefd58e552006-04-10 21:17:48 -050091
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +010092static int ia64_msi_retrigger_irq(struct irq_data *data)
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070093{
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +010094 unsigned int vector = irq_to_vector(data->irq);
Eric W. Biederman3b7d1922006-10-04 02:16:59 -070095 ia64_resend_irq(vector);
96
97 return 1;
98}
99
100/*
101 * Generic ops used on most IA64 platforms.
102 */
103static struct irq_chip ia64_msi_chip = {
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100104 .name = "PCI-MSI",
Thomas Gleixner280510f2014-11-23 12:23:20 +0100105 .irq_mask = pci_msi_mask_irq,
106 .irq_unmask = pci_msi_unmask_irq,
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100107 .irq_ack = ia64_ack_msi_irq,
Eric W. Biederman3b7d1922006-10-04 02:16:59 -0700108#ifdef CONFIG_SMP
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100109 .irq_set_affinity = ia64_set_msi_irq_affinity,
Eric W. Biederman3b7d1922006-10-04 02:16:59 -0700110#endif
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100111 .irq_retrigger = ia64_msi_retrigger_irq,
Mark Maulefd58e552006-04-10 21:17:48 -0500112};
Eric W. Biederman3b7d1922006-10-04 02:16:59 -0700113
114
Eric W. Biedermanf7feaca2007-01-28 12:56:37 -0700115int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
Eric W. Biederman3b7d1922006-10-04 02:16:59 -0700116{
117 if (platform_setup_msi_irq)
Eric W. Biedermanf7feaca2007-01-28 12:56:37 -0700118 return platform_setup_msi_irq(pdev, desc);
Eric W. Biederman3b7d1922006-10-04 02:16:59 -0700119
Eric W. Biedermanf7feaca2007-01-28 12:56:37 -0700120 return ia64_setup_msi_irq(pdev, desc);
Eric W. Biederman3b7d1922006-10-04 02:16:59 -0700121}
122
123void arch_teardown_msi_irq(unsigned int irq)
124{
125 if (platform_teardown_msi_irq)
126 return platform_teardown_msi_irq(irq);
127
128 return ia64_teardown_msi_irq(irq);
129}
Fenghua Yu62fdd762008-10-17 12:14:13 -0700130
Suresh Siddhad3f13812011-08-23 17:05:25 -0700131#ifdef CONFIG_INTEL_IOMMU
Fenghua Yu62fdd762008-10-17 12:14:13 -0700132#ifdef CONFIG_SMP
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100133static int dmar_msi_set_affinity(struct irq_data *data,
134 const struct cpumask *mask, bool force)
Fenghua Yu62fdd762008-10-17 12:14:13 -0700135{
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100136 unsigned int irq = data->irq;
Fenghua Yu62fdd762008-10-17 12:14:13 -0700137 struct irq_cfg *cfg = irq_cfg + irq;
138 struct msi_msg msg;
Thomas Gleixner785aebd2014-03-04 20:43:38 +0000139 int cpu = cpumask_first_and(mask, cpu_online_mask);
Fenghua Yu62fdd762008-10-17 12:14:13 -0700140
141 if (irq_prepare_move(irq, cpu))
Yinghai Lud5dedd42009-04-27 17:59:21 -0700142 return -1;
Fenghua Yu62fdd762008-10-17 12:14:13 -0700143
144 dmar_msi_read(irq, &msg);
145
146 msg.data &= ~MSI_DATA_VECTOR_MASK;
147 msg.data |= MSI_DATA_VECTOR(cfg->vector);
Xiantao Zhang2fa89372009-02-16 15:14:48 +0800148 msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
149 msg.address_lo |= MSI_ADDR_DEST_ID_CPU(cpu_physical_id(cpu));
Fenghua Yu62fdd762008-10-17 12:14:13 -0700150
151 dmar_msi_write(irq, &msg);
Jiang Liuc42574e2015-07-13 20:42:46 +0000152 cpumask_copy(irq_data_get_affinity_mask(data), mask);
Yinghai Lud5dedd42009-04-27 17:59:21 -0700153
154 return 0;
Fenghua Yu62fdd762008-10-17 12:14:13 -0700155}
156#endif /* CONFIG_SMP */
157
Jaswinder Singh Rajput9542b212009-06-10 12:45:01 -0700158static struct irq_chip dmar_msi_type = {
Fenghua Yu62fdd762008-10-17 12:14:13 -0700159 .name = "DMAR_MSI",
Thomas Gleixner5c2837f2010-09-28 17:15:11 +0200160 .irq_unmask = dmar_msi_unmask,
161 .irq_mask = dmar_msi_mask,
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100162 .irq_ack = ia64_ack_msi_irq,
Fenghua Yu62fdd762008-10-17 12:14:13 -0700163#ifdef CONFIG_SMP
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100164 .irq_set_affinity = dmar_msi_set_affinity,
Fenghua Yu62fdd762008-10-17 12:14:13 -0700165#endif
Thomas Gleixnerf1f701e2011-02-04 20:18:43 +0100166 .irq_retrigger = ia64_msi_retrigger_irq,
Fenghua Yu62fdd762008-10-17 12:14:13 -0700167};
168
Jiang Liu34742db2015-04-13 14:11:41 +0800169static void
Fenghua Yu62fdd762008-10-17 12:14:13 -0700170msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
171{
172 struct irq_cfg *cfg = irq_cfg + irq;
173 unsigned dest;
Fenghua Yu62fdd762008-10-17 12:14:13 -0700174
Rusty Russell51f7bd82015-03-05 10:48:49 +1030175 dest = cpu_physical_id(cpumask_first_and(&(irq_to_domain(irq)),
176 cpu_online_mask));
Fenghua Yu62fdd762008-10-17 12:14:13 -0700177
178 msg->address_hi = 0;
179 msg->address_lo =
180 MSI_ADDR_HEADER |
Xiantao Zhang2fa89372009-02-16 15:14:48 +0800181 MSI_ADDR_DEST_MODE_PHYS |
Fenghua Yu62fdd762008-10-17 12:14:13 -0700182 MSI_ADDR_REDIRECTION_CPU |
Xiantao Zhang2fa89372009-02-16 15:14:48 +0800183 MSI_ADDR_DEST_ID_CPU(dest);
Fenghua Yu62fdd762008-10-17 12:14:13 -0700184
185 msg->data =
186 MSI_DATA_TRIGGER_EDGE |
187 MSI_DATA_LEVEL_ASSERT |
188 MSI_DATA_DELIVERY_FIXED |
189 MSI_DATA_VECTOR(cfg->vector);
Fenghua Yu62fdd762008-10-17 12:14:13 -0700190}
191
Jiang Liu34742db2015-04-13 14:11:41 +0800192int dmar_alloc_hwirq(int id, int node, void *arg)
Fenghua Yu62fdd762008-10-17 12:14:13 -0700193{
Jiang Liu34742db2015-04-13 14:11:41 +0800194 int irq;
Fenghua Yu62fdd762008-10-17 12:14:13 -0700195 struct msi_msg msg;
196
Jiang Liu34742db2015-04-13 14:11:41 +0800197 irq = create_irq();
198 if (irq > 0) {
199 irq_set_handler_data(irq, arg);
200 irq_set_chip_and_handler_name(irq, &dmar_msi_type,
201 handle_edge_irq, "edge");
202 msi_compose_msg(NULL, irq, &msg);
203 dmar_msi_write(irq, &msg);
204 }
205
206 return irq;
207}
208
209void dmar_free_hwirq(int irq)
210{
211 irq_set_handler_data(irq, NULL);
212 destroy_irq(irq);
Fenghua Yu62fdd762008-10-17 12:14:13 -0700213}
Suresh Siddhad3f13812011-08-23 17:05:25 -0700214#endif /* CONFIG_INTEL_IOMMU */
Fenghua Yu62fdd762008-10-17 12:14:13 -0700215