xref: /openbmc/linux/arch/mips/dec/kn02-irq.c (revision 87c2ce3b)
1 /*
2  *	linux/arch/mips/dec/kn02-irq.c
3  *
4  *	DECstation 5000/200 (KN02) Control and Status Register
5  *	interrupts.
6  *
7  *	Copyright (c) 2002, 2003, 2005  Maciej W. Rozycki
8  *
9  *	This program is free software; you can redistribute it and/or
10  *	modify it under the terms of the GNU General Public License
11  *	as published by the Free Software Foundation; either version
12  *	2 of the License, or (at your option) any later version.
13  */
14 
15 #include <linux/init.h>
16 #include <linux/irq.h>
17 #include <linux/spinlock.h>
18 #include <linux/types.h>
19 
20 #include <asm/dec/kn02.h>
21 
22 
23 /*
24  * Bits 7:0 of the Control Register are write-only -- the
25  * corresponding bits of the Status Register have a different
26  * meaning.  Hence we use a cache.  It speeds up things a bit
27  * as well.
28  *
29  * There is no default value -- it has to be initialized.
30  */
31 u32 cached_kn02_csr;
32 DEFINE_SPINLOCK(kn02_lock);
33 
34 
35 static int kn02_irq_base;
36 
37 
38 static inline void unmask_kn02_irq(unsigned int irq)
39 {
40 	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
41 						       KN02_CSR);
42 
43 	cached_kn02_csr |= (1 << (irq - kn02_irq_base + 16));
44 	*csr = cached_kn02_csr;
45 }
46 
47 static inline void mask_kn02_irq(unsigned int irq)
48 {
49 	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
50 						       KN02_CSR);
51 
52 	cached_kn02_csr &= ~(1 << (irq - kn02_irq_base + 16));
53 	*csr = cached_kn02_csr;
54 }
55 
56 static inline void enable_kn02_irq(unsigned int irq)
57 {
58 	unsigned long flags;
59 
60 	spin_lock_irqsave(&kn02_lock, flags);
61 	unmask_kn02_irq(irq);
62 	spin_unlock_irqrestore(&kn02_lock, flags);
63 }
64 
65 static inline void disable_kn02_irq(unsigned int irq)
66 {
67 	unsigned long flags;
68 
69 	spin_lock_irqsave(&kn02_lock, flags);
70 	mask_kn02_irq(irq);
71 	spin_unlock_irqrestore(&kn02_lock, flags);
72 }
73 
74 
75 static unsigned int startup_kn02_irq(unsigned int irq)
76 {
77 	enable_kn02_irq(irq);
78 	return 0;
79 }
80 
81 #define shutdown_kn02_irq disable_kn02_irq
82 
83 static void ack_kn02_irq(unsigned int irq)
84 {
85 	spin_lock(&kn02_lock);
86 	mask_kn02_irq(irq);
87 	spin_unlock(&kn02_lock);
88 	iob();
89 }
90 
91 static void end_kn02_irq(unsigned int irq)
92 {
93 	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
94 		enable_kn02_irq(irq);
95 }
96 
97 static struct hw_interrupt_type kn02_irq_type = {
98 	.typename = "KN02-CSR",
99 	.startup = startup_kn02_irq,
100 	.shutdown = shutdown_kn02_irq,
101 	.enable = enable_kn02_irq,
102 	.disable = disable_kn02_irq,
103 	.ack = ack_kn02_irq,
104 	.end = end_kn02_irq,
105 };
106 
107 
108 void __init init_kn02_irqs(int base)
109 {
110 	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
111 						       KN02_CSR);
112 	unsigned long flags;
113 	int i;
114 
115 	/* Mask interrupts. */
116 	spin_lock_irqsave(&kn02_lock, flags);
117 	cached_kn02_csr &= ~KN02_CSR_IOINTEN;
118 	*csr = cached_kn02_csr;
119 	iob();
120 	spin_unlock_irqrestore(&kn02_lock, flags);
121 
122 	for (i = base; i < base + KN02_IRQ_LINES; i++) {
123 		irq_desc[i].status = IRQ_DISABLED;
124 		irq_desc[i].action = 0;
125 		irq_desc[i].depth = 1;
126 		irq_desc[i].handler = &kn02_irq_type;
127 	}
128 
129 	kn02_irq_base = base;
130 }
131