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