aboutsummaryrefslogtreecommitdiff
path: root/arch/mips/momentum/ocelot_c/cpci-irq.c
blob: a5dc230520dffc9bdb06924c8c1dff8bc5cf7d9a (plain)
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
88
89
90
91
92
93
94
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
152
/*
 * Copyright 2002 Momentum Computer
 * Author: mdharm@momenco.com
 *
 * arch/mips/momentum/ocelot_c/cpci-irq.c
 *     Interrupt routines for cpci.  Interrupt numbers are assigned from
 *     CPCI_IRQ_BASE to CPCI_IRQ_BASE+8 (8 interrupt sources).
 *
 * Note that the high-level software will need to be careful about using
 * these interrupts.  If this board is asserting a cPCI interrupt, it will
 * also see the asserted interrupt.  Care must be taken to avoid an
 * interrupt flood.
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <asm/ptrace.h>
#include <linux/sched.h>
#include <linux/kernel_stat.h>
#include <asm/io.h>
#include "ocelot_c_fpga.h"

#define CPCI_IRQ_BASE	8

static inline int ls1bit8(unsigned int x)
{
        int b = 7, s;

        s =  4; if (((unsigned char)(x <<  4)) == 0) s = 0; b -= s; x <<= s;
        s =  2; if (((unsigned char)(x <<  2)) == 0) s = 0; b -= s; x <<= s;
        s =  1; if (((unsigned char)(x <<  1)) == 0) s = 0; b -= s;

        return b;
}

/* mask off an interrupt -- 0 is enable, 1 is disable */
static inline void mask_cpci_irq(unsigned int irq)
{
	uint32_t value;

	value = OCELOT_FPGA_READ(INTMASK);
	value |= 1 << (irq - CPCI_IRQ_BASE);
	OCELOT_FPGA_WRITE(value, INTMASK);

	/* read the value back to assure that it's really been written */
	value = OCELOT_FPGA_READ(INTMASK);
}

/* unmask an interrupt -- 0 is enable, 1 is disable */
static inline void unmask_cpci_irq(unsigned int irq)
{
	uint32_t value;

	value = OCELOT_FPGA_READ(INTMASK);
	value &= ~(1 << (irq - CPCI_IRQ_BASE));
	OCELOT_FPGA_WRITE(value, INTMASK);

	/* read the value back to assure that it's really been written */
	value = OCELOT_FPGA_READ(INTMASK);
}

/*
 * Enables the IRQ in the FPGA
 */
static void enable_cpci_irq(unsigned int irq)
{
	unmask_cpci_irq(irq);
}

/*
 * Initialize the IRQ in the FPGA
 */
static unsigned int startup_cpci_irq(unsigned int irq)
{
	unmask_cpci_irq(irq);
	return 0;
}

/*
 * Disables the IRQ in the FPGA
 */
static void disable_cpci_irq(unsigned int irq)
{
	mask_cpci_irq(irq);
}

/*
 * Masks and ACKs an IRQ
 */
static void mask_and_ack_cpci_irq(unsigned int irq)
{
	mask_cpci_irq(irq);
}

/*
 * End IRQ processing
 */
static void end_cpci_irq(unsigned int irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
		unmask_cpci_irq(irq);
}

/*
 * Interrupt handler for interrupts coming from the FPGA chip.
 * It could be built in ethernet ports etc...
 */
void ll_cpci_irq(struct pt_regs *regs)
{
	unsigned int irq_src, irq_mask;

	/* read the interrupt status registers */
	irq_src = OCELOT_FPGA_READ(INTSTAT);
	irq_mask = OCELOT_FPGA_READ(INTMASK);

	/* mask for just the interrupts we want */
	irq_src &= ~irq_mask;

	do_IRQ(ls1bit8(irq_src) + CPCI_IRQ_BASE, regs);
}

#define shutdown_cpci_irq	disable_cpci_irq

struct irq_chip cpci_irq_type = {
	.typename = "CPCI/FPGA",
	.startup = startup_cpci_irq,
	.shutdown = shutdown_cpci_irq,
	.enable = enable_cpci_irq,
	.disable = disable_cpci_irq,
	.ack = mask_and_ack_cpci_irq,
	.end = end_cpci_irq,
};

void cpci_irq_init(void)
{
	int i;

	/* Reset irq handlers pointers to NULL */
	for (i = CPCI_IRQ_BASE; i < (CPCI_IRQ_BASE + 8); i++) {
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = 0;
		irq_desc[i].depth = 2;
		irq_desc[i].chip = &cpci_irq_type;
	}
}