aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/cpu/irq/pint.c
blob: 67602685df1a2f22f2d7e7848e036f0d1ad1ff9c (plain) (blame)
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
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
 * arch/sh/kernel/cpu/irq/pint.c - Interrupt handling for PINT-based IRQs.
 *
 * Copyright (C) 1999  Niibe Yutaka & Takeshi Yaegashi
 * Copyright (C) 2000  Kazumoto Kojima
 * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
 *
 * 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/init.h>
#include <linux/irq.h>
#include <linux/module.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/machvec.h>

#if defined(CONFIG_CPU_SUBTYPE_SH7705)
#define INTC_INTER      0xA4000014UL
#define INTC_IPRD       0xA4000018UL
#define INTC_ICR2       0xA4000012UL

/* PFC */
#define PORT_PACR       0xA4000100UL
#define PORT_PBCR       0xA4000102UL
#define PORT_PCCR       0xA4000104UL
#define PORT_PDCR       0xA4000106UL
#define PORT_PECR       0xA4000108UL
#define PORT_PFCR       0xA400010AUL
#define PORT_PGCR       0xA400010CUL
#define PORT_PHCR       0xA400010EUL
#define PORT_PJCR       0xA4000110UL
#define PORT_PKCR       0xA4000112UL
#define PORT_PLCR       0xA4000114UL
#define PORT_PMCR       0xA4000118UL
#define PORT_PNCR       0xA400011AUL
#define PORT_PECR2      0xA4050148UL
#define PORT_PFCR2      0xA405014AUL
#define PORT_PNCR2      0xA405015AUL

/* I/O port */
#define PORT_PADR       0xA4000120UL
#define PORT_PBDR       0xA4000122UL
#define PORT_PCDR       0xA4000124UL
#define PORT_PDDR       0xA4000126UL
#define PORT_PEDR       0xA4000128UL
#define PORT_PFDR       0xA400012AUL
#define PORT_PGDR       0xA400012CUL
#define PORT_PHDR       0xA400012EUL
#define PORT_PJDR       0xA4000130UL
#define PORT_PKDR       0xA4000132UL
#define PORT_PLDR       0xA4000134UL
#define PORT_PMDR       0xA4000138UL
#define PORT_PNDR       0xA400013AUL

#define PINT0_IRQ       40
#define PINT8_IRQ       41
#define PINT_IRQ_BASE   86

#define PINT0_IPR_ADDR          INTC_IPRD
#define PINT0_IPR_POS           3
#define PINT0_PRIORITY      2

#define PINT8_IPR_ADDR          INTC_IPRD
#define PINT8_IPR_POS           2
#define PINT8_PRIORITY      2

#endif /* CONFIG_CPU_SUBTYPE_SH7705 */

static unsigned char pint_map[256];
static unsigned long portcr_mask;

static void enable_pint_irq(unsigned int irq);
static void disable_pint_irq(unsigned int irq);

/* shutdown is same as "disable" */
#define shutdown_pint_irq disable_pint_irq

static void mask_and_ack_pint(unsigned int);
static void end_pint_irq(unsigned int irq);

static unsigned int startup_pint_irq(unsigned int irq)
{
	enable_pint_irq(irq);
	return 0; /* never anything pending */
}

static struct hw_interrupt_type pint_irq_type = {
	.typename = "PINT-IRQ",
	.startup = startup_pint_irq,
	.shutdown = shutdown_pint_irq,
	.enable = enable_pint_irq,
	.disable = disable_pint_irq,
	.ack = mask_and_ack_pint,
	.end = end_pint_irq
};

static void disable_pint_irq(unsigned int irq)
{
	unsigned long val;

	val = ctrl_inw(INTC_INTER);
	val &= ~(1 << (irq - PINT_IRQ_BASE));
	ctrl_outw(val, INTC_INTER);	/* disable PINTn */
	portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2);
}

static void enable_pint_irq(unsigned int irq)
{
	unsigned long val;

	val = ctrl_inw(INTC_INTER);
	val |= 1 << (irq - PINT_IRQ_BASE);
	ctrl_outw(val, INTC_INTER);	/* enable PINTn */
	portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2;
}

static void mask_and_ack_pint(unsigned int irq)
{
	disable_pint_irq(irq);
}

static void end_pint_irq(unsigned int irq)
{
	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
		enable_pint_irq(irq);
}

void make_pint_irq(unsigned int irq)
{
	disable_irq_nosync(irq);
	irq_desc[irq].chip = &pint_irq_type;
	disable_pint_irq(irq);
}

static struct ipr_data pint_ipr_map[] = {
	{ PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY },
	{ PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY },
};

void __init init_IRQ_pint(void)
{
	int i;

	make_ipr_irq(pint_ipr_map, ARRAY_SIZE(pint_ipr_map));

	enable_irq(PINT0_IRQ);
	enable_irq(PINT8_IRQ);

	for(i = 0; i < 16; i++)
		make_pint_irq(PINT_IRQ_BASE + i);

	for(i = 0; i < 256; i++) {
		if (i & 1)
			pint_map[i] = 0;
		else if (i & 2)
			pint_map[i] = 1;
		else if (i & 4)
			pint_map[i] = 2;
		else if (i & 8)
			pint_map[i] = 3;
		else if (i & 0x10)
			pint_map[i] = 4;
		else if (i & 0x20)
			pint_map[i] = 5;
		else if (i & 0x40)
			pint_map[i] = 6;
		else if (i & 0x80)
			pint_map[i] = 7;
	}
}

int ipr_irq_demux(int irq)
{
	unsigned long creg, dreg, d, sav;

	if (irq == PINT0_IRQ) {
#if defined(CONFIG_CPU_SUBTYPE_SH7705) || defined(CONFIG_CPU_SUBTYPE_SH7707)
		creg = PORT_PACR;
		dreg = PORT_PADR;
#else
		creg = PORT_PCCR;
		dreg = PORT_PCDR;
#endif
		sav = ctrl_inw(creg);
		ctrl_outw(sav | portcr_mask, creg);
		d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) &
			ctrl_inw(INTC_INTER) & 0xff;
		ctrl_outw(sav, creg);

		if (d == 0)
			return irq;

		return PINT_IRQ_BASE + pint_map[d];
	} else if (irq == PINT8_IRQ) {
#if defined(CONFIG_CPU_SUBTYPE_SH7705) || defined(CONFIG_CPU_SUBTYPE_SH7707)
		creg = PORT_PBCR;
		dreg = PORT_PBDR;
#else
		creg = PORT_PFCR;
		dreg = PORT_PFDR;
#endif
		sav = ctrl_inw(creg);
		ctrl_outw(sav | (portcr_mask >> 16), creg);
		d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) &
			(ctrl_inw(INTC_INTER) >> 8) & 0xff;
		ctrl_outw(sav, creg);

		if (d == 0)
			return irq;

		return PINT_IRQ_BASE + 8 + pint_map[d];
	}

	return irq;
}