123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- /*
- * Copyright (C) 2000 David J. Mckay (david.mckay@st.com)
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * Looks after interrupts on the overdrive board.
- *
- * Bases on the IPR irq system
- */
- #include <linux/config.h>
- #include <linux/init.h>
- #include <linux/irq.h>
- #include <asm/system.h>
- #include <asm/io.h>
- #include <asm/overdrive/overdrive.h>
- struct od_data {
- int overdrive_irq;
- int irq_mask;
- };
- #define NUM_EXTERNAL_IRQS 16
- #define EXTERNAL_IRQ_NOT_IN_USE (-1)
- #define EXTERNAL_IRQ_NOT_ASSIGNED (-1)
- /*
- * This table is used to determine what to program into the FPGA's CT register
- * for the specified Linux IRQ.
- *
- * The irq_mask gives the interrupt number from the PCI board (PCI_Int(6:0))
- * but is one greater than that because the because the FPGA treats 0
- * as disabled, a value of 1 asserts PCI_Int0, and so on.
- *
- * The overdrive_irq specifies which of the eight interrupt sources generates
- * that interrupt, and but is multiplied by four to give the bit offset into
- * the CT register.
- *
- * The seven interrupts levels (SH4 IRL's) we have available here is hardwired
- * by the EPLD. The assignments here of which PCI interrupt generates each
- * level is arbitary.
- */
- static struct od_data od_data_table[NUM_EXTERNAL_IRQS] = {
- /* overdrive_irq , irq_mask */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 0 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, 7}, /* 1 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, 6}, /* 2 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 3 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, 5}, /* 4 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 5 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 6 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, 4}, /* 7 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 8 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 9 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, 3}, /* 10 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, 2}, /* 11 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 12 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, 1}, /* 13 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE}, /* 14 */
- {EXTERNAL_IRQ_NOT_ASSIGNED, EXTERNAL_IRQ_NOT_IN_USE} /* 15 */
- };
- static void set_od_data(int overdrive_irq, int irq)
- {
- if (irq >= NUM_EXTERNAL_IRQS || irq < 0)
- return;
- od_data_table[irq].overdrive_irq = overdrive_irq << 2;
- }
- static void enable_od_irq(unsigned int irq);
- void disable_od_irq(unsigned int irq);
- /* shutdown is same as "disable" */
- #define shutdown_od_irq disable_od_irq
- static void mask_and_ack_od(unsigned int);
- static void end_od_irq(unsigned int irq);
- static unsigned int startup_od_irq(unsigned int irq)
- {
- enable_od_irq(irq);
- return 0; /* never anything pending */
- }
- static struct hw_interrupt_type od_irq_type = {
- "Overdrive-IRQ",
- startup_od_irq,
- shutdown_od_irq,
- enable_od_irq,
- disable_od_irq,
- mask_and_ack_od,
- end_od_irq
- };
- static void disable_od_irq(unsigned int irq)
- {
- unsigned val, flags;
- int overdrive_irq;
- unsigned mask;
- /* Not a valid interrupt */
- if (irq < 0 || irq >= NUM_EXTERNAL_IRQS)
- return;
- /* Is is necessary to use a cli here? Would a spinlock not be
- * mroe efficient?
- */
- local_irq_save(flags);
- overdrive_irq = od_data_table[irq].overdrive_irq;
- if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) {
- mask = ~(0x7 << overdrive_irq);
- val = ctrl_inl(OVERDRIVE_INT_CT);
- val &= mask;
- ctrl_outl(val, OVERDRIVE_INT_CT);
- }
- local_irq_restore(flags);
- }
- static void enable_od_irq(unsigned int irq)
- {
- unsigned val, flags;
- int overdrive_irq;
- unsigned mask;
- /* Not a valid interrupt */
- if (irq < 0 || irq >= NUM_EXTERNAL_IRQS)
- return;
- /* Set priority in OD back to original value */
- local_irq_save(flags);
- /* This one is not in use currently */
- overdrive_irq = od_data_table[irq].overdrive_irq;
- if (overdrive_irq != EXTERNAL_IRQ_NOT_ASSIGNED) {
- val = ctrl_inl(OVERDRIVE_INT_CT);
- mask = ~(0x7 << overdrive_irq);
- val &= mask;
- mask = od_data_table[irq].irq_mask << overdrive_irq;
- val |= mask;
- ctrl_outl(val, OVERDRIVE_INT_CT);
- }
- local_irq_restore(flags);
- }
- /* this functions sets the desired irq handler to be an overdrive type */
- static void __init make_od_irq(unsigned int irq)
- {
- disable_irq_nosync(irq);
- irq_desc[irq].handler = &od_irq_type;
- disable_od_irq(irq);
- }
- static void mask_and_ack_od(unsigned int irq)
- {
- disable_od_irq(irq);
- }
- static void end_od_irq(unsigned int irq)
- {
- enable_od_irq(irq);
- }
- void __init init_overdrive_irq(void)
- {
- int i;
- /* Disable all interrupts */
- ctrl_outl(0, OVERDRIVE_INT_CT);
- /* Update interrupt pin mode to use encoded interrupts */
- i = ctrl_inw(INTC_ICR);
- i &= ~INTC_ICR_IRLM;
- ctrl_outw(i, INTC_ICR);
- for (i = 0; i < NUM_EXTERNAL_IRQS; i++) {
- if (od_data_table[i].irq_mask != EXTERNAL_IRQ_NOT_IN_USE) {
- make_od_irq(i);
- } else if (i != 15) { // Cannot use imask on level 15
- make_imask_irq(i);
- }
- }
- /* Set up the interrupts */
- set_od_data(OVERDRIVE_PCI_INTA, OVERDRIVE_PCI_IRQ1);
- set_od_data(OVERDRIVE_PCI_INTB, OVERDRIVE_PCI_IRQ2);
- set_od_data(OVERDRIVE_AUDIO_INT, OVERDRIVE_ESS_IRQ);
- }
|