Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/m68k/amiga/cia.c
26470 views
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
#include <linux/irq.h>
22
23
#include <asm/irq.h>
24
#include <asm/amigahw.h>
25
#include <asm/amigaints.h>
26
27
struct ciabase {
28
volatile struct CIA *cia;
29
unsigned char icr_mask, icr_data;
30
unsigned short int_mask;
31
int handler_irq, cia_irq, server_irq;
32
char *name;
33
} ciaa_base = {
34
.cia = &ciaa,
35
.int_mask = IF_PORTS,
36
.handler_irq = IRQ_AMIGA_PORTS,
37
.cia_irq = IRQ_AMIGA_CIAA,
38
.name = "CIAA"
39
}, ciab_base = {
40
.cia = &ciab,
41
.int_mask = IF_EXTER,
42
.handler_irq = IRQ_AMIGA_EXTER,
43
.cia_irq = IRQ_AMIGA_CIAB,
44
.name = "CIAB"
45
};
46
47
/*
48
* Cause or clear CIA interrupts, return old interrupt status.
49
*/
50
51
unsigned char cia_set_irq(struct ciabase *base, unsigned char mask)
52
{
53
unsigned char old;
54
55
old = (base->icr_data |= base->cia->icr);
56
if (mask & CIA_ICR_SETCLR)
57
base->icr_data |= mask;
58
else
59
base->icr_data &= ~mask;
60
if (base->icr_data & base->icr_mask)
61
amiga_custom.intreq = IF_SETCLR | base->int_mask;
62
return old & base->icr_mask;
63
}
64
65
/*
66
* Enable or disable CIA interrupts, return old interrupt mask,
67
*/
68
69
unsigned char cia_able_irq(struct ciabase *base, unsigned char mask)
70
{
71
unsigned char old;
72
73
old = base->icr_mask;
74
base->icr_data |= base->cia->icr;
75
base->cia->icr = mask;
76
if (mask & CIA_ICR_SETCLR)
77
base->icr_mask |= mask;
78
else
79
base->icr_mask &= ~mask;
80
base->icr_mask &= CIA_ICR_ALL;
81
if (base->icr_data & base->icr_mask)
82
amiga_custom.intreq = IF_SETCLR | base->int_mask;
83
return old;
84
}
85
86
static irqreturn_t cia_handler(int irq, void *dev_id)
87
{
88
struct ciabase *base = dev_id;
89
int mach_irq;
90
unsigned char ints;
91
unsigned long flags;
92
93
/* Interrupts get disabled while the timer irq flag is cleared and
94
* the timer interrupt serviced.
95
*/
96
mach_irq = base->cia_irq;
97
local_irq_save(flags);
98
ints = cia_set_irq(base, CIA_ICR_ALL);
99
amiga_custom.intreq = base->int_mask;
100
if (ints & 1)
101
generic_handle_irq(mach_irq);
102
local_irq_restore(flags);
103
mach_irq++, ints >>= 1;
104
for (; ints; mach_irq++, ints >>= 1) {
105
if (ints & 1)
106
generic_handle_irq(mach_irq);
107
}
108
return IRQ_HANDLED;
109
}
110
111
static void cia_irq_enable(struct irq_data *data)
112
{
113
unsigned int irq = data->irq;
114
unsigned char mask;
115
116
if (irq >= IRQ_AMIGA_CIAB) {
117
mask = 1 << (irq - IRQ_AMIGA_CIAB);
118
cia_set_irq(&ciab_base, mask);
119
cia_able_irq(&ciab_base, CIA_ICR_SETCLR | mask);
120
} else {
121
mask = 1 << (irq - IRQ_AMIGA_CIAA);
122
cia_set_irq(&ciaa_base, mask);
123
cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | mask);
124
}
125
}
126
127
static void cia_irq_disable(struct irq_data *data)
128
{
129
unsigned int irq = data->irq;
130
131
if (irq >= IRQ_AMIGA_CIAB)
132
cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB));
133
else
134
cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA));
135
}
136
137
static struct irq_chip cia_irq_chip = {
138
.name = "cia",
139
.irq_enable = cia_irq_enable,
140
.irq_disable = cia_irq_disable,
141
};
142
143
/*
144
* Override auto irq 2 & 6 and use them as general chain
145
* for external interrupts, we link the CIA interrupt sources
146
* into this chain.
147
*/
148
149
static void auto_irq_enable(struct irq_data *data)
150
{
151
switch (data->irq) {
152
case IRQ_AUTO_2:
153
amiga_custom.intena = IF_SETCLR | IF_PORTS;
154
break;
155
case IRQ_AUTO_6:
156
amiga_custom.intena = IF_SETCLR | IF_EXTER;
157
break;
158
}
159
}
160
161
static void auto_irq_disable(struct irq_data *data)
162
{
163
switch (data->irq) {
164
case IRQ_AUTO_2:
165
amiga_custom.intena = IF_PORTS;
166
break;
167
case IRQ_AUTO_6:
168
amiga_custom.intena = IF_EXTER;
169
break;
170
}
171
}
172
173
static struct irq_chip auto_irq_chip = {
174
.name = "auto",
175
.irq_enable = auto_irq_enable,
176
.irq_disable = auto_irq_disable,
177
};
178
179
void __init cia_init_IRQ(struct ciabase *base)
180
{
181
m68k_setup_irq_controller(&cia_irq_chip, handle_simple_irq,
182
base->cia_irq, CIA_IRQS);
183
184
/* clear any pending interrupt and turn off all interrupts */
185
cia_set_irq(base, CIA_ICR_ALL);
186
cia_able_irq(base, CIA_ICR_ALL);
187
188
/* override auto int and install CIA handler */
189
m68k_setup_irq_controller(&auto_irq_chip, handle_simple_irq,
190
base->handler_irq, 1);
191
m68k_irq_startup_irq(base->handler_irq);
192
if (request_irq(base->handler_irq, cia_handler, IRQF_SHARED,
193
base->name, base))
194
pr_err("Couldn't register %s interrupt\n", base->name);
195
}
196
197