msi_ia64.c 5.3 KB
Newer Older
1 2 3 4 5 6
/*
 * MSI hooks for standard x86 apic
 */

#include <linux/pci.h>
#include <linux/irq.h>
7
#include <linux/msi.h>
8
#include <linux/dmar.h>
9
#include <asm/smp.h>
10 11 12 13 14 15 16

/*
 * Shifts for APIC-based data
 */

#define MSI_DATA_VECTOR_SHIFT		0
#define	    MSI_DATA_VECTOR(v)		(((u8)v) << MSI_DATA_VECTOR_SHIFT)
17
#define MSI_DATA_VECTOR_MASK		0xffffff00
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

#define MSI_DATA_DELIVERY_SHIFT		8
#define     MSI_DATA_DELIVERY_FIXED	(0 << MSI_DATA_DELIVERY_SHIFT)
#define     MSI_DATA_DELIVERY_LOWPRI	(1 << MSI_DATA_DELIVERY_SHIFT)

#define MSI_DATA_LEVEL_SHIFT		14
#define     MSI_DATA_LEVEL_DEASSERT	(0 << MSI_DATA_LEVEL_SHIFT)
#define     MSI_DATA_LEVEL_ASSERT	(1 << MSI_DATA_LEVEL_SHIFT)

#define MSI_DATA_TRIGGER_SHIFT		15
#define     MSI_DATA_TRIGGER_EDGE	(0 << MSI_DATA_TRIGGER_SHIFT)
#define     MSI_DATA_TRIGGER_LEVEL	(1 << MSI_DATA_TRIGGER_SHIFT)

/*
 * Shift/mask fields for APIC-based bus address
 */

35
#define MSI_TARGET_CPU_SHIFT		4
36 37 38 39 40 41 42 43 44 45 46 47 48
#define MSI_ADDR_HEADER			0xfee00000

#define MSI_ADDR_DESTID_MASK		0xfff0000f
#define     MSI_ADDR_DESTID_CPU(cpu)	((cpu) << MSI_TARGET_CPU_SHIFT)

#define MSI_ADDR_DESTMODE_SHIFT		2
#define     MSI_ADDR_DESTMODE_PHYS	(0 << MSI_ADDR_DESTMODE_SHIFT)
#define	    MSI_ADDR_DESTMODE_LOGIC	(1 << MSI_ADDR_DESTMODE_SHIFT)

#define MSI_ADDR_REDIRECTION_SHIFT	3
#define     MSI_ADDR_REDIRECTION_CPU	(0 << MSI_ADDR_REDIRECTION_SHIFT)
#define     MSI_ADDR_REDIRECTION_LOWPRI	(1 << MSI_ADDR_REDIRECTION_SHIFT)

49
static struct irq_chip	ia64_msi_chip;
50

51 52
#ifdef CONFIG_SMP
static void ia64_set_msi_irq_affinity(unsigned int irq, cpumask_t cpu_mask)
53
{
54
	struct msi_msg msg;
55 56
	u32 addr, data;
	int cpu = first_cpu(cpu_mask);
57

58 59 60
	if (!cpu_online(cpu))
		return;

61
	if (irq_prepare_move(irq, cpu))
62 63
		return;

64
	read_msi_msg(irq, &msg);
65

66
	addr = msg.address_lo;
67
	addr &= MSI_ADDR_DESTID_MASK;
68
	addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(cpu));
69
	msg.address_lo = addr;
70

71 72 73 74 75
	data = msg.data;
	data &= MSI_DATA_VECTOR_MASK;
	data |= MSI_DATA_VECTOR(irq_to_vector(irq));
	msg.data = data;

76
	write_msi_msg(irq, &msg);
77
	irq_desc[irq].affinity = cpumask_of_cpu(cpu);
78
}
79
#endif /* CONFIG_SMP */
80

81
int ia64_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
82
{
83
	struct msi_msg	msg;
84
	unsigned long	dest_phys_id;
85
	int	irq, vector;
86
	cpumask_t mask;
87

88 89 90 91 92
	irq = create_irq();
	if (irq < 0)
		return irq;

	set_irq_msi(irq, desc);
93 94
	cpus_and(mask, irq_to_domain(irq), cpu_online_map);
	dest_phys_id = cpu_physical_id(first_cpu(mask));
95
	vector = irq_to_vector(irq);
96

97 98
	msg.address_hi = 0;
	msg.address_lo =
99 100 101 102
		MSI_ADDR_HEADER |
		MSI_ADDR_DESTMODE_PHYS |
		MSI_ADDR_REDIRECTION_CPU |
		MSI_ADDR_DESTID_CPU(dest_phys_id);
103

104
	msg.data =
105
		MSI_DATA_TRIGGER_EDGE |
106 107 108 109
		MSI_DATA_LEVEL_ASSERT |
		MSI_DATA_DELIVERY_FIXED |
		MSI_DATA_VECTOR(vector);

110 111 112
	write_msi_msg(irq, &msg);
	set_irq_chip_and_handler(irq, &ia64_msi_chip, handle_edge_irq);

113
	return 0;
114 115
}

116
void ia64_teardown_msi_irq(unsigned int irq)
117
{
118
	destroy_irq(irq);
119 120
}

121 122
static void ia64_ack_msi_irq(unsigned int irq)
{
123
	irq_complete_move(irq);
124 125 126 127 128 129
	move_native_irq(irq);
	ia64_eoi();
}

static int ia64_msi_retrigger_irq(unsigned int irq)
{
130
	unsigned int vector = irq_to_vector(irq);
131 132 133 134 135
	ia64_resend_irq(vector);

	return 1;
}

136
/*
137
 * Generic ops used on most IA64 platforms.
138
 */
139 140 141 142 143 144 145 146 147
static struct irq_chip ia64_msi_chip = {
	.name		= "PCI-MSI",
	.mask		= mask_msi_irq,
	.unmask		= unmask_msi_irq,
	.ack		= ia64_ack_msi_irq,
#ifdef CONFIG_SMP
	.set_affinity	= ia64_set_msi_irq_affinity,
#endif
	.retrigger	= ia64_msi_retrigger_irq,
148
};
149 150


151
int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
152 153
{
	if (platform_setup_msi_irq)
154
		return platform_setup_msi_irq(pdev, desc);
155

156
	return ia64_setup_msi_irq(pdev, desc);
157 158 159 160 161 162 163 164 165
}

void arch_teardown_msi_irq(unsigned int irq)
{
	if (platform_teardown_msi_irq)
		return platform_teardown_msi_irq(irq);

	return ia64_teardown_msi_irq(irq);
}
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

#ifdef CONFIG_DMAR
#ifdef CONFIG_SMP
static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask)
{
	struct irq_cfg *cfg = irq_cfg + irq;
	struct msi_msg msg;
	int cpu = first_cpu(mask);


	if (!cpu_online(cpu))
		return;

	if (irq_prepare_move(irq, cpu))
		return;

	dmar_msi_read(irq, &msg);

	msg.data &= ~MSI_DATA_VECTOR_MASK;
	msg.data |= MSI_DATA_VECTOR(cfg->vector);
	msg.address_lo &= ~MSI_ADDR_DESTID_MASK;
	msg.address_lo |= MSI_ADDR_DESTID_CPU(cpu_physical_id(cpu));

	dmar_msi_write(irq, &msg);
	irq_desc[irq].affinity = mask;
}
#endif /* CONFIG_SMP */

struct irq_chip dmar_msi_type = {
	.name = "DMAR_MSI",
	.unmask = dmar_msi_unmask,
	.mask = dmar_msi_mask,
	.ack = ia64_ack_msi_irq,
#ifdef CONFIG_SMP
	.set_affinity = dmar_msi_set_affinity,
#endif
	.retrigger = ia64_msi_retrigger_irq,
};

static int
msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
{
	struct irq_cfg *cfg = irq_cfg + irq;
	unsigned dest;
	cpumask_t mask;

	cpus_and(mask, irq_to_domain(irq), cpu_online_map);
	dest = cpu_physical_id(first_cpu(mask));

	msg->address_hi = 0;
	msg->address_lo =
		MSI_ADDR_HEADER |
		MSI_ADDR_DESTMODE_PHYS |
		MSI_ADDR_REDIRECTION_CPU |
		MSI_ADDR_DESTID_CPU(dest);

	msg->data =
		MSI_DATA_TRIGGER_EDGE |
		MSI_DATA_LEVEL_ASSERT |
		MSI_DATA_DELIVERY_FIXED |
		MSI_DATA_VECTOR(cfg->vector);
	return 0;
}

int arch_setup_dmar_msi(unsigned int irq)
{
	int ret;
	struct msi_msg msg;

	ret = msi_compose_msg(NULL, irq, &msg);
	if (ret < 0)
		return ret;
	dmar_msi_write(irq, &msg);
	set_irq_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
		"edge");
	return 0;
}
#endif /* CONFIG_DMAR */