1#include <linux/module.h>
2#include <linux/stddef.h>
3#include <linux/init.h>
4#include <linux/sched.h>
5#include <linux/signal.h>
6#include <linux/interrupt.h>
7#include <asm/irq.h>
8#include <asm/io.h>
9#include <asm/8xx_immap.h>
10#include <asm/mpc8xx.h>
11#include "ppc8xx_pic.h"
12
13extern int cpm_get_irq(void);
14
15/* The 8xx internal interrupt controller.  It is usually
16 * the only interrupt controller.  Some boards, like the MBX and
17 * Sandpoint have the 8259 as a secondary controller.  Depending
18 * upon the processor type, the internal controller can have as
19 * few as 16 interrups or as many as 64.  We could use  the
20 * "clear_bit()" and "set_bit()" functions like other platforms,
21 * but they are overkill for us.
22 */
23
24static void m8xx_mask_irq(unsigned int irq_nr)
25{
26	int	bit, word;
27
28	bit = irq_nr & 0x1f;
29	word = irq_nr >> 5;
30
31	ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
32	out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
33}
34
35static void m8xx_unmask_irq(unsigned int irq_nr)
36{
37	int	bit, word;
38
39	bit = irq_nr & 0x1f;
40	word = irq_nr >> 5;
41
42	ppc_cached_irq_mask[word] |= (1 << (31-bit));
43	out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
44}
45
46static void m8xx_end_irq(unsigned int irq_nr)
47{
48	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
49			&& irq_desc[irq_nr].action) {
50		int bit, word;
51
52		bit = irq_nr & 0x1f;
53		word = irq_nr >> 5;
54
55		ppc_cached_irq_mask[word] |= (1 << (31-bit));
56		out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
57	}
58}
59
60
61static void m8xx_mask_and_ack(unsigned int irq_nr)
62{
63	int	bit, word;
64
65	bit = irq_nr & 0x1f;
66	word = irq_nr >> 5;
67
68	ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
69	out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
70	out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend, 1 << (31-bit));
71}
72
73struct hw_interrupt_type ppc8xx_pic = {
74	.typename = " 8xx SIU  ",
75	.enable = m8xx_unmask_irq,
76	.disable = m8xx_mask_irq,
77	.ack = m8xx_mask_and_ack,
78	.end = m8xx_end_irq,
79};
80
81/*
82 * We either return a valid interrupt or -1 if there is nothing pending
83 */
84int
85m8xx_get_irq(struct pt_regs *regs)
86{
87	int irq;
88
89	/* For MPC8xx, read the SIVEC register and shift the bits down
90	 * to get the irq number.
91	 */
92	irq = in_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec) >> 26;
93
94	/*
95	 * When we read the sivec without an interrupt to process, we will
96	 * get back SIU_LEVEL7.  In this case, return -1
97	 */
98        if (irq == CPM_INTERRUPT)
99        	irq = CPM_IRQ_OFFSET + cpm_get_irq();
100#if defined(CONFIG_PCI)
101	else if (irq == ISA_BRIDGE_INT) {
102		int isa_irq;
103
104		if ((isa_irq = i8259_poll(regs)) >= 0)
105			irq = I8259_IRQ_OFFSET + isa_irq;
106	}
107#endif	/* CONFIG_PCI */
108	else if (irq == SIU_LEVEL7)
109		irq = -1;
110
111	return irq;
112}
113
114#if defined(CONFIG_MBX) && defined(CONFIG_PCI)
115/* Only the MBX uses the external 8259.  This allows us to catch standard
116 * drivers that may mess up the internal interrupt controllers, and also
117 * allow them to run without modification on the MBX.
118 */
119void mbx_i8259_action(int irq, void *dev_id, struct pt_regs *regs)
120{
121	/* This interrupt handler never actually gets called.  It is
122	 * installed only to unmask the 8259 cascade interrupt in the SIU
123	 * and to make the 8259 cascade interrupt visible in /proc/interrupts.
124	 */
125}
126#endif	/* CONFIG_PCI */
127