summaryrefslogtreecommitdiff
path: root/arch/mips/dec/kn02-irq.c
blob: 898bed502a348fb3a7572e8bd65d550a20d0f46c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 *	linux/arch/mips/dec/kn02-irq.c
 *
 *	DECstation 5000/200 (KN02) Control and Status Register
 *	interrupts.
 *
 *	Copyright (c) 2002, 2003, 2005  Maciej W. Rozycki
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 */

#include <linux/init.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/types.h>

#include <asm/dec/kn02.h>


/*
 * Bits 7:0 of the Control Register are write-only -- the
 * corresponding bits of the Status Register have a different
 * meaning.  Hence we use a cache.  It speeds up things a bit
 * as well.
 *
 * There is no default value -- it has to be initialized.
 */
u32 cached_kn02_csr;
DEFINE_SPINLOCK(kn02_lock);


static int kn02_irq_base;


static inline void unmask_kn02_irq(unsigned int irq)
{
	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
						       KN02_CSR);

	cached_kn02_csr |= (1 << (irq - kn02_irq_base + 16));
	*csr = cached_kn02_csr;
}

static inline void mask_kn02_irq(unsigned int irq)
{
	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
						       KN02_CSR);

	cached_kn02_csr &= ~(1 << (irq - kn02_irq_base + 16));
	*csr = cached_kn02_csr;
}

static inline void enable_kn02_irq(unsigned int irq)
{
	unsigned long flags;

	spin_lock_irqsave(&kn02_lock, flags);
	unmask_kn02_irq(irq);
	spin_unlock_irqrestore(&kn02_lock, flags);
}

static inline void disable_kn02_irq(unsigned int irq)
{
	unsigned long flags;

	spin_lock_irqsave(&kn02_lock, flags);
	mask_kn02_irq(irq);
	spin_unlock_irqrestore(&kn02_lock, flags);
}


static unsigned int startup_kn02_irq(unsigned int irq)
{
	enable_kn02_irq(irq);
	return 0;
}

#define shutdown_kn02_irq disable_kn02_irq

static void ack_kn02_irq(unsigned int irq)
{
	spin_lock(&kn02_lock);
	mask_kn02_irq(irq);
	spin_unlock(&kn02_lock);
	iob();
}

static void end_kn02_irq(unsigned int irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
		enable_kn02_irq(irq);
}

static struct hw_interrupt_type kn02_irq_type = {
	.typename = "KN02-CSR",
	.startup = startup_kn02_irq,
	.shutdown = shutdown_kn02_irq,
	.enable = enable_kn02_irq,
	.disable = disable_kn02_irq,
	.ack = ack_kn02_irq,
	.end = end_kn02_irq,
};


void __init init_kn02_irqs(int base)
{
	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
						       KN02_CSR);
	unsigned long flags;
	int i;

	/* Mask interrupts. */
	spin_lock_irqsave(&kn02_lock, flags);
	cached_kn02_csr &= ~KN02_CSR_IOINTEN;
	*csr = cached_kn02_csr;
	iob();
	spin_unlock_irqrestore(&kn02_lock, flags);

	for (i = base; i < base + KN02_IRQ_LINES; i++) {
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = 0;
		irq_desc[i].depth = 1;
		irq_desc[i].handler = &kn02_irq_type;
	}

	kn02_irq_base = base;
}