]> bbs.cooldavid.org Git - net-next-2.6.git/blame - arch/ppc/syslib/ppc4xx_pic.c
Linux-2.6.12-rc2
[net-next-2.6.git] / arch / ppc / syslib / ppc4xx_pic.c
CommitLineData
1da177e4
LT
1/*
2 * arch/ppc/syslib/ppc4xx_pic.c
3 *
4 * Interrupt controller driver for PowerPC 4xx-based processors.
5 *
6 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
7 * Copyright (c) 2004, 2005 Zultys Technologies
8 *
9 * Based on original code by
10 * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
11 * Armin Custer <akuster@mvista.com>
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
17*/
18#include <linux/config.h>
19#include <linux/init.h>
20#include <linux/sched.h>
21#include <linux/signal.h>
22#include <linux/stddef.h>
23
24#include <asm/processor.h>
25#include <asm/system.h>
26#include <asm/irq.h>
27#include <asm/ppc4xx_pic.h>
28
29/* See comment in include/arch-ppc/ppc4xx_pic.h
30 * for more info about these two variables
31 */
32extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
33 __attribute__ ((weak));
34extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak));
35
36#define IRQ_MASK_UIC0(irq) (1 << (31 - (irq)))
37#define IRQ_MASK_UICx(irq) (1 << (31 - ((irq) & 0x1f)))
38#define IRQ_MASK_UIC1(irq) IRQ_MASK_UICx(irq)
39#define IRQ_MASK_UIC2(irq) IRQ_MASK_UICx(irq)
40
41#define UIC_HANDLERS(n) \
42static void ppc4xx_uic##n##_enable(unsigned int irq) \
43{ \
44 ppc_cached_irq_mask[n] |= IRQ_MASK_UIC##n(irq); \
45 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
46} \
47 \
48static void ppc4xx_uic##n##_disable(unsigned int irq) \
49{ \
50 ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \
51 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
52 ACK_UIC##n##_PARENT \
53} \
54 \
55static void ppc4xx_uic##n##_ack(unsigned int irq) \
56{ \
57 u32 mask = IRQ_MASK_UIC##n(irq); \
58 ppc_cached_irq_mask[n] &= ~mask; \
59 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
60 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
61 ACK_UIC##n##_PARENT \
62} \
63 \
64static void ppc4xx_uic##n##_end(unsigned int irq) \
65{ \
66 unsigned int status = irq_desc[irq].status; \
67 u32 mask = IRQ_MASK_UIC##n(irq); \
68 if (status & IRQ_LEVEL) { \
69 mtdcr(DCRN_UIC_SR(UIC##n), mask); \
70 ACK_UIC##n##_PARENT \
71 } \
72 if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { \
73 ppc_cached_irq_mask[n] |= mask; \
74 mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
75 } \
76}
77
78#define DECLARE_UIC(n) \
79{ \
80 .typename = "UIC"#n, \
81 .enable = ppc4xx_uic##n##_enable, \
82 .disable = ppc4xx_uic##n##_disable, \
83 .ack = ppc4xx_uic##n##_ack, \
84 .end = ppc4xx_uic##n##_end, \
85} \
86
87#if NR_UICS == 3
88#define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
89#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
90#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
91UIC_HANDLERS(0);
92UIC_HANDLERS(1);
93UIC_HANDLERS(2);
94
95static int ppc4xx_pic_get_irq(struct pt_regs *regs)
96{
97 u32 uicb = mfdcr(DCRN_UIC_MSR(UICB));
98 if (uicb & UICB_UIC0NC)
99 return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0)));
100 else if (uicb & UICB_UIC1NC)
101 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
102 else if (uicb & UICB_UIC2NC)
103 return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
104 else
105 return -1;
106}
107
108static void __init ppc4xx_pic_impl_init(void)
109{
110 /* Configure Base UIC */
111 mtdcr(DCRN_UIC_CR(UICB), 0);
112 mtdcr(DCRN_UIC_TR(UICB), 0);
113 mtdcr(DCRN_UIC_PR(UICB), 0xffffffff);
114 mtdcr(DCRN_UIC_SR(UICB), 0xffffffff);
115 mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC);
116}
117
118#elif NR_UICS == 2
119#define ACK_UIC0_PARENT
120#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
121UIC_HANDLERS(0);
122UIC_HANDLERS(1);
123
124static int ppc4xx_pic_get_irq(struct pt_regs *regs)
125{
126 u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
127 if (uic0 & UIC0_UIC1NC)
128 return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
129 else
130 return uic0 ? 32 - ffs(uic0) : -1;
131}
132
133static void __init ppc4xx_pic_impl_init(void)
134{
135 /* Enable cascade interrupt in UIC0 */
136 ppc_cached_irq_mask[0] |= UIC0_UIC1NC;
137 mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
138 mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
139}
140
141#elif NR_UICS == 1
142#define ACK_UIC0_PARENT
143UIC_HANDLERS(0);
144
145static int ppc4xx_pic_get_irq(struct pt_regs *regs)
146{
147 u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
148 return uic0 ? 32 - ffs(uic0) : -1;
149}
150
151static inline void ppc4xx_pic_impl_init(void)
152{
153}
154#endif
155
156static struct ppc4xx_uic_impl {
157 struct hw_interrupt_type decl;
158 int base; /* Base DCR number */
159} __uic[] = {
160 { .decl = DECLARE_UIC(0), .base = UIC0 },
161#if NR_UICS > 1
162 { .decl = DECLARE_UIC(1), .base = UIC1 },
163#if NR_UICS > 2
164 { .decl = DECLARE_UIC(2), .base = UIC2 },
165#endif
166#endif
167};
168
169static inline int is_level_sensitive(int irq)
170{
171 u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base));
172 return (tr & IRQ_MASK_UICx(irq)) == 0;
173}
174
175void __init ppc4xx_pic_init(void)
176{
177 int i;
178 unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg;
179
180 for (i = 0; i < NR_UICS; ++i) {
181 int base = __uic[i].base;
182
183 /* Disable everything by default */
184 ppc_cached_irq_mask[i] = 0;
185 mtdcr(DCRN_UIC_ER(base), 0);
186
187 /* We don't use critical interrupts */
188 mtdcr(DCRN_UIC_CR(base), 0);
189
190 /* Configure polarity and triggering */
191 if (ppc4xx_core_uic_cfg) {
192 struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i;
193 u32 mask = p->ext_irq_mask;
194 u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask;
195 u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask;
196
197 /* "Fixed" interrupts (on-chip devices) */
198 pr |= p->polarity & ~mask;
199 tr |= p->triggering & ~mask;
200
201 /* Merge external IRQs settings if board port
202 * provided them
203 */
204 if (eirqs && mask) {
205 pr &= ~mask;
206 tr &= ~mask;
207 while (mask) {
208 /* Extract current external IRQ mask */
209 u32 eirq_mask = 1 << __ilog2(mask);
210
211 if (!(*eirqs & IRQ_SENSE_LEVEL))
212 tr |= eirq_mask;
213
214 if (*eirqs & IRQ_POLARITY_POSITIVE)
215 pr |= eirq_mask;
216
217 mask &= ~eirq_mask;
218 ++eirqs;
219 }
220 }
221 mtdcr(DCRN_UIC_PR(base), pr);
222 mtdcr(DCRN_UIC_TR(base), tr);
223 }
224
225 /* ACK any pending interrupts to prevent false
226 * triggering after first enable
227 */
228 mtdcr(DCRN_UIC_SR(base), 0xffffffff);
229 }
230
231 /* Perform optional implementation specific setup
232 * (e.g. enable cascade interrupts for multi-UIC configurations)
233 */
234 ppc4xx_pic_impl_init();
235
236 /* Attach low-level handlers */
237 for (i = 0; i < (NR_UICS << 5); ++i) {
238 irq_desc[i].handler = &__uic[i >> 5].decl;
239 if (is_level_sensitive(i))
240 irq_desc[i].status |= IRQ_LEVEL;
241 }
242
243 ppc_md.get_irq = ppc4xx_pic_get_irq;
244}