irq.c 11.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
/*
 * Platform dependent support for SGI SN
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
J
Jes Sorensen 已提交
8
 * Copyright (c) 2000-2006 Silicon Graphics, Inc.  All Rights Reserved.
L
Linus Torvalds 已提交
9 10 11
 */

#include <linux/irq.h>
12
#include <linux/spinlock.h>
J
Jes Sorensen 已提交
13
#include <linux/init.h>
L
Linus Torvalds 已提交
14 15
#include <asm/sn/addrs.h>
#include <asm/sn/arch.h>
16 17
#include <asm/sn/intr.h>
#include <asm/sn/pcibr_provider.h>
18 19
#include <asm/sn/pcibus_provider_defs.h>
#include <asm/sn/pcidev.h>
L
Linus Torvalds 已提交
20 21 22 23 24 25 26
#include <asm/sn/shub_mmr.h>
#include <asm/sn/sn_sal.h>

static void force_interrupt(int irq);
static void register_intr_pda(struct sn_irq_info *sn_irq_info);
static void unregister_intr_pda(struct sn_irq_info *sn_irq_info);

L
Len Brown 已提交
27
int sn_force_interrupt_flag = 1;
L
Linus Torvalds 已提交
28
extern int sn_ioif_inited;
M
Mark Maule 已提交
29
struct list_head **sn_irq_lh;
I
Ingo Molnar 已提交
30
static DEFINE_SPINLOCK(sn_irq_info_lock); /* non-IRQ lock */
L
Linus Torvalds 已提交
31

M
Mark Maule 已提交
32 33
u64 sn_intr_alloc(nasid_t local_nasid, int local_widget,
				     struct sn_irq_info *sn_irq_info,
L
Linus Torvalds 已提交
34 35 36 37 38 39 40 41 42
				     int req_irq, nasid_t req_nasid,
				     int req_slice)
{
	struct ia64_sal_retval ret_stuff;
	ret_stuff.status = 0;
	ret_stuff.v0 = 0;

	SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
			(u64) SAL_INTR_ALLOC, (u64) local_nasid,
M
Mark Maule 已提交
43
			(u64) local_widget, __pa(sn_irq_info), (u64) req_irq,
L
Linus Torvalds 已提交
44
			(u64) req_nasid, (u64) req_slice);
M
Mark Maule 已提交
45

L
Linus Torvalds 已提交
46 47 48
	return ret_stuff.status;
}

M
Mark Maule 已提交
49
void sn_intr_free(nasid_t local_nasid, int local_widget,
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
				struct sn_irq_info *sn_irq_info)
{
	struct ia64_sal_retval ret_stuff;
	ret_stuff.status = 0;
	ret_stuff.v0 = 0;

	SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
			(u64) SAL_INTR_FREE, (u64) local_nasid,
			(u64) local_widget, (u64) sn_irq_info->irq_irq,
			(u64) sn_irq_info->irq_cookie, 0, 0);
}

static unsigned int sn_startup_irq(unsigned int irq)
{
	return 0;
}

static void sn_shutdown_irq(unsigned int irq)
{
}

static void sn_disable_irq(unsigned int irq)
{
}

static void sn_enable_irq(unsigned int irq)
{
}

static void sn_ack_irq(unsigned int irq)
{
J
Jes Sorensen 已提交
81
	u64 event_occurred, mask;
L
Linus Torvalds 已提交
82 83

	irq = irq & 0xff;
J
Jes Sorensen 已提交
84
	event_occurred = HUB_L((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED));
85
	mask = event_occurred & SH_ALL_INT_MASK;
J
Jes Sorensen 已提交
86
	HUB_S((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS), mask);
L
Linus Torvalds 已提交
87 88
	__set_bit(irq, (volatile void *)pda->sn_in_service_ivecs);

89
	move_native_irq(irq);
L
Linus Torvalds 已提交
90 91 92 93 94
}

static void sn_end_irq(unsigned int irq)
{
	int ivec;
95
	u64 event_occurred;
L
Linus Torvalds 已提交
96 97 98

	ivec = irq & 0xff;
	if (ivec == SGI_UART_VECTOR) {
99
		event_occurred = HUB_L((u64*)LOCAL_MMR_ADDR (SH_EVENT_OCCURRED));
100
		/* If the UART bit is set here, we may have received an
L
Linus Torvalds 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113
		 * interrupt from the UART that the driver missed.  To
		 * make sure, we IPI ourselves to force us to look again.
		 */
		if (event_occurred & SH_EVENT_OCCURRED_UART_INT_MASK) {
			platform_send_ipi(smp_processor_id(), SGI_UART_VECTOR,
					  IA64_IPI_DM_INT, 0);
		}
	}
	__clear_bit(ivec, (volatile void *)pda->sn_in_service_ivecs);
	if (sn_force_interrupt_flag)
		force_interrupt(irq);
}

114 115
static void sn_irq_info_free(struct rcu_head *head);

M
Mark Maule 已提交
116 117
struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info,
				       nasid_t nasid, int slice)
L
Linus Torvalds 已提交
118
{
M
Mark Maule 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	int vector;
	int cpuphys;
	int64_t bridge;
	int local_widget, status;
	nasid_t local_nasid;
	struct sn_irq_info *new_irq_info;
	struct sn_pcibus_provider *pci_provider;

	new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC);
	if (new_irq_info == NULL)
		return NULL;

	memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info));

	bridge = (u64) new_irq_info->irq_bridge;
	if (!bridge) {
		kfree(new_irq_info);
		return NULL; /* irq is not a device interrupt */
	}
L
Linus Torvalds 已提交
138

M
Mark Maule 已提交
139
	local_nasid = NASID_GET(bridge);
L
Linus Torvalds 已提交
140

M
Mark Maule 已提交
141 142 143 144
	if (local_nasid & 1)
		local_widget = TIO_SWIN_WIDGETNUM(bridge);
	else
		local_widget = SWIN_WIDGETNUM(bridge);
L
Linus Torvalds 已提交
145

M
Mark Maule 已提交
146 147 148 149 150
	vector = sn_irq_info->irq_irq;
	/* Free the old PROM new_irq_info structure */
	sn_intr_free(local_nasid, local_widget, new_irq_info);
	/* Update kernels new_irq_info with new target info */
	unregister_intr_pda(new_irq_info);
L
Linus Torvalds 已提交
151

M
Mark Maule 已提交
152 153 154 155
	/* allocate a new PROM new_irq_info struct */
	status = sn_intr_alloc(local_nasid, local_widget,
			       new_irq_info, vector,
			       nasid, slice);
L
Linus Torvalds 已提交
156

M
Mark Maule 已提交
157 158 159 160 161
	/* SAL call failed */
	if (status) {
		kfree(new_irq_info);
		return NULL;
	}
162

M
Mark Maule 已提交
163 164 165
	cpuphys = nasid_slice_to_cpuid(nasid, slice);
	new_irq_info->irq_cpuid = cpuphys;
	register_intr_pda(new_irq_info);
L
Linus Torvalds 已提交
166

M
Mark Maule 已提交
167
	pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type];
168

M
Mark Maule 已提交
169 170 171 172 173 174 175
	/*
	 * If this represents a line interrupt, target it.  If it's
	 * an msi (irq_int_bit < 0), it's already targeted.
	 */
	if (new_irq_info->irq_int_bit >= 0 &&
	    pci_provider && pci_provider->target_interrupt)
		(pci_provider->target_interrupt)(new_irq_info);
176

M
Mark Maule 已提交
177 178 179 180
	spin_lock(&sn_irq_info_lock);
	list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
	spin_unlock(&sn_irq_info_lock);
	call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
L
Linus Torvalds 已提交
181 182

#ifdef CONFIG_SMP
M
Mark Maule 已提交
183
	set_irq_affinity_info((vector & 0xff), cpuphys, 0);
L
Linus Torvalds 已提交
184
#endif
M
Mark Maule 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

	return new_irq_info;
}

static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
{
	struct sn_irq_info *sn_irq_info, *sn_irq_info_safe;
	nasid_t nasid;
	int slice;

	nasid = cpuid_to_nasid(first_cpu(mask));
	slice = cpuid_to_slice(first_cpu(mask));

	list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe,
				 sn_irq_lh[irq], list)
		(void)sn_retarget_vector(sn_irq_info, nasid, slice);
L
Linus Torvalds 已提交
201 202 203
}

struct hw_interrupt_type irq_type_sn = {
204 205 206 207 208 209 210 211
	.typename	= "SN hub",
	.startup	= sn_startup_irq,
	.shutdown	= sn_shutdown_irq,
	.enable		= sn_enable_irq,
	.disable	= sn_disable_irq,
	.ack		= sn_ack_irq,
	.end		= sn_end_irq,
	.set_affinity	= sn_set_affinity_irq
L
Linus Torvalds 已提交
212 213 214 215 216 217 218 219 220 221 222 223
};

unsigned int sn_local_vector_to_irq(u8 vector)
{
	return (CPU_VECTOR_TO_IRQ(smp_processor_id(), vector));
}

void sn_irq_init(void)
{
	int i;
	irq_desc_t *base_desc = irq_desc;

224 225 226
	ia64_first_device_vector = IA64_SN2_FIRST_DEVICE_VECTOR;
	ia64_last_device_vector = IA64_SN2_LAST_DEVICE_VECTOR;

L
Linus Torvalds 已提交
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	for (i = 0; i < NR_IRQS; i++) {
		if (base_desc[i].handler == &no_irq_type) {
			base_desc[i].handler = &irq_type_sn;
		}
	}
}

static void register_intr_pda(struct sn_irq_info *sn_irq_info)
{
	int irq = sn_irq_info->irq_irq;
	int cpu = sn_irq_info->irq_cpuid;

	if (pdacpu(cpu)->sn_last_irq < irq) {
		pdacpu(cpu)->sn_last_irq = irq;
	}

J
Jes Sorensen 已提交
243
	if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq)
L
Linus Torvalds 已提交
244 245 246 247 248 249 250 251 252 253
		pdacpu(cpu)->sn_first_irq = irq;
}

static void unregister_intr_pda(struct sn_irq_info *sn_irq_info)
{
	int irq = sn_irq_info->irq_irq;
	int cpu = sn_irq_info->irq_cpuid;
	struct sn_irq_info *tmp_irq_info;
	int i, foundmatch;

254
	rcu_read_lock();
L
Linus Torvalds 已提交
255 256
	if (pdacpu(cpu)->sn_last_irq == irq) {
		foundmatch = 0;
257 258 259 260 261
		for (i = pdacpu(cpu)->sn_last_irq - 1;
		     i && !foundmatch; i--) {
			list_for_each_entry_rcu(tmp_irq_info,
						sn_irq_lh[i],
						list) {
L
Linus Torvalds 已提交
262
				if (tmp_irq_info->irq_cpuid == cpu) {
263
					foundmatch = 1;
L
Linus Torvalds 已提交
264 265 266 267 268 269 270 271 272
					break;
				}
			}
		}
		pdacpu(cpu)->sn_last_irq = i;
	}

	if (pdacpu(cpu)->sn_first_irq == irq) {
		foundmatch = 0;
273 274 275 276 277
		for (i = pdacpu(cpu)->sn_first_irq + 1;
		     i < NR_IRQS && !foundmatch; i++) {
			list_for_each_entry_rcu(tmp_irq_info,
						sn_irq_lh[i],
						list) {
L
Linus Torvalds 已提交
278
				if (tmp_irq_info->irq_cpuid == cpu) {
279
					foundmatch = 1;
L
Linus Torvalds 已提交
280 281 282 283 284 285
					break;
				}
			}
		}
		pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : i);
	}
286
	rcu_read_unlock();
L
Linus Torvalds 已提交
287 288
}

289
static void sn_irq_info_free(struct rcu_head *head)
L
Linus Torvalds 已提交
290 291 292
{
	struct sn_irq_info *sn_irq_info;

293
	sn_irq_info = container_of(head, struct sn_irq_info, rcu);
L
Linus Torvalds 已提交
294 295 296 297 298 299 300 301 302
	kfree(sn_irq_info);
}

void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info)
{
	nasid_t nasid = sn_irq_info->irq_nasid;
	int slice = sn_irq_info->irq_slice;
	int cpu = nasid_slice_to_cpuid(nasid, slice);

303
	pci_dev_get(pci_dev);
L
Linus Torvalds 已提交
304 305 306 307
	sn_irq_info->irq_cpuid = cpu;
	sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev);

	/* link it into the sn_irq[irq] list */
308 309
	spin_lock(&sn_irq_info_lock);
	list_add_rcu(&sn_irq_info->list, sn_irq_lh[sn_irq_info->irq_irq]);
310
	reserve_irq_vector(sn_irq_info->irq_irq);
311
	spin_unlock(&sn_irq_info_lock);
L
Linus Torvalds 已提交
312

J
Jes Sorensen 已提交
313
	register_intr_pda(sn_irq_info);
L
Linus Torvalds 已提交
314 315
}

316 317 318 319 320 321 322 323 324
void sn_irq_unfixup(struct pci_dev *pci_dev)
{
	struct sn_irq_info *sn_irq_info;

	/* Only cleanup IRQ stuff if this device has a host bus context */
	if (!SN_PCIDEV_BUSSOFT(pci_dev))
		return;

	sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info;
325 326 327
	if (!sn_irq_info)
		return;
	if (!sn_irq_info->irq_irq) {
328
		kfree(sn_irq_info);
329
		return;
330
	}
331 332 333 334 335

	unregister_intr_pda(sn_irq_info);
	spin_lock(&sn_irq_info_lock);
	list_del_rcu(&sn_irq_info->list);
	spin_unlock(&sn_irq_info_lock);
336 337
	if (list_empty(sn_irq_lh[sn_irq_info->irq_irq]))
		free_irq_vector(sn_irq_info->irq_irq);
338 339
	call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
	pci_dev_put(pci_dev);
340

341 342
}

343 344 345 346 347 348 349 350 351 352
static inline void
sn_call_force_intr_provider(struct sn_irq_info *sn_irq_info)
{
	struct sn_pcibus_provider *pci_provider;

	pci_provider = sn_pci_provider[sn_irq_info->irq_bridge_type];
	if (pci_provider && pci_provider->force_interrupt)
		(*pci_provider->force_interrupt)(sn_irq_info);
}

L
Linus Torvalds 已提交
353 354 355 356 357 358
static void force_interrupt(int irq)
{
	struct sn_irq_info *sn_irq_info;

	if (!sn_ioif_inited)
		return;
359 360

	rcu_read_lock();
361 362 363
	list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[irq], list)
		sn_call_force_intr_provider(sn_irq_info);

364
	rcu_read_unlock();
L
Linus Torvalds 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377
}

/*
 * Check for lost interrupts.  If the PIC int_status reg. says that
 * an interrupt has been sent, but not handled, and the interrupt
 * is not pending in either the cpu irr regs or in the soft irr regs,
 * and the interrupt is not in service, then the interrupt may have
 * been lost.  Force an interrupt on that pin.  It is possible that
 * the interrupt is in flight, so we may generate a spurious interrupt,
 * but we should never miss a real lost interrupt.
 */
static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info)
{
378
	u64 regval;
L
Linus Torvalds 已提交
379 380 381
	struct pcidev_info *pcidev_info;
	struct pcibus_info *pcibus_info;

382 383 384 385 386 387 388 389
	/*
	 * Bridge types attached to TIO (anything but PIC) do not need this WAR
	 * since they do not target Shub II interrupt registers.  If that
	 * ever changes, this check needs to accomodate.
	 */
	if (sn_irq_info->irq_bridge_type != PCIIO_ASIC_TYPE_PIC)
		return;

L
Linus Torvalds 已提交
390 391 392 393 394 395 396 397 398
	pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
	if (!pcidev_info)
		return;

	pcibus_info =
	    (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
	    pdi_pcibus_info;
	regval = pcireg_intr_status_get(pcibus_info);

399
	if (!ia64_get_irr(irq_to_vector(irq))) {
400 401 402 403 404 405
		if (!test_bit(irq, pda->sn_in_service_ivecs)) {
			regval &= 0xff;
			if (sn_irq_info->irq_int_bit & regval &
			    sn_irq_info->irq_last_intr) {
				regval &= ~(sn_irq_info->irq_int_bit & regval);
				sn_call_force_intr_provider(sn_irq_info);
L
Linus Torvalds 已提交
406 407 408 409 410 411 412 413
			}
		}
	}
	sn_irq_info->irq_last_intr = regval;
}

void sn_lb_int_war_check(void)
{
414
	struct sn_irq_info *sn_irq_info;
L
Linus Torvalds 已提交
415 416 417 418
	int i;

	if (!sn_ioif_inited || pda->sn_first_irq == 0)
		return;
419 420

	rcu_read_lock();
L
Linus Torvalds 已提交
421
	for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) {
422
		list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[i], list) {
423
			sn_check_intr(i, sn_irq_info);
L
Linus Torvalds 已提交
424 425
		}
	}
426 427 428
	rcu_read_unlock();
}

J
Jes Sorensen 已提交
429
void __init sn_irq_lh_init(void)
430 431 432 433 434 435 436 437 438 439 440 441 442 443
{
	int i;

	sn_irq_lh = kmalloc(sizeof(struct list_head *) * NR_IRQS, GFP_KERNEL);
	if (!sn_irq_lh)
		panic("SN PCI INIT: Failed to allocate memory for PCI init\n");

	for (i = 0; i < NR_IRQS; i++) {
		sn_irq_lh[i] = kmalloc(sizeof(struct list_head), GFP_KERNEL);
		if (!sn_irq_lh[i])
			panic("SN PCI INIT: Failed IRQ memory allocation\n");

		INIT_LIST_HEAD(sn_irq_lh[i]);
	}
L
Linus Torvalds 已提交
444
}