summaryrefslogtreecommitdiff
path: root/arch/mips/momentum/ocelot_c/uart-irq.c
blob: 9f33d8f1d8268fe859fecaae7e95a390b87d7fec (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
/*
 * Copyright 2002 Momentum Computer
 * Author: mdharm@momenco.com
 *
 * arch/mips/momentum/ocelot_c/uart-irq.c
 *     Interrupt routines for UARTs.  Interrupt numbers are assigned from
 *     80 to 81 (2 interrupt sources).
 *
 * 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 <asm/irq.h>
#include "ocelot_c_fpga.h"

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_uart_irq(unsigned int irq)
{
	uint8_t value;

	value = OCELOT_FPGA_READ(UART_INTMASK);
	value |= 1 << (irq - 74);
	OCELOT_FPGA_WRITE(value, UART_INTMASK);

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

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

	value = OCELOT_FPGA_READ(UART_INTMASK);
	value &= ~(1 << (irq - 74));
	OCELOT_FPGA_WRITE(value, UART_INTMASK);

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

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

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

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

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

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

/*
 * Interrupt handler for interrupts coming from the FPGA chip.
 */
void ll_uart_irq(struct pt_regs *regs)
{
	unsigned int irq_src, irq_mask;

	/* read the interrupt status registers */
	irq_src = OCELOT_FPGA_READ(UART_INTSTAT);
	irq_mask = OCELOT_FPGA_READ(UART_INTMASK);

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

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

#define shutdown_uart_irq	disable_uart_irq

struct irq_chip uart_irq_type = {
	.typename = "UART/FPGA",
	.startup = startup_uart_irq,
	.shutdown = shutdown_uart_irq,
	.enable = enable_uart_irq,
	.disable = disable_uart_irq,
	.ack = mask_and_ack_uart_irq,
	.end = end_uart_irq,
};

void uart_irq_init(void)
{
	/* Reset irq handlers pointers to NULL */
	irq_desc[80].status = IRQ_DISABLED;
	irq_desc[80].action = 0;
	irq_desc[80].depth = 2;
	irq_desc[80].chip = &uart_irq_type;

	irq_desc[81].status = IRQ_DISABLED;
	irq_desc[81].action = 0;
	irq_desc[81].depth = 2;
	irq_desc[81].chip = &uart_irq_type;
}