xref: /openbmc/linux/arch/powerpc/sysdev/cpm2_pic.c (revision 7bf811a8)
1b0c110b4SVitaly Bordug /*
2b0c110b4SVitaly Bordug  * Platform information definitions.
3b0c110b4SVitaly Bordug  *
4b0c110b4SVitaly Bordug  * Copied from arch/ppc/syslib/cpm2_pic.c with minor subsequent updates
5b0c110b4SVitaly Bordug  * to make in work in arch/powerpc/. Original (c) belongs to Dan Malek.
6b0c110b4SVitaly Bordug  *
7b0c110b4SVitaly Bordug  * Author:  Vitaly Bordug <vbordug@ru.mvista.com>
8b0c110b4SVitaly Bordug  *
9b0c110b4SVitaly Bordug  * 1999-2001 (c) Dan Malek <dan@embeddedalley.com>
10b0c110b4SVitaly Bordug  * 2006 (c) MontaVista Software, Inc.
11b0c110b4SVitaly Bordug  *
12b0c110b4SVitaly Bordug  * This file is licensed under the terms of the GNU General Public License
13b0c110b4SVitaly Bordug  * version 2. This program is licensed "as is" without any warranty of any
14b0c110b4SVitaly Bordug  * kind, whether express or implied.
15b0c110b4SVitaly Bordug  */
16b0c110b4SVitaly Bordug 
17b0c110b4SVitaly Bordug /* The CPM2 internal interrupt controller.  It is usually
18b0c110b4SVitaly Bordug  * the only interrupt controller.
19b0c110b4SVitaly Bordug  * There are two 32-bit registers (high/low) for up to 64
20b0c110b4SVitaly Bordug  * possible interrupts.
21b0c110b4SVitaly Bordug  *
22b0c110b4SVitaly Bordug  * Now, the fun starts.....Interrupt Numbers DO NOT MAP
23b0c110b4SVitaly Bordug  * in a simple arithmetic fashion to mask or pending registers.
24b0c110b4SVitaly Bordug  * That is, interrupt 4 does not map to bit position 4.
25b0c110b4SVitaly Bordug  * We create two tables, indexed by vector number, to indicate
26b0c110b4SVitaly Bordug  * which register to use and which bit in the register to use.
27b0c110b4SVitaly Bordug  */
28b0c110b4SVitaly Bordug 
29b0c110b4SVitaly Bordug #include <linux/stddef.h>
30b0c110b4SVitaly Bordug #include <linux/init.h>
31b0c110b4SVitaly Bordug #include <linux/sched.h>
32b0c110b4SVitaly Bordug #include <linux/signal.h>
33b0c110b4SVitaly Bordug #include <linux/irq.h>
34b0c110b4SVitaly Bordug 
35b0c110b4SVitaly Bordug #include <asm/immap_cpm2.h>
36b0c110b4SVitaly Bordug #include <asm/mpc8260.h>
37b0c110b4SVitaly Bordug #include <asm/io.h>
38b0c110b4SVitaly Bordug #include <asm/prom.h>
3973844ecbSVitaly Bordug #include <asm/fs_pd.h>
40b0c110b4SVitaly Bordug 
41b0c110b4SVitaly Bordug #include "cpm2_pic.h"
42b0c110b4SVitaly Bordug 
4373844ecbSVitaly Bordug /* External IRQS */
4473844ecbSVitaly Bordug #define CPM2_IRQ_EXT1		19
4573844ecbSVitaly Bordug #define CPM2_IRQ_EXT7		25
4673844ecbSVitaly Bordug 
4773844ecbSVitaly Bordug /* Port C IRQS */
4873844ecbSVitaly Bordug #define CPM2_IRQ_PORTC15	48
4973844ecbSVitaly Bordug #define CPM2_IRQ_PORTC0		63
5073844ecbSVitaly Bordug 
51449012daSScott Wood static intctl_cpm2_t __iomem *cpm2_intctl;
5273844ecbSVitaly Bordug 
53b0c110b4SVitaly Bordug static struct irq_host *cpm2_pic_host;
54b0c110b4SVitaly Bordug #define NR_MASK_WORDS   ((NR_IRQS + 31) / 32)
55b0c110b4SVitaly Bordug static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
56b0c110b4SVitaly Bordug 
57b0c110b4SVitaly Bordug static const u_char irq_to_siureg[] = {
58b0c110b4SVitaly Bordug 	1, 1, 1, 1, 1, 1, 1, 1,
59b0c110b4SVitaly Bordug 	1, 1, 1, 1, 1, 1, 1, 1,
60b0c110b4SVitaly Bordug 	0, 0, 0, 0, 0, 0, 0, 0,
61b0c110b4SVitaly Bordug 	0, 0, 0, 0, 0, 0, 0, 0,
62b0c110b4SVitaly Bordug 	1, 1, 1, 1, 1, 1, 1, 1,
63b0c110b4SVitaly Bordug 	1, 1, 1, 1, 1, 1, 1, 1,
64b0c110b4SVitaly Bordug 	0, 0, 0, 0, 0, 0, 0, 0,
65b0c110b4SVitaly Bordug 	0, 0, 0, 0, 0, 0, 0, 0
66b0c110b4SVitaly Bordug };
67b0c110b4SVitaly Bordug 
68b0c110b4SVitaly Bordug /* bit numbers do not match the docs, these are precomputed so the bit for
69b0c110b4SVitaly Bordug  * a given irq is (1 << irq_to_siubit[irq]) */
70b0c110b4SVitaly Bordug static const u_char irq_to_siubit[] = {
71b0c110b4SVitaly Bordug 	 0, 15, 14, 13, 12, 11, 10,  9,
72b0c110b4SVitaly Bordug 	 8,  7,  6,  5,  4,  3,  2,  1,
73b0c110b4SVitaly Bordug 	 2,  1,  0, 14, 13, 12, 11, 10,
74b0c110b4SVitaly Bordug 	 9,  8,  7,  6,  5,  4,  3,  0,
75b0c110b4SVitaly Bordug 	31, 30, 29, 28, 27, 26, 25, 24,
76b0c110b4SVitaly Bordug 	23, 22, 21, 20, 19, 18, 17, 16,
77b0c110b4SVitaly Bordug 	16, 17, 18, 19, 20, 21, 22, 23,
78b0c110b4SVitaly Bordug 	24, 25, 26, 27, 28, 29, 30, 31,
79b0c110b4SVitaly Bordug };
80b0c110b4SVitaly Bordug 
81c47eefa6SLennert Buytenhek static void cpm2_mask_irq(struct irq_data *d)
82b0c110b4SVitaly Bordug {
83b0c110b4SVitaly Bordug 	int	bit, word;
84c47eefa6SLennert Buytenhek 	unsigned int irq_nr = virq_to_hw(d->irq);
85b0c110b4SVitaly Bordug 
86b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
87b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
88b0c110b4SVitaly Bordug 
89b0c110b4SVitaly Bordug 	ppc_cached_irq_mask[word] &= ~(1 << bit);
9073844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
91b0c110b4SVitaly Bordug }
92b0c110b4SVitaly Bordug 
93c47eefa6SLennert Buytenhek static void cpm2_unmask_irq(struct irq_data *d)
94b0c110b4SVitaly Bordug {
95b0c110b4SVitaly Bordug 	int	bit, word;
96c47eefa6SLennert Buytenhek 	unsigned int irq_nr = virq_to_hw(d->irq);
97b0c110b4SVitaly Bordug 
98b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
99b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
100b0c110b4SVitaly Bordug 
101b0c110b4SVitaly Bordug 	ppc_cached_irq_mask[word] |= 1 << bit;
10273844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
103b0c110b4SVitaly Bordug }
104b0c110b4SVitaly Bordug 
105c47eefa6SLennert Buytenhek static void cpm2_ack(struct irq_data *d)
106b0c110b4SVitaly Bordug {
107b0c110b4SVitaly Bordug 	int	bit, word;
108c47eefa6SLennert Buytenhek 	unsigned int irq_nr = virq_to_hw(d->irq);
109b0c110b4SVitaly Bordug 
110b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
111b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
112b0c110b4SVitaly Bordug 
11373844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_sipnrh + word, 1 << bit);
114b0c110b4SVitaly Bordug }
115b0c110b4SVitaly Bordug 
116c47eefa6SLennert Buytenhek static void cpm2_end_irq(struct irq_data *d)
117b0c110b4SVitaly Bordug {
118b0c110b4SVitaly Bordug 	int	bit, word;
119c47eefa6SLennert Buytenhek 	unsigned int irq_nr = virq_to_hw(d->irq);
120b0c110b4SVitaly Bordug 
121b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
122b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
123b0c110b4SVitaly Bordug 
124b0c110b4SVitaly Bordug 	ppc_cached_irq_mask[word] |= 1 << bit;
12573844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
12673844ecbSVitaly Bordug 
127b0c110b4SVitaly Bordug 	/*
128b0c110b4SVitaly Bordug 	 * Work around large numbers of spurious IRQs on PowerPC 82xx
129b0c110b4SVitaly Bordug 	 * systems.
130b0c110b4SVitaly Bordug 	 */
131b0c110b4SVitaly Bordug 	mb();
132b0c110b4SVitaly Bordug }
133b0c110b4SVitaly Bordug 
134c47eefa6SLennert Buytenhek static int cpm2_set_irq_type(struct irq_data *d, unsigned int flow_type)
13573844ecbSVitaly Bordug {
136c47eefa6SLennert Buytenhek 	unsigned int src = virq_to_hw(d->irq);
13773844ecbSVitaly Bordug 	unsigned int vold, vnew, edibit;
13873844ecbSVitaly Bordug 
139b22b97c1SMark Ware 	/* Port C interrupts are either IRQ_TYPE_EDGE_FALLING or
140b22b97c1SMark Ware 	 * IRQ_TYPE_EDGE_BOTH (default).  All others are IRQ_TYPE_EDGE_FALLING
141b22b97c1SMark Ware 	 * or IRQ_TYPE_LEVEL_LOW (default)
142b22b97c1SMark Ware 	 */
143b22b97c1SMark Ware 	if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) {
144b22b97c1SMark Ware 		if (flow_type == IRQ_TYPE_NONE)
145b22b97c1SMark Ware 			flow_type = IRQ_TYPE_EDGE_BOTH;
146b22b97c1SMark Ware 
147b22b97c1SMark Ware 		if (flow_type != IRQ_TYPE_EDGE_BOTH &&
148b22b97c1SMark Ware 		    flow_type != IRQ_TYPE_EDGE_FALLING)
149b22b97c1SMark Ware 			goto err_sense;
150b22b97c1SMark Ware 	} else {
15173844ecbSVitaly Bordug 		if (flow_type == IRQ_TYPE_NONE)
15273844ecbSVitaly Bordug 			flow_type = IRQ_TYPE_LEVEL_LOW;
15373844ecbSVitaly Bordug 
154b22b97c1SMark Ware 		if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
155b22b97c1SMark Ware 			goto err_sense;
15673844ecbSVitaly Bordug 	}
15773844ecbSVitaly Bordug 
158a28ab38cSThomas Gleixner 	irqd_set_trigger_type(d, flow_type);
159a28ab38cSThomas Gleixner 	if (flow_type & IRQ_TYPE_LEVEL_LOW)
160a28ab38cSThomas Gleixner 		__set_irq_handler_unlocked(d->irq, handle_level_irq);
161a28ab38cSThomas Gleixner 	else
162a28ab38cSThomas Gleixner 		__set_irq_handler_unlocked(d->irq, handle_edge_irq);
16373844ecbSVitaly Bordug 
16473844ecbSVitaly Bordug 	/* internal IRQ senses are LEVEL_LOW
16573844ecbSVitaly Bordug 	 * EXT IRQ and Port C IRQ senses are programmable
16673844ecbSVitaly Bordug 	 */
16773844ecbSVitaly Bordug 	if (src >= CPM2_IRQ_EXT1 && src <= CPM2_IRQ_EXT7)
16873844ecbSVitaly Bordug 			edibit = (14 - (src - CPM2_IRQ_EXT1));
16973844ecbSVitaly Bordug 	else
17073844ecbSVitaly Bordug 		if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0)
1717f3ea17fSpaulfax 			edibit = (31 - (CPM2_IRQ_PORTC0 - src));
17273844ecbSVitaly Bordug 		else
173a28ab38cSThomas Gleixner 			return (flow_type & IRQ_TYPE_LEVEL_LOW) ?
174a28ab38cSThomas Gleixner 				IRQ_SET_MASK_OK_NOCOPY : -EINVAL;
17573844ecbSVitaly Bordug 
17673844ecbSVitaly Bordug 	vold = in_be32(&cpm2_intctl->ic_siexr);
17773844ecbSVitaly Bordug 
17873844ecbSVitaly Bordug 	if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING)
17973844ecbSVitaly Bordug 		vnew = vold | (1 << edibit);
18073844ecbSVitaly Bordug 	else
18173844ecbSVitaly Bordug 		vnew = vold & ~(1 << edibit);
18273844ecbSVitaly Bordug 
18373844ecbSVitaly Bordug 	if (vold != vnew)
18473844ecbSVitaly Bordug 		out_be32(&cpm2_intctl->ic_siexr, vnew);
185a28ab38cSThomas Gleixner 	return IRQ_SET_MASK_OK_NOCOPY;
186b22b97c1SMark Ware 
187b22b97c1SMark Ware err_sense:
188b22b97c1SMark Ware 	pr_err("CPM2 PIC: sense type 0x%x not supported\n", flow_type);
189b22b97c1SMark Ware 	return -EINVAL;
19073844ecbSVitaly Bordug }
19173844ecbSVitaly Bordug 
192b0c110b4SVitaly Bordug static struct irq_chip cpm2_pic = {
193b27df672SThomas Gleixner 	.name = "CPM2 SIU",
194c47eefa6SLennert Buytenhek 	.irq_mask = cpm2_mask_irq,
195c47eefa6SLennert Buytenhek 	.irq_unmask = cpm2_unmask_irq,
196c47eefa6SLennert Buytenhek 	.irq_ack = cpm2_ack,
197c47eefa6SLennert Buytenhek 	.irq_eoi = cpm2_end_irq,
198c47eefa6SLennert Buytenhek 	.irq_set_type = cpm2_set_irq_type,
1997bf811a8SThomas Gleixner 	.flags = IRQCHIP_EOI_IF_HANDLED,
200b0c110b4SVitaly Bordug };
201b0c110b4SVitaly Bordug 
20235a84c2fSOlaf Hering unsigned int cpm2_get_irq(void)
203b0c110b4SVitaly Bordug {
204b0c110b4SVitaly Bordug 	int irq;
205b0c110b4SVitaly Bordug 	unsigned long bits;
206b0c110b4SVitaly Bordug 
207b0c110b4SVitaly Bordug        /* For CPM2, read the SIVEC register and shift the bits down
208b0c110b4SVitaly Bordug          * to get the irq number.         */
20973844ecbSVitaly Bordug         bits = in_be32(&cpm2_intctl->ic_sivec);
210b0c110b4SVitaly Bordug         irq = bits >> 26;
211b0c110b4SVitaly Bordug 
212b0c110b4SVitaly Bordug 	if (irq == 0)
213b0c110b4SVitaly Bordug 		return(-1);
21473844ecbSVitaly Bordug 	return irq_linear_revmap(cpm2_pic_host, irq);
215b0c110b4SVitaly Bordug }
216b0c110b4SVitaly Bordug 
217b0c110b4SVitaly Bordug static int cpm2_pic_host_map(struct irq_host *h, unsigned int virq,
218b0c110b4SVitaly Bordug 			  irq_hw_number_t hw)
219b0c110b4SVitaly Bordug {
220b0c110b4SVitaly Bordug 	pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw);
221b0c110b4SVitaly Bordug 
22298488db9SThomas Gleixner 	irq_set_status_flags(virq, IRQ_LEVEL);
223b0c110b4SVitaly Bordug 	set_irq_chip_and_handler(virq, &cpm2_pic, handle_level_irq);
224b0c110b4SVitaly Bordug 	return 0;
225b0c110b4SVitaly Bordug }
226b0c110b4SVitaly Bordug 
227b0c110b4SVitaly Bordug static int cpm2_pic_host_xlate(struct irq_host *h, struct device_node *ct,
22840d50cf7SRoman Fietze 			    const u32 *intspec, unsigned int intsize,
229b0c110b4SVitaly Bordug 			    irq_hw_number_t *out_hwirq, unsigned int *out_flags)
230b0c110b4SVitaly Bordug {
231b0c110b4SVitaly Bordug 	*out_hwirq = intspec[0];
23273844ecbSVitaly Bordug 	if (intsize > 1)
23373844ecbSVitaly Bordug 		*out_flags = intspec[1];
234b0c110b4SVitaly Bordug 	else
235b0c110b4SVitaly Bordug 		*out_flags = IRQ_TYPE_NONE;
236b0c110b4SVitaly Bordug 	return 0;
237b0c110b4SVitaly Bordug }
238b0c110b4SVitaly Bordug 
239b0c110b4SVitaly Bordug static struct irq_host_ops cpm2_pic_host_ops = {
240b0c110b4SVitaly Bordug 	.map = cpm2_pic_host_map,
241b0c110b4SVitaly Bordug 	.xlate = cpm2_pic_host_xlate,
242b0c110b4SVitaly Bordug };
243b0c110b4SVitaly Bordug 
244b0c110b4SVitaly Bordug void cpm2_pic_init(struct device_node *node)
245b0c110b4SVitaly Bordug {
246b0c110b4SVitaly Bordug 	int i;
247b0c110b4SVitaly Bordug 
24873844ecbSVitaly Bordug 	cpm2_intctl = cpm2_map(im_intctl);
24973844ecbSVitaly Bordug 
250b0c110b4SVitaly Bordug 	/* Clear the CPM IRQ controller, in case it has any bits set
251b0c110b4SVitaly Bordug 	 * from the bootloader
252b0c110b4SVitaly Bordug 	 */
253b0c110b4SVitaly Bordug 
254b0c110b4SVitaly Bordug 	/* Mask out everything */
255b0c110b4SVitaly Bordug 
25673844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh, 0x00000000);
25773844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrl, 0x00000000);
258b0c110b4SVitaly Bordug 
259b0c110b4SVitaly Bordug 	wmb();
260b0c110b4SVitaly Bordug 
261b0c110b4SVitaly Bordug 	/* Ack everything */
26273844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_sipnrh, 0xffffffff);
26373844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_sipnrl, 0xffffffff);
264b0c110b4SVitaly Bordug 	wmb();
265b0c110b4SVitaly Bordug 
266b0c110b4SVitaly Bordug 	/* Dummy read of the vector */
26773844ecbSVitaly Bordug 	i = in_be32(&cpm2_intctl->ic_sivec);
268b0c110b4SVitaly Bordug 	rmb();
269b0c110b4SVitaly Bordug 
270b0c110b4SVitaly Bordug 	/* Initialize the default interrupt mapping priorities,
271b0c110b4SVitaly Bordug 	 * in case the boot rom changed something on us.
272b0c110b4SVitaly Bordug 	 */
27373844ecbSVitaly Bordug 	out_be16(&cpm2_intctl->ic_sicr, 0);
27473844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_scprrh, 0x05309770);
27573844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_scprrl, 0x05309770);
276b0c110b4SVitaly Bordug 
277b0c110b4SVitaly Bordug 	/* create a legacy host */
27819fc65b5SMichael Ellerman 	cpm2_pic_host = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR,
27952964f87SMichael Ellerman 				       64, &cpm2_pic_host_ops, 64);
280b0c110b4SVitaly Bordug 	if (cpm2_pic_host == NULL) {
281b0c110b4SVitaly Bordug 		printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
282b0c110b4SVitaly Bordug 		return;
283b0c110b4SVitaly Bordug 	}
284b0c110b4SVitaly Bordug }
285