2005-04-16 18:20:36 -04:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/stddef.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/signal.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <asm/irq.h>
|
2005-10-28 20:46:10 -04:00
|
|
|
#include <asm/io.h>
|
2005-04-16 18:20:36 -04:00
|
|
|
#include <asm/8xx_immap.h>
|
|
|
|
#include <asm/mpc8xx.h>
|
|
|
|
#include "ppc8xx_pic.h"
|
|
|
|
|
2006-10-09 07:48:42 -04:00
|
|
|
extern int cpm_get_irq(void);
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
/* The 8xx internal interrupt controller. It is usually
|
|
|
|
* the only interrupt controller. Some boards, like the MBX and
|
|
|
|
* Sandpoint have the 8259 as a secondary controller. Depending
|
|
|
|
* upon the processor type, the internal controller can have as
|
|
|
|
* few as 16 interrups or as many as 64. We could use the
|
|
|
|
* "clear_bit()" and "set_bit()" functions like other platforms,
|
|
|
|
* but they are overkill for us.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void m8xx_mask_irq(unsigned int irq_nr)
|
|
|
|
{
|
|
|
|
int bit, word;
|
|
|
|
|
|
|
|
bit = irq_nr & 0x1f;
|
|
|
|
word = irq_nr >> 5;
|
|
|
|
|
|
|
|
ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
|
2005-10-28 20:46:10 -04:00
|
|
|
out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void m8xx_unmask_irq(unsigned int irq_nr)
|
|
|
|
{
|
|
|
|
int bit, word;
|
|
|
|
|
|
|
|
bit = irq_nr & 0x1f;
|
|
|
|
word = irq_nr >> 5;
|
|
|
|
|
|
|
|
ppc_cached_irq_mask[word] |= (1 << (31-bit));
|
2005-10-28 20:46:10 -04:00
|
|
|
out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void m8xx_end_irq(unsigned int irq_nr)
|
|
|
|
{
|
|
|
|
if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
|
|
|
|
&& irq_desc[irq_nr].action) {
|
|
|
|
int bit, word;
|
|
|
|
|
|
|
|
bit = irq_nr & 0x1f;
|
|
|
|
word = irq_nr >> 5;
|
|
|
|
|
|
|
|
ppc_cached_irq_mask[word] |= (1 << (31-bit));
|
2005-10-28 20:46:10 -04:00
|
|
|
out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void m8xx_mask_and_ack(unsigned int irq_nr)
|
|
|
|
{
|
|
|
|
int bit, word;
|
|
|
|
|
|
|
|
bit = irq_nr & 0x1f;
|
|
|
|
word = irq_nr >> 5;
|
|
|
|
|
|
|
|
ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
|
2005-10-28 20:46:10 -04:00
|
|
|
out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask, ppc_cached_irq_mask[word]);
|
|
|
|
out_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend, 1 << (31-bit));
|
2005-04-16 18:20:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
struct hw_interrupt_type ppc8xx_pic = {
|
|
|
|
.typename = " 8xx SIU ",
|
|
|
|
.enable = m8xx_unmask_irq,
|
|
|
|
.disable = m8xx_mask_irq,
|
|
|
|
.ack = m8xx_mask_and_ack,
|
|
|
|
.end = m8xx_end_irq,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We either return a valid interrupt or -1 if there is nothing pending
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
m8xx_get_irq(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
int irq;
|
|
|
|
|
|
|
|
/* For MPC8xx, read the SIVEC register and shift the bits down
|
|
|
|
* to get the irq number.
|
|
|
|
*/
|
2005-10-28 20:46:10 -04:00
|
|
|
irq = in_be32(&((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec) >> 26;
|
2005-04-16 18:20:36 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* When we read the sivec without an interrupt to process, we will
|
|
|
|
* get back SIU_LEVEL7. In this case, return -1
|
|
|
|
*/
|
|
|
|
if (irq == CPM_INTERRUPT)
|
2006-10-09 07:48:42 -04:00
|
|
|
irq = CPM_IRQ_OFFSET + cpm_get_irq();
|
2005-04-16 18:20:36 -04:00
|
|
|
#if defined(CONFIG_PCI)
|
|
|
|
else if (irq == ISA_BRIDGE_INT) {
|
|
|
|
int isa_irq;
|
|
|
|
|
|
|
|
if ((isa_irq = i8259_poll(regs)) >= 0)
|
|
|
|
irq = I8259_IRQ_OFFSET + isa_irq;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PCI */
|
|
|
|
else if (irq == SIU_LEVEL7)
|
|
|
|
irq = -1;
|
|
|
|
|
|
|
|
return irq;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_MBX) && defined(CONFIG_PCI)
|
|
|
|
/* Only the MBX uses the external 8259. This allows us to catch standard
|
|
|
|
* drivers that may mess up the internal interrupt controllers, and also
|
|
|
|
* allow them to run without modification on the MBX.
|
|
|
|
*/
|
|
|
|
void mbx_i8259_action(int irq, void *dev_id, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
/* This interrupt handler never actually gets called. It is
|
|
|
|
* installed only to unmask the 8259 cascade interrupt in the SIU
|
|
|
|
* and to make the 8259 cascade interrupt visible in /proc/interrupts.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PCI */
|