1/*
2 *  linux/arch/m68k/amiga/cia.c - CIA support
3 *
4 *  Copyright (C) 1996 Roman Zippel
5 *
6 *  The concept of some functions bases on the original Amiga OS function
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License.  See the file COPYING in the main directory of this archive
10 * for more details.
11 */
12
13#include <linux/types.h>
14#include <linux/kernel.h>
15#include <linux/sched.h>
16#include <linux/errno.h>
17#include <linux/kernel_stat.h>
18#include <linux/init.h>
19#include <linux/seq_file.h>
20#include <linux/interrupt.h>
21
22#include <asm/irq.h>
23#include <asm/amigahw.h>
24#include <asm/amigaints.h>
25
26struct ciabase {
27	volatile struct CIA *cia;
28	unsigned char icr_mask, icr_data;
29	unsigned short int_mask;
30	int handler_irq, cia_irq, server_irq;
31	char *name;
32} ciaa_base = {
33	.cia		= &ciaa,
34	.int_mask	= IF_PORTS,
35	.handler_irq	= IRQ_AMIGA_PORTS,
36	.cia_irq	= IRQ_AMIGA_CIAA,
37	.name		= "CIAA"
38}, ciab_base = {
39	.cia		= &ciab,
40	.int_mask	= IF_EXTER,
41	.handler_irq	= IRQ_AMIGA_EXTER,
42	.cia_irq	= IRQ_AMIGA_CIAB,
43	.name		= "CIAB"
44};
45
46/*
47 *  Cause or clear CIA interrupts, return old interrupt status.
48 */
49
50unsigned char cia_set_irq(struct ciabase *base, unsigned char mask)
51{
52	unsigned char old;
53
54	old = (base->icr_data |= base->cia->icr);
55	if (mask & CIA_ICR_SETCLR)
56		base->icr_data |= mask;
57	else
58		base->icr_data &= ~mask;
59	if (base->icr_data & base->icr_mask)
60		amiga_custom.intreq = IF_SETCLR | base->int_mask;
61	return old & base->icr_mask;
62}
63
64/*
65 *  Enable or disable CIA interrupts, return old interrupt mask,
66 */
67
68unsigned char cia_able_irq(struct ciabase *base, unsigned char mask)
69{
70	unsigned char old;
71
72	old = base->icr_mask;
73	base->icr_data |= base->cia->icr;
74	base->cia->icr = mask;
75	if (mask & CIA_ICR_SETCLR)
76		base->icr_mask |= mask;
77	else
78		base->icr_mask &= ~mask;
79	base->icr_mask &= CIA_ICR_ALL;
80	if (base->icr_data & base->icr_mask)
81		amiga_custom.intreq = IF_SETCLR | base->int_mask;
82	return old;
83}
84
85static irqreturn_t cia_handler(int irq, void *dev_id)
86{
87	struct ciabase *base = (struct ciabase *)dev_id;
88	int mach_irq;
89	unsigned char ints;
90
91	mach_irq = base->cia_irq;
92	ints = cia_set_irq(base, CIA_ICR_ALL);
93	amiga_custom.intreq = base->int_mask;
94	for (; ints; mach_irq++, ints >>= 1) {
95		if (ints & 1)
96			m68k_handle_int(mach_irq);
97	}
98	return IRQ_HANDLED;
99}
100
101static void cia_enable_irq(unsigned int irq)
102{
103	unsigned char mask;
104
105	if (irq >= IRQ_AMIGA_CIAB) {
106		mask = 1 << (irq - IRQ_AMIGA_CIAB);
107		cia_set_irq(&ciab_base, mask);
108		cia_able_irq(&ciab_base, CIA_ICR_SETCLR | mask);
109	} else {
110		mask = 1 << (irq - IRQ_AMIGA_CIAA);
111		cia_set_irq(&ciaa_base, mask);
112		cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | mask);
113	}
114}
115
116static void cia_disable_irq(unsigned int irq)
117{
118	if (irq >= IRQ_AMIGA_CIAB)
119		cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB));
120	else
121		cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA));
122}
123
124static struct irq_controller cia_irq_controller = {
125	.name		= "cia",
126	.lock		= __SPIN_LOCK_UNLOCKED(cia_irq_controller.lock),
127	.enable		= cia_enable_irq,
128	.disable	= cia_disable_irq,
129};
130
131/*
132 * Override auto irq 2 & 6 and use them as general chain
133 * for external interrupts, we link the CIA interrupt sources
134 * into this chain.
135 */
136
137static void auto_enable_irq(unsigned int irq)
138{
139	switch (irq) {
140	case IRQ_AUTO_2:
141		amiga_custom.intena = IF_SETCLR | IF_PORTS;
142		break;
143	case IRQ_AUTO_6:
144		amiga_custom.intena = IF_SETCLR | IF_EXTER;
145		break;
146	}
147}
148
149static void auto_disable_irq(unsigned int irq)
150{
151	switch (irq) {
152	case IRQ_AUTO_2:
153		amiga_custom.intena = IF_PORTS;
154		break;
155	case IRQ_AUTO_6:
156		amiga_custom.intena = IF_EXTER;
157		break;
158	}
159}
160
161static struct irq_controller auto_irq_controller = {
162	.name		= "auto",
163	.lock		= __SPIN_LOCK_UNLOCKED(auto_irq_controller.lock),
164	.enable		= auto_enable_irq,
165	.disable	= auto_disable_irq,
166};
167
168void __init cia_init_IRQ(struct ciabase *base)
169{
170	m68k_setup_irq_controller(&cia_irq_controller, base->cia_irq, CIA_IRQS);
171
172	/* clear any pending interrupt and turn off all interrupts */
173	cia_set_irq(base, CIA_ICR_ALL);
174	cia_able_irq(base, CIA_ICR_ALL);
175
176	/* override auto int and install CIA handler */
177	m68k_setup_irq_controller(&auto_irq_controller, base->handler_irq, 1);
178	m68k_irq_startup(base->handler_irq);
179	request_irq(base->handler_irq, cia_handler, IRQF_SHARED, base->name, base);
180}
181