162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MSI hooks for standard x86 apic
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/pci.h>
762306a36Sopenharmony_ci#include <linux/irq.h>
862306a36Sopenharmony_ci#include <linux/msi.h>
962306a36Sopenharmony_ci#include <linux/dmar.h>
1062306a36Sopenharmony_ci#include <asm/smp.h>
1162306a36Sopenharmony_ci#include <asm/msidef.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic struct irq_chip	ia64_msi_chip;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#ifdef CONFIG_SMP
1662306a36Sopenharmony_cistatic int ia64_set_msi_irq_affinity(struct irq_data *idata,
1762306a36Sopenharmony_ci				     const cpumask_t *cpu_mask, bool force)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct msi_msg msg;
2062306a36Sopenharmony_ci	u32 addr, data;
2162306a36Sopenharmony_ci	int cpu = cpumask_first_and(cpu_mask, cpu_online_mask);
2262306a36Sopenharmony_ci	unsigned int irq = idata->irq;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (irq_prepare_move(irq, cpu))
2562306a36Sopenharmony_ci		return -1;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	__get_cached_msi_msg(irq_data_get_msi_desc(idata), &msg);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	addr = msg.address_lo;
3062306a36Sopenharmony_ci	addr &= MSI_ADDR_DEST_ID_MASK;
3162306a36Sopenharmony_ci	addr |= MSI_ADDR_DEST_ID_CPU(cpu_physical_id(cpu));
3262306a36Sopenharmony_ci	msg.address_lo = addr;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	data = msg.data;
3562306a36Sopenharmony_ci	data &= MSI_DATA_VECTOR_MASK;
3662306a36Sopenharmony_ci	data |= MSI_DATA_VECTOR(irq_to_vector(irq));
3762306a36Sopenharmony_ci	msg.data = data;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	pci_write_msi_msg(irq, &msg);
4062306a36Sopenharmony_ci	irq_data_update_affinity(idata, cpumask_of(cpu));
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return 0;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci#endif /* CONFIG_SMP */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciint arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct msi_msg	msg;
4962306a36Sopenharmony_ci	unsigned long	dest_phys_id;
5062306a36Sopenharmony_ci	int	irq, vector;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	irq = create_irq();
5362306a36Sopenharmony_ci	if (irq < 0)
5462306a36Sopenharmony_ci		return irq;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	irq_set_msi_desc(irq, desc);
5762306a36Sopenharmony_ci	dest_phys_id = cpu_physical_id(cpumask_any_and(&(irq_to_domain(irq)),
5862306a36Sopenharmony_ci						       cpu_online_mask));
5962306a36Sopenharmony_ci	vector = irq_to_vector(irq);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	msg.address_hi = 0;
6262306a36Sopenharmony_ci	msg.address_lo =
6362306a36Sopenharmony_ci		MSI_ADDR_HEADER |
6462306a36Sopenharmony_ci		MSI_ADDR_DEST_MODE_PHYS |
6562306a36Sopenharmony_ci		MSI_ADDR_REDIRECTION_CPU |
6662306a36Sopenharmony_ci		MSI_ADDR_DEST_ID_CPU(dest_phys_id);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	msg.data =
6962306a36Sopenharmony_ci		MSI_DATA_TRIGGER_EDGE |
7062306a36Sopenharmony_ci		MSI_DATA_LEVEL_ASSERT |
7162306a36Sopenharmony_ci		MSI_DATA_DELIVERY_FIXED |
7262306a36Sopenharmony_ci		MSI_DATA_VECTOR(vector);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	pci_write_msi_msg(irq, &msg);
7562306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_civoid arch_teardown_msi_irq(unsigned int irq)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	destroy_irq(irq);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void ia64_ack_msi_irq(struct irq_data *data)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	irq_complete_move(data->irq);
8862306a36Sopenharmony_ci	irq_move_irq(data);
8962306a36Sopenharmony_ci	ia64_eoi();
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int ia64_msi_retrigger_irq(struct irq_data *data)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	unsigned int vector = irq_to_vector(data->irq);
9562306a36Sopenharmony_ci	ia64_resend_irq(vector);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 1;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * Generic ops used on most IA64 platforms.
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cistatic struct irq_chip ia64_msi_chip = {
10462306a36Sopenharmony_ci	.name			= "PCI-MSI",
10562306a36Sopenharmony_ci	.irq_mask		= pci_msi_mask_irq,
10662306a36Sopenharmony_ci	.irq_unmask		= pci_msi_unmask_irq,
10762306a36Sopenharmony_ci	.irq_ack		= ia64_ack_msi_irq,
10862306a36Sopenharmony_ci#ifdef CONFIG_SMP
10962306a36Sopenharmony_ci	.irq_set_affinity	= ia64_set_msi_irq_affinity,
11062306a36Sopenharmony_ci#endif
11162306a36Sopenharmony_ci	.irq_retrigger		= ia64_msi_retrigger_irq,
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#ifdef CONFIG_INTEL_IOMMU
11562306a36Sopenharmony_ci#ifdef CONFIG_SMP
11662306a36Sopenharmony_cistatic int dmar_msi_set_affinity(struct irq_data *data,
11762306a36Sopenharmony_ci				 const struct cpumask *mask, bool force)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	unsigned int irq = data->irq;
12062306a36Sopenharmony_ci	struct irq_cfg *cfg = irq_cfg + irq;
12162306a36Sopenharmony_ci	struct msi_msg msg;
12262306a36Sopenharmony_ci	int cpu = cpumask_first_and(mask, cpu_online_mask);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (irq_prepare_move(irq, cpu))
12562306a36Sopenharmony_ci		return -1;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	dmar_msi_read(irq, &msg);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	msg.data &= ~MSI_DATA_VECTOR_MASK;
13062306a36Sopenharmony_ci	msg.data |= MSI_DATA_VECTOR(cfg->vector);
13162306a36Sopenharmony_ci	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
13262306a36Sopenharmony_ci	msg.address_lo |= MSI_ADDR_DEST_ID_CPU(cpu_physical_id(cpu));
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	dmar_msi_write(irq, &msg);
13562306a36Sopenharmony_ci	irq_data_update_affinity(data, mask);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci#endif /* CONFIG_SMP */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct irq_chip dmar_msi_type = {
14262306a36Sopenharmony_ci	.name = "DMAR_MSI",
14362306a36Sopenharmony_ci	.irq_unmask = dmar_msi_unmask,
14462306a36Sopenharmony_ci	.irq_mask = dmar_msi_mask,
14562306a36Sopenharmony_ci	.irq_ack = ia64_ack_msi_irq,
14662306a36Sopenharmony_ci#ifdef CONFIG_SMP
14762306a36Sopenharmony_ci	.irq_set_affinity = dmar_msi_set_affinity,
14862306a36Sopenharmony_ci#endif
14962306a36Sopenharmony_ci	.irq_retrigger = ia64_msi_retrigger_irq,
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void
15362306a36Sopenharmony_cimsi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct irq_cfg *cfg = irq_cfg + irq;
15662306a36Sopenharmony_ci	unsigned dest;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	dest = cpu_physical_id(cpumask_first_and(&(irq_to_domain(irq)),
15962306a36Sopenharmony_ci						 cpu_online_mask));
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	msg->address_hi = 0;
16262306a36Sopenharmony_ci	msg->address_lo =
16362306a36Sopenharmony_ci		MSI_ADDR_HEADER |
16462306a36Sopenharmony_ci		MSI_ADDR_DEST_MODE_PHYS |
16562306a36Sopenharmony_ci		MSI_ADDR_REDIRECTION_CPU |
16662306a36Sopenharmony_ci		MSI_ADDR_DEST_ID_CPU(dest);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	msg->data =
16962306a36Sopenharmony_ci		MSI_DATA_TRIGGER_EDGE |
17062306a36Sopenharmony_ci		MSI_DATA_LEVEL_ASSERT |
17162306a36Sopenharmony_ci		MSI_DATA_DELIVERY_FIXED |
17262306a36Sopenharmony_ci		MSI_DATA_VECTOR(cfg->vector);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciint dmar_alloc_hwirq(int id, int node, void *arg)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	int irq;
17862306a36Sopenharmony_ci	struct msi_msg msg;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	irq = create_irq();
18162306a36Sopenharmony_ci	if (irq > 0) {
18262306a36Sopenharmony_ci		irq_set_handler_data(irq, arg);
18362306a36Sopenharmony_ci		irq_set_chip_and_handler_name(irq, &dmar_msi_type,
18462306a36Sopenharmony_ci					      handle_edge_irq, "edge");
18562306a36Sopenharmony_ci		msi_compose_msg(NULL, irq, &msg);
18662306a36Sopenharmony_ci		dmar_msi_write(irq, &msg);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return irq;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_civoid dmar_free_hwirq(int irq)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	irq_set_handler_data(irq, NULL);
19562306a36Sopenharmony_ci	destroy_irq(irq);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci#endif /* CONFIG_INTEL_IOMMU */
19862306a36Sopenharmony_ci
199