irq.c 5.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
/* 
 * 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/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 = {
88 89 90 91 92 93 94
	.typename = "Overdrive-IRQ",
	.startup = startup_od_irq,
	.shutdown = shutdown_od_irq,
	.enable = enable_od_irq,
	.disable = disable_od_irq,
	.ack = mask_and_ack_od,
	.end = end_od_irq
L
Linus Torvalds 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
};

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);
152
	irq_desc[irq].chip = &od_irq_type;
L
Linus Torvalds 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	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);
}