1/*
2 * Interrupt controller support for IBM Spruce
3 *
4 * Authors: Mark Greer, Matt Porter, and Johnnie Peters
5 *	    mgreer@mvista.com
6 *          mporter@mvista.com
7 *          jpeters@mvista.com
8 *
9 * 2001-2002 (c) MontaVista, Software, Inc.  This file is licensed under
10 * the terms of the GNU General Public License version 2.  This program
11 * is licensed "as is" without any warranty of any kind, whether express
12 * or implied.
13 */
14
15#include <linux/stddef.h>
16#include <linux/init.h>
17#include <linux/sched.h>
18#include <linux/signal.h>
19#include <linux/irq.h>
20
21#include <asm/io.h>
22#include <asm/system.h>
23#include <asm/irq.h>
24
25#include "cpc700.h"
26
27static void
28cpc700_unmask_irq(unsigned int irq)
29{
30	unsigned int tr_bits;
31
32	/*
33	 * IRQ 31 is largest IRQ supported.
34	 * IRQs 17-19 are reserved.
35	 */
36	if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
37		tr_bits = CPC700_IN_32(CPC700_UIC_UICTR);
38
39		if ((tr_bits & (1 << (31 - irq))) == 0) {
40			/* level trigger interrupt, clear bit in status
41			 * register */
42			CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq));
43		}
44
45		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
46		ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq);
47
48		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
49	}
50	return;
51}
52
53static void
54cpc700_mask_irq(unsigned int irq)
55{
56	/*
57	 * IRQ 31 is largest IRQ supported.
58	 * IRQs 17-19 are reserved.
59	 */
60	if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
61		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
62		ppc_cached_irq_mask[0] &=
63			~CPC700_UIC_IRQ_BIT(irq);
64
65		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
66	}
67	return;
68}
69
70static void
71cpc700_mask_and_ack_irq(unsigned int irq)
72{
73	u_int	bit;
74
75	/*
76	 * IRQ 31 is largest IRQ supported.
77	 * IRQs 17-19 are reserved.
78	 */
79	if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
80		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
81		bit = CPC700_UIC_IRQ_BIT(irq);
82
83		ppc_cached_irq_mask[0] &= ~bit;
84		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
85		CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */
86	}
87	return;
88}
89
90static struct hw_interrupt_type cpc700_pic = {
91	.typename = "CPC700 PIC",
92	.enable = cpc700_unmask_irq,
93	.disable = cpc700_mask_irq,
94	.ack = cpc700_mask_and_ack_irq,
95};
96
97__init static void
98cpc700_pic_init_irq(unsigned int irq)
99{
100	unsigned int tmp;
101
102	/* Set interrupt sense */
103	tmp = CPC700_IN_32(CPC700_UIC_UICTR);
104	if (cpc700_irq_assigns[irq][0] == 0) {
105		tmp &= ~CPC700_UIC_IRQ_BIT(irq);
106	} else {
107		tmp |= CPC700_UIC_IRQ_BIT(irq);
108	}
109	CPC700_OUT_32(CPC700_UIC_UICTR, tmp);
110
111	/* Set interrupt polarity */
112	tmp = CPC700_IN_32(CPC700_UIC_UICPR);
113	if (cpc700_irq_assigns[irq][1]) {
114		tmp |= CPC700_UIC_IRQ_BIT(irq);
115	} else {
116		tmp &= ~CPC700_UIC_IRQ_BIT(irq);
117	}
118	CPC700_OUT_32(CPC700_UIC_UICPR, tmp);
119
120	/* Set interrupt critical */
121	tmp = CPC700_IN_32(CPC700_UIC_UICCR);
122	tmp |= CPC700_UIC_IRQ_BIT(irq);
123	CPC700_OUT_32(CPC700_UIC_UICCR, tmp);
124
125	return;
126}
127
128__init void
129cpc700_init_IRQ(void)
130{
131	int i;
132
133	ppc_cached_irq_mask[0] = 0;
134	CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000);    /* Disable all irq's */
135	CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff);    /* Clear cur intrs */
136	CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff);    /* Gen INT not MCP */
137	CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000);    /* Active low */
138	CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000);    /* Level Sensitive */
139	CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI);
140						        /* IRQ 0 is highest */
141
142	for (i = 0; i < 17; i++) {
143		irq_desc[i].chip = &cpc700_pic;
144		cpc700_pic_init_irq(i);
145	}
146
147	for (i = 20; i < 32; i++) {
148		irq_desc[i].chip = &cpc700_pic;
149		cpc700_pic_init_irq(i);
150	}
151
152	return;
153}
154
155
156
157/*
158 * Find the highest IRQ that generating an interrupt, if any.
159 */
160int
161cpc700_get_irq(void)
162{
163	int irq = 0;
164	u_int irq_status, irq_test = 1;
165
166	irq_status = CPC700_IN_32(CPC700_UIC_UICMSR);
167
168	do
169	{
170		if (irq_status & irq_test)
171			break;
172		irq++;
173		irq_test <<= 1;
174	} while (irq < NR_IRQS);
175
176
177	if (irq == NR_IRQS)
178	    irq = 33;
179
180	return (31 - irq);
181}
182