summaryrefslogtreecommitdiffstats
path: root/arch/m68knommu/platform/coldfire/intc-2.c
blob: 66d4e47dd8d0f7d41289f12e32c62cf41b5839e8 (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
/*
 * intc-2.c
 *
 * General interrupt controller code for the many ColdFire cores that use
 * interrupt controllers with 63 interrupt sources, organized as 56 fully-
 * programmable + 7 fixed-level interrupt sources. This includes the 523x
 * family, the 5270, 5271, 5274, 5275, and the 528x family which have two such
 * controllers, and the 547x and 548x families which have only one of them.
 *
 * (C) Copyright 2009, Greg Ungerer <gerg@snapgear.com>
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive
 * for more details.
 */

#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include <asm/traps.h>

/*
 * Bit definitions for the ICR family of registers.
 */
#define MCFSIM_ICR_LEVEL(l)	((l)<<3)	/* Level l intr */
#define MCFSIM_ICR_PRI(p)	(p)		/* Priority p intr */

#ifdef MCFICM_INTC1
#define NR_VECS	128
#else
#define NR_VECS	64
#endif

static void intc_irq_mask(struct irq_data *d)
{
	unsigned int irq = d->irq - MCFINT_VECBASE;
	unsigned long imraddr;
	u32 val, imrbit;

#ifdef MCFICM_INTC1
	imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0;
#else
	imraddr = MCFICM_INTC0;
#endif
	imraddr += (irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL;
	imrbit = 0x1 << (irq & 0x1f);

	val = __raw_readl(imraddr);
	__raw_writel(val | imrbit, imraddr);
}

static void intc_irq_unmask(struct irq_data *d)
{
	unsigned int irq = d->irq - MCFINT_VECBASE;
	unsigned long imraddr;
	u32 val, imrbit;

#ifdef MCFICM_INTC1
	imraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0;
#else
	imraddr = MCFICM_INTC0;
#endif
	imraddr += ((irq & 0x20) ? MCFINTC_IMRH : MCFINTC_IMRL);
	imrbit = 0x1 << (irq & 0x1f);

	/* Don't set the "maskall" bit! */
	if ((irq & 0x20) == 0)
		imrbit |= 0x1;

	val = __raw_readl(imraddr);
	__raw_writel(val & ~imrbit, imraddr);
}

static int intc_irq_set_type(struct irq_data *d, unsigned int type)
{
	return 0;
}

/*
 *	Each vector needs a unique priority and level associated with it.
 *	We don't really care so much what they are, we don't rely on the
 *	traditional priority interrupt scheme of the m68k/ColdFire. This
 *	only needs to be set once for an interrupt, and we will never change
 *	these values once we have set them.
 */
static u8 intc_intpri = MCFSIM_ICR_LEVEL(6) | MCFSIM_ICR_PRI(6);

static unsigned int intc_irq_startup(struct irq_data *d)
{
	unsigned int irq = d->irq - MCFINT_VECBASE;
	unsigned long icraddr;

#ifdef MCFICM_INTC1
	icraddr = (irq & 0x40) ? MCFICM_INTC1 : MCFICM_INTC0;
#else
	icraddr = MCFICM_INTC0;
#endif
	icraddr += MCFINTC_ICR0 + (irq & 0x3f);
	if (__raw_readb(icraddr) == 0)
		__raw_writeb(intc_intpri--, icraddr);

	intc_irq_unmask(d);
	return 0;
}

static struct irq_chip intc_irq_chip = {
	.name		= "CF-INTC",
	.irq_startup	= intc_irq_startup,
	.irq_mask	= intc_irq_mask,
	.irq_unmask	= intc_irq_unmask,
	.irq_set_type	= intc_irq_set_type,
};

void __init init_IRQ(void)
{
	int irq;

	init_vectors();

	/* Mask all interrupt sources */
	__raw_writel(0x1, MCFICM_INTC0 + MCFINTC_IMRL);
#ifdef MCFICM_INTC1
	__raw_writel(0x1, MCFICM_INTC1 + MCFINTC_IMRL);
#endif

	for (irq = MCFINT_VECBASE; (irq < MCFINT_VECBASE + NR_VECS); irq++) {
		set_irq_chip(irq, &intc_irq_chip);
		set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
		set_irq_handler(irq, handle_level_irq);
	}
}