xref: /openbmc/linux/arch/ia64/kernel/msi_ia64.c (revision 073352e9)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
203571e11SEric W. Biederman /*
303571e11SEric W. Biederman  * MSI hooks for standard x86 apic
403571e11SEric W. Biederman  */
503571e11SEric W. Biederman 
603571e11SEric W. Biederman #include <linux/pci.h>
703571e11SEric W. Biederman #include <linux/irq.h>
803571e11SEric W. Biederman #include <linux/msi.h>
962fdd767SFenghua Yu #include <linux/dmar.h>
1003571e11SEric W. Biederman #include <asm/smp.h>
112fa8937fSXiantao Zhang #include <asm/msidef.h>
1203571e11SEric W. Biederman 
1303571e11SEric W. Biederman static struct irq_chip	ia64_msi_chip;
1403571e11SEric W. Biederman 
1503571e11SEric W. Biederman #ifdef CONFIG_SMP
ia64_set_msi_irq_affinity(struct irq_data * idata,const cpumask_t * cpu_mask,bool force)16f1f701e9SThomas Gleixner static int ia64_set_msi_irq_affinity(struct irq_data *idata,
17f1f701e9SThomas Gleixner 				     const cpumask_t *cpu_mask, bool force)
1803571e11SEric W. Biederman {
1903571e11SEric W. Biederman 	struct msi_msg msg;
20cd378f18SYasuaki Ishimatsu 	u32 addr, data;
21785aebd0SThomas Gleixner 	int cpu = cpumask_first_and(cpu_mask, cpu_online_mask);
22f1f701e9SThomas Gleixner 	unsigned int irq = idata->irq;
2303571e11SEric W. Biederman 
24a6cd6322SKenji Kaneshige 	if (irq_prepare_move(irq, cpu))
25d5dedd45SYinghai Lu 		return -1;
264994be1bSYasuaki Ishimatsu 
27507a883eSJiang Liu 	__get_cached_msi_msg(irq_data_get_msi_desc(idata), &msg);
2803571e11SEric W. Biederman 
2903571e11SEric W. Biederman 	addr = msg.address_lo;
302fa8937fSXiantao Zhang 	addr &= MSI_ADDR_DEST_ID_MASK;
312fa8937fSXiantao Zhang 	addr |= MSI_ADDR_DEST_ID_CPU(cpu_physical_id(cpu));
3203571e11SEric W. Biederman 	msg.address_lo = addr;
3303571e11SEric W. Biederman 
34cd378f18SYasuaki Ishimatsu 	data = msg.data;
35cd378f18SYasuaki Ishimatsu 	data &= MSI_DATA_VECTOR_MASK;
36cd378f18SYasuaki Ishimatsu 	data |= MSI_DATA_VECTOR(irq_to_vector(irq));
37cd378f18SYasuaki Ishimatsu 	msg.data = data;
38cd378f18SYasuaki Ishimatsu 
3983a18912SJiang Liu 	pci_write_msi_msg(irq, &msg);
40*073352e9SSamuel Holland 	irq_data_update_affinity(idata, cpumask_of(cpu));
41d5dedd45SYinghai Lu 
42d5dedd45SYinghai Lu 	return 0;
4303571e11SEric W. Biederman }
4403571e11SEric W. Biederman #endif /* CONFIG_SMP */
4503571e11SEric W. Biederman 
arch_setup_msi_irq(struct pci_dev * pdev,struct msi_desc * desc)4605933aacSChristoph Hellwig int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
4703571e11SEric W. Biederman {
4803571e11SEric W. Biederman 	struct msi_msg	msg;
4903571e11SEric W. Biederman 	unsigned long	dest_phys_id;
508a3a0ee7SKenji Kaneshige 	int	irq, vector;
5103571e11SEric W. Biederman 
52f7feaca7SEric W. Biederman 	irq = create_irq();
53f7feaca7SEric W. Biederman 	if (irq < 0)
54f7feaca7SEric W. Biederman 		return irq;
55f7feaca7SEric W. Biederman 
5653c909c9SThomas Gleixner 	irq_set_msi_desc(irq, desc);
5751f7bd85SRusty Russell 	dest_phys_id = cpu_physical_id(cpumask_any_and(&(irq_to_domain(irq)),
5851f7bd85SRusty Russell 						       cpu_online_mask));
599438a121SIshimatsu Yasuaki 	vector = irq_to_vector(irq);
6003571e11SEric W. Biederman 
6103571e11SEric W. Biederman 	msg.address_hi = 0;
6203571e11SEric W. Biederman 	msg.address_lo =
6303571e11SEric W. Biederman 		MSI_ADDR_HEADER |
642fa8937fSXiantao Zhang 		MSI_ADDR_DEST_MODE_PHYS |
6503571e11SEric W. Biederman 		MSI_ADDR_REDIRECTION_CPU |
662fa8937fSXiantao Zhang 		MSI_ADDR_DEST_ID_CPU(dest_phys_id);
6703571e11SEric W. Biederman 
6803571e11SEric W. Biederman 	msg.data =
6903571e11SEric W. Biederman 		MSI_DATA_TRIGGER_EDGE |
7003571e11SEric W. Biederman 		MSI_DATA_LEVEL_ASSERT |
7103571e11SEric W. Biederman 		MSI_DATA_DELIVERY_FIXED |
7203571e11SEric W. Biederman 		MSI_DATA_VECTOR(vector);
7303571e11SEric W. Biederman 
7483a18912SJiang Liu 	pci_write_msi_msg(irq, &msg);
7553c909c9SThomas Gleixner 	irq_set_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq);
7603571e11SEric W. Biederman 
773aff0373SKenji Kaneshige 	return 0;
7803571e11SEric W. Biederman }
7903571e11SEric W. Biederman 
arch_teardown_msi_irq(unsigned int irq)8005933aacSChristoph Hellwig void arch_teardown_msi_irq(unsigned int irq)
8103571e11SEric W. Biederman {
82f7feaca7SEric W. Biederman 	destroy_irq(irq);
8303571e11SEric W. Biederman }
8403571e11SEric W. Biederman 
ia64_ack_msi_irq(struct irq_data * data)85f1f701e9SThomas Gleixner static void ia64_ack_msi_irq(struct irq_data *data)
8603571e11SEric W. Biederman {
87f1f701e9SThomas Gleixner 	irq_complete_move(data->irq);
8897499b2eSThomas Gleixner 	irq_move_irq(data);
8903571e11SEric W. Biederman 	ia64_eoi();
9003571e11SEric W. Biederman }
9103571e11SEric W. Biederman 
ia64_msi_retrigger_irq(struct irq_data * data)92f1f701e9SThomas Gleixner static int ia64_msi_retrigger_irq(struct irq_data *data)
9303571e11SEric W. Biederman {
94f1f701e9SThomas Gleixner 	unsigned int vector = irq_to_vector(data->irq);
9503571e11SEric W. Biederman 	ia64_resend_irq(vector);
9603571e11SEric W. Biederman 
9703571e11SEric W. Biederman 	return 1;
9803571e11SEric W. Biederman }
9903571e11SEric W. Biederman 
10003571e11SEric W. Biederman /*
10103571e11SEric W. Biederman  * Generic ops used on most IA64 platforms.
10203571e11SEric W. Biederman  */
10303571e11SEric W. Biederman static struct irq_chip ia64_msi_chip = {
10403571e11SEric W. Biederman 	.name			= "PCI-MSI",
105280510f1SThomas Gleixner 	.irq_mask		= pci_msi_mask_irq,
106280510f1SThomas Gleixner 	.irq_unmask		= pci_msi_unmask_irq,
107f1f701e9SThomas Gleixner 	.irq_ack		= ia64_ack_msi_irq,
10803571e11SEric W. Biederman #ifdef CONFIG_SMP
109f1f701e9SThomas Gleixner 	.irq_set_affinity	= ia64_set_msi_irq_affinity,
11003571e11SEric W. Biederman #endif
111f1f701e9SThomas Gleixner 	.irq_retrigger		= ia64_msi_retrigger_irq,
11203571e11SEric W. Biederman };
11303571e11SEric W. Biederman 
114d3f13810SSuresh Siddha #ifdef CONFIG_INTEL_IOMMU
11562fdd767SFenghua Yu #ifdef CONFIG_SMP
dmar_msi_set_affinity(struct irq_data * data,const struct cpumask * mask,bool force)116f1f701e9SThomas Gleixner static int dmar_msi_set_affinity(struct irq_data *data,
117f1f701e9SThomas Gleixner 				 const struct cpumask *mask, bool force)
11862fdd767SFenghua Yu {
119f1f701e9SThomas Gleixner 	unsigned int irq = data->irq;
12062fdd767SFenghua Yu 	struct irq_cfg *cfg = irq_cfg + irq;
12162fdd767SFenghua Yu 	struct msi_msg msg;
122785aebd0SThomas Gleixner 	int cpu = cpumask_first_and(mask, cpu_online_mask);
12362fdd767SFenghua Yu 
12462fdd767SFenghua Yu 	if (irq_prepare_move(irq, cpu))
125d5dedd45SYinghai Lu 		return -1;
12662fdd767SFenghua Yu 
12762fdd767SFenghua Yu 	dmar_msi_read(irq, &msg);
12862fdd767SFenghua Yu 
12962fdd767SFenghua Yu 	msg.data &= ~MSI_DATA_VECTOR_MASK;
13062fdd767SFenghua Yu 	msg.data |= MSI_DATA_VECTOR(cfg->vector);
1312fa8937fSXiantao Zhang 	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
1322fa8937fSXiantao Zhang 	msg.address_lo |= MSI_ADDR_DEST_ID_CPU(cpu_physical_id(cpu));
13362fdd767SFenghua Yu 
13462fdd767SFenghua Yu 	dmar_msi_write(irq, &msg);
135*073352e9SSamuel Holland 	irq_data_update_affinity(data, mask);
136d5dedd45SYinghai Lu 
137d5dedd45SYinghai Lu 	return 0;
13862fdd767SFenghua Yu }
13962fdd767SFenghua Yu #endif /* CONFIG_SMP */
14062fdd767SFenghua Yu 
1419542b21eSJaswinder Singh Rajput static struct irq_chip dmar_msi_type = {
14262fdd767SFenghua Yu 	.name = "DMAR_MSI",
1435c2837fbSThomas Gleixner 	.irq_unmask = dmar_msi_unmask,
1445c2837fbSThomas Gleixner 	.irq_mask = dmar_msi_mask,
145f1f701e9SThomas Gleixner 	.irq_ack = ia64_ack_msi_irq,
14662fdd767SFenghua Yu #ifdef CONFIG_SMP
147f1f701e9SThomas Gleixner 	.irq_set_affinity = dmar_msi_set_affinity,
14862fdd767SFenghua Yu #endif
149f1f701e9SThomas Gleixner 	.irq_retrigger = ia64_msi_retrigger_irq,
15062fdd767SFenghua Yu };
15162fdd767SFenghua Yu 
15234742db8SJiang Liu static void
msi_compose_msg(struct pci_dev * pdev,unsigned int irq,struct msi_msg * msg)15362fdd767SFenghua Yu msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
15462fdd767SFenghua Yu {
15562fdd767SFenghua Yu 	struct irq_cfg *cfg = irq_cfg + irq;
15662fdd767SFenghua Yu 	unsigned dest;
15762fdd767SFenghua Yu 
15851f7bd85SRusty Russell 	dest = cpu_physical_id(cpumask_first_and(&(irq_to_domain(irq)),
15951f7bd85SRusty Russell 						 cpu_online_mask));
16062fdd767SFenghua Yu 
16162fdd767SFenghua Yu 	msg->address_hi = 0;
16262fdd767SFenghua Yu 	msg->address_lo =
16362fdd767SFenghua Yu 		MSI_ADDR_HEADER |
1642fa8937fSXiantao Zhang 		MSI_ADDR_DEST_MODE_PHYS |
16562fdd767SFenghua Yu 		MSI_ADDR_REDIRECTION_CPU |
1662fa8937fSXiantao Zhang 		MSI_ADDR_DEST_ID_CPU(dest);
16762fdd767SFenghua Yu 
16862fdd767SFenghua Yu 	msg->data =
16962fdd767SFenghua Yu 		MSI_DATA_TRIGGER_EDGE |
17062fdd767SFenghua Yu 		MSI_DATA_LEVEL_ASSERT |
17162fdd767SFenghua Yu 		MSI_DATA_DELIVERY_FIXED |
17262fdd767SFenghua Yu 		MSI_DATA_VECTOR(cfg->vector);
17362fdd767SFenghua Yu }
17462fdd767SFenghua Yu 
dmar_alloc_hwirq(int id,int node,void * arg)17534742db8SJiang Liu int dmar_alloc_hwirq(int id, int node, void *arg)
17662fdd767SFenghua Yu {
17734742db8SJiang Liu 	int irq;
17862fdd767SFenghua Yu 	struct msi_msg msg;
17962fdd767SFenghua Yu 
18034742db8SJiang Liu 	irq = create_irq();
18134742db8SJiang Liu 	if (irq > 0) {
18234742db8SJiang Liu 		irq_set_handler_data(irq, arg);
18334742db8SJiang Liu 		irq_set_chip_and_handler_name(irq, &dmar_msi_type,
18434742db8SJiang Liu 					      handle_edge_irq, "edge");
18534742db8SJiang Liu 		msi_compose_msg(NULL, irq, &msg);
18662fdd767SFenghua Yu 		dmar_msi_write(irq, &msg);
18734742db8SJiang Liu 	}
18834742db8SJiang Liu 
18934742db8SJiang Liu 	return irq;
19034742db8SJiang Liu }
19134742db8SJiang Liu 
dmar_free_hwirq(int irq)19234742db8SJiang Liu void dmar_free_hwirq(int irq)
19334742db8SJiang Liu {
19434742db8SJiang Liu 	irq_set_handler_data(irq, NULL);
19534742db8SJiang Liu 	destroy_irq(irq);
19662fdd767SFenghua Yu }
197d3f13810SSuresh Siddha #endif /* CONFIG_INTEL_IOMMU */
19862fdd767SFenghua Yu 
199