xref: /openbmc/linux/arch/powerpc/sysdev/cpm2_pic.c (revision 7768716d)
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/sched.h>
31b0c110b4SVitaly Bordug #include <linux/signal.h>
32b0c110b4SVitaly Bordug #include <linux/irq.h>
33e6f6390aSChristophe Leroy #include <linux/irqdomain.h>
34b0c110b4SVitaly Bordug 
35b0c110b4SVitaly Bordug #include <asm/immap_cpm2.h>
36b0c110b4SVitaly Bordug #include <asm/io.h>
37b0c110b4SVitaly Bordug 
38b0c110b4SVitaly Bordug #include "cpm2_pic.h"
39b0c110b4SVitaly Bordug 
4073844ecbSVitaly Bordug /* External IRQS */
4173844ecbSVitaly Bordug #define CPM2_IRQ_EXT1		19
4273844ecbSVitaly Bordug #define CPM2_IRQ_EXT7		25
4373844ecbSVitaly Bordug 
4473844ecbSVitaly Bordug /* Port C IRQS */
4573844ecbSVitaly Bordug #define CPM2_IRQ_PORTC15	48
4673844ecbSVitaly Bordug #define CPM2_IRQ_PORTC0		63
4773844ecbSVitaly Bordug 
48449012daSScott Wood static intctl_cpm2_t __iomem *cpm2_intctl;
4973844ecbSVitaly Bordug 
50bae1d8f1SGrant Likely static struct irq_domain *cpm2_pic_host;
514013369fSGrant Likely static unsigned long ppc_cached_irq_mask[2]; /* 2 32-bit registers */
52b0c110b4SVitaly Bordug 
53b0c110b4SVitaly Bordug static const u_char irq_to_siureg[] = {
54b0c110b4SVitaly Bordug 	1, 1, 1, 1, 1, 1, 1, 1,
55b0c110b4SVitaly Bordug 	1, 1, 1, 1, 1, 1, 1, 1,
56b0c110b4SVitaly Bordug 	0, 0, 0, 0, 0, 0, 0, 0,
57b0c110b4SVitaly Bordug 	0, 0, 0, 0, 0, 0, 0, 0,
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 };
63b0c110b4SVitaly Bordug 
64b0c110b4SVitaly Bordug /* bit numbers do not match the docs, these are precomputed so the bit for
65b0c110b4SVitaly Bordug  * a given irq is (1 << irq_to_siubit[irq]) */
66b0c110b4SVitaly Bordug static const u_char irq_to_siubit[] = {
67b0c110b4SVitaly Bordug 	 0, 15, 14, 13, 12, 11, 10,  9,
68b0c110b4SVitaly Bordug 	 8,  7,  6,  5,  4,  3,  2,  1,
69b0c110b4SVitaly Bordug 	 2,  1,  0, 14, 13, 12, 11, 10,
70b0c110b4SVitaly Bordug 	 9,  8,  7,  6,  5,  4,  3,  0,
71b0c110b4SVitaly Bordug 	31, 30, 29, 28, 27, 26, 25, 24,
72b0c110b4SVitaly Bordug 	23, 22, 21, 20, 19, 18, 17, 16,
73b0c110b4SVitaly Bordug 	16, 17, 18, 19, 20, 21, 22, 23,
74b0c110b4SVitaly Bordug 	24, 25, 26, 27, 28, 29, 30, 31,
75b0c110b4SVitaly Bordug };
76b0c110b4SVitaly Bordug 
cpm2_mask_irq(struct irq_data * d)77c47eefa6SLennert Buytenhek static void cpm2_mask_irq(struct irq_data *d)
78b0c110b4SVitaly Bordug {
79b0c110b4SVitaly Bordug 	int	bit, word;
80476eb491SGrant Likely 	unsigned int irq_nr = irqd_to_hwirq(d);
81b0c110b4SVitaly Bordug 
82b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
83b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
84b0c110b4SVitaly Bordug 
85b0c110b4SVitaly Bordug 	ppc_cached_irq_mask[word] &= ~(1 << bit);
8673844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
87b0c110b4SVitaly Bordug }
88b0c110b4SVitaly Bordug 
cpm2_unmask_irq(struct irq_data * d)89c47eefa6SLennert Buytenhek static void cpm2_unmask_irq(struct irq_data *d)
90b0c110b4SVitaly Bordug {
91b0c110b4SVitaly Bordug 	int	bit, word;
92476eb491SGrant Likely 	unsigned int irq_nr = irqd_to_hwirq(d);
93b0c110b4SVitaly Bordug 
94b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
95b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
96b0c110b4SVitaly Bordug 
97b0c110b4SVitaly Bordug 	ppc_cached_irq_mask[word] |= 1 << bit;
9873844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
99b0c110b4SVitaly Bordug }
100b0c110b4SVitaly Bordug 
cpm2_ack(struct irq_data * d)101c47eefa6SLennert Buytenhek static void cpm2_ack(struct irq_data *d)
102b0c110b4SVitaly Bordug {
103b0c110b4SVitaly Bordug 	int	bit, word;
104476eb491SGrant Likely 	unsigned int irq_nr = irqd_to_hwirq(d);
105b0c110b4SVitaly Bordug 
106b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
107b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
108b0c110b4SVitaly Bordug 
10973844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_sipnrh + word, 1 << bit);
110b0c110b4SVitaly Bordug }
111b0c110b4SVitaly Bordug 
cpm2_end_irq(struct irq_data * d)112c47eefa6SLennert Buytenhek static void cpm2_end_irq(struct irq_data *d)
113b0c110b4SVitaly Bordug {
114b0c110b4SVitaly Bordug 	int	bit, word;
115476eb491SGrant Likely 	unsigned int irq_nr = irqd_to_hwirq(d);
116b0c110b4SVitaly Bordug 
117b0c110b4SVitaly Bordug 	bit = irq_to_siubit[irq_nr];
118b0c110b4SVitaly Bordug 	word = irq_to_siureg[irq_nr];
119b0c110b4SVitaly Bordug 
120b0c110b4SVitaly Bordug 	ppc_cached_irq_mask[word] |= 1 << bit;
12173844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
12273844ecbSVitaly Bordug 
123b0c110b4SVitaly Bordug 	/*
124b0c110b4SVitaly Bordug 	 * Work around large numbers of spurious IRQs on PowerPC 82xx
125b0c110b4SVitaly Bordug 	 * systems.
126b0c110b4SVitaly Bordug 	 */
127b0c110b4SVitaly Bordug 	mb();
128b0c110b4SVitaly Bordug }
129b0c110b4SVitaly Bordug 
cpm2_set_irq_type(struct irq_data * d,unsigned int flow_type)130c47eefa6SLennert Buytenhek static int cpm2_set_irq_type(struct irq_data *d, unsigned int flow_type)
13173844ecbSVitaly Bordug {
132476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
13373844ecbSVitaly Bordug 	unsigned int vold, vnew, edibit;
13473844ecbSVitaly Bordug 
135b22b97c1SMark Ware 	/* Port C interrupts are either IRQ_TYPE_EDGE_FALLING or
136b22b97c1SMark Ware 	 * IRQ_TYPE_EDGE_BOTH (default).  All others are IRQ_TYPE_EDGE_FALLING
137b22b97c1SMark Ware 	 * or IRQ_TYPE_LEVEL_LOW (default)
138b22b97c1SMark Ware 	 */
139b22b97c1SMark Ware 	if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) {
140b22b97c1SMark Ware 		if (flow_type == IRQ_TYPE_NONE)
141b22b97c1SMark Ware 			flow_type = IRQ_TYPE_EDGE_BOTH;
142b22b97c1SMark Ware 
143b22b97c1SMark Ware 		if (flow_type != IRQ_TYPE_EDGE_BOTH &&
144b22b97c1SMark Ware 		    flow_type != IRQ_TYPE_EDGE_FALLING)
145b22b97c1SMark Ware 			goto err_sense;
146b22b97c1SMark Ware 	} else {
14773844ecbSVitaly Bordug 		if (flow_type == IRQ_TYPE_NONE)
14873844ecbSVitaly Bordug 			flow_type = IRQ_TYPE_LEVEL_LOW;
14973844ecbSVitaly Bordug 
150b22b97c1SMark Ware 		if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
151b22b97c1SMark Ware 			goto err_sense;
15273844ecbSVitaly Bordug 	}
15373844ecbSVitaly Bordug 
154a28ab38cSThomas Gleixner 	irqd_set_trigger_type(d, flow_type);
155a28ab38cSThomas Gleixner 	if (flow_type & IRQ_TYPE_LEVEL_LOW)
156e9e879a3SThomas Gleixner 		irq_set_handler_locked(d, handle_level_irq);
157a28ab38cSThomas Gleixner 	else
158e9e879a3SThomas Gleixner 		irq_set_handler_locked(d, handle_edge_irq);
15973844ecbSVitaly Bordug 
16073844ecbSVitaly Bordug 	/* internal IRQ senses are LEVEL_LOW
16173844ecbSVitaly Bordug 	 * EXT IRQ and Port C IRQ senses are programmable
16273844ecbSVitaly Bordug 	 */
16373844ecbSVitaly Bordug 	if (src >= CPM2_IRQ_EXT1 && src <= CPM2_IRQ_EXT7)
16473844ecbSVitaly Bordug 			edibit = (14 - (src - CPM2_IRQ_EXT1));
16573844ecbSVitaly Bordug 	else
16673844ecbSVitaly Bordug 		if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0)
1677f3ea17fSpaulfax 			edibit = (31 - (CPM2_IRQ_PORTC0 - src));
16873844ecbSVitaly Bordug 		else
169a28ab38cSThomas Gleixner 			return (flow_type & IRQ_TYPE_LEVEL_LOW) ?
170a28ab38cSThomas Gleixner 				IRQ_SET_MASK_OK_NOCOPY : -EINVAL;
17173844ecbSVitaly Bordug 
17273844ecbSVitaly Bordug 	vold = in_be32(&cpm2_intctl->ic_siexr);
17373844ecbSVitaly Bordug 
17473844ecbSVitaly Bordug 	if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING)
17573844ecbSVitaly Bordug 		vnew = vold | (1 << edibit);
17673844ecbSVitaly Bordug 	else
17773844ecbSVitaly Bordug 		vnew = vold & ~(1 << edibit);
17873844ecbSVitaly Bordug 
17973844ecbSVitaly Bordug 	if (vold != vnew)
18073844ecbSVitaly Bordug 		out_be32(&cpm2_intctl->ic_siexr, vnew);
181a28ab38cSThomas Gleixner 	return IRQ_SET_MASK_OK_NOCOPY;
182b22b97c1SMark Ware 
183b22b97c1SMark Ware err_sense:
184b22b97c1SMark Ware 	pr_err("CPM2 PIC: sense type 0x%x not supported\n", flow_type);
185b22b97c1SMark Ware 	return -EINVAL;
18673844ecbSVitaly Bordug }
18773844ecbSVitaly Bordug 
188b0c110b4SVitaly Bordug static struct irq_chip cpm2_pic = {
189b27df672SThomas Gleixner 	.name = "CPM2 SIU",
190c47eefa6SLennert Buytenhek 	.irq_mask = cpm2_mask_irq,
191c47eefa6SLennert Buytenhek 	.irq_unmask = cpm2_unmask_irq,
192c47eefa6SLennert Buytenhek 	.irq_ack = cpm2_ack,
193c47eefa6SLennert Buytenhek 	.irq_eoi = cpm2_end_irq,
194c47eefa6SLennert Buytenhek 	.irq_set_type = cpm2_set_irq_type,
1957bf811a8SThomas Gleixner 	.flags = IRQCHIP_EOI_IF_HANDLED,
196b0c110b4SVitaly Bordug };
197b0c110b4SVitaly Bordug 
cpm2_get_irq(void)19835a84c2fSOlaf Hering unsigned int cpm2_get_irq(void)
199b0c110b4SVitaly Bordug {
200b0c110b4SVitaly Bordug 	int irq;
201b0c110b4SVitaly Bordug 	unsigned long bits;
202b0c110b4SVitaly Bordug 
203b0c110b4SVitaly Bordug        /* For CPM2, read the SIVEC register and shift the bits down
204b0c110b4SVitaly Bordug          * to get the irq number.         */
20573844ecbSVitaly Bordug         bits = in_be32(&cpm2_intctl->ic_sivec);
206b0c110b4SVitaly Bordug         irq = bits >> 26;
207b0c110b4SVitaly Bordug 
208b0c110b4SVitaly Bordug 	if (irq == 0)
209b0c110b4SVitaly Bordug 		return(-1);
21073844ecbSVitaly Bordug 	return irq_linear_revmap(cpm2_pic_host, irq);
211b0c110b4SVitaly Bordug }
212b0c110b4SVitaly Bordug 
cpm2_pic_host_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hw)213bae1d8f1SGrant Likely static int cpm2_pic_host_map(struct irq_domain *h, unsigned int virq,
214b0c110b4SVitaly Bordug 			  irq_hw_number_t hw)
215b0c110b4SVitaly Bordug {
216b0c110b4SVitaly Bordug 	pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw);
217b0c110b4SVitaly Bordug 
21898488db9SThomas Gleixner 	irq_set_status_flags(virq, IRQ_LEVEL);
219ec775d0eSThomas Gleixner 	irq_set_chip_and_handler(virq, &cpm2_pic, handle_level_irq);
220b0c110b4SVitaly Bordug 	return 0;
221b0c110b4SVitaly Bordug }
222b0c110b4SVitaly Bordug 
2239f70b8ebSGrant Likely static const struct irq_domain_ops cpm2_pic_host_ops = {
224b0c110b4SVitaly Bordug 	.map = cpm2_pic_host_map,
225ff8c3ab8SGrant Likely 	.xlate = irq_domain_xlate_onetwocell,
226b0c110b4SVitaly Bordug };
227b0c110b4SVitaly Bordug 
cpm2_pic_init(struct device_node * node)228b0c110b4SVitaly Bordug void cpm2_pic_init(struct device_node *node)
229b0c110b4SVitaly Bordug {
230b0c110b4SVitaly Bordug 	int i;
231b0c110b4SVitaly Bordug 
232*7768716dSChristophe Leroy 	cpm2_intctl = &cpm2_immr->im_intctl;
23373844ecbSVitaly Bordug 
234b0c110b4SVitaly Bordug 	/* Clear the CPM IRQ controller, in case it has any bits set
235b0c110b4SVitaly Bordug 	 * from the bootloader
236b0c110b4SVitaly Bordug 	 */
237b0c110b4SVitaly Bordug 
238b0c110b4SVitaly Bordug 	/* Mask out everything */
239b0c110b4SVitaly Bordug 
24073844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrh, 0x00000000);
24173844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_simrl, 0x00000000);
242b0c110b4SVitaly Bordug 
243b0c110b4SVitaly Bordug 	wmb();
244b0c110b4SVitaly Bordug 
245b0c110b4SVitaly Bordug 	/* Ack everything */
24673844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_sipnrh, 0xffffffff);
24773844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_sipnrl, 0xffffffff);
248b0c110b4SVitaly Bordug 	wmb();
249b0c110b4SVitaly Bordug 
250b0c110b4SVitaly Bordug 	/* Dummy read of the vector */
25173844ecbSVitaly Bordug 	i = in_be32(&cpm2_intctl->ic_sivec);
252b0c110b4SVitaly Bordug 	rmb();
253b0c110b4SVitaly Bordug 
254b0c110b4SVitaly Bordug 	/* Initialize the default interrupt mapping priorities,
255b0c110b4SVitaly Bordug 	 * in case the boot rom changed something on us.
256b0c110b4SVitaly Bordug 	 */
25773844ecbSVitaly Bordug 	out_be16(&cpm2_intctl->ic_sicr, 0);
25873844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_scprrh, 0x05309770);
25973844ecbSVitaly Bordug 	out_be32(&cpm2_intctl->ic_scprrl, 0x05309770);
260b0c110b4SVitaly Bordug 
261b0c110b4SVitaly Bordug 	/* create a legacy host */
262a8db8cf0SGrant Likely 	cpm2_pic_host = irq_domain_add_linear(node, 64, &cpm2_pic_host_ops, NULL);
263b0c110b4SVitaly Bordug 	if (cpm2_pic_host == NULL) {
264b0c110b4SVitaly Bordug 		printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
265b0c110b4SVitaly Bordug 		return;
266b0c110b4SVitaly Bordug 	}
267b0c110b4SVitaly Bordug }
268