11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds * linux/arch/m68k/amiga/cia.c - CIA support
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * Copyright (C) 1996 Roman Zippel
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * The concept of some functions bases on the original Amiga OS function
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public
91da177e4SLinus Torvalds * License. See the file COPYING in the main directory of this archive
101da177e4SLinus Torvalds * for more details.
111da177e4SLinus Torvalds */
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds #include <linux/types.h>
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/sched.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/kernel_stat.h>
181da177e4SLinus Torvalds #include <linux/init.h>
191da177e4SLinus Torvalds #include <linux/seq_file.h>
201da177e4SLinus Torvalds #include <linux/interrupt.h>
2114b4319aSThomas Gleixner #include <linux/irq.h>
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds #include <asm/irq.h>
241da177e4SLinus Torvalds #include <asm/amigahw.h>
251da177e4SLinus Torvalds #include <asm/amigaints.h>
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds struct ciabase {
281da177e4SLinus Torvalds volatile struct CIA *cia;
291da177e4SLinus Torvalds unsigned char icr_mask, icr_data;
301da177e4SLinus Torvalds unsigned short int_mask;
311da177e4SLinus Torvalds int handler_irq, cia_irq, server_irq;
321da177e4SLinus Torvalds char *name;
331da177e4SLinus Torvalds } ciaa_base = {
341da177e4SLinus Torvalds .cia = &ciaa,
351da177e4SLinus Torvalds .int_mask = IF_PORTS,
3674be8d08SRoman Zippel .handler_irq = IRQ_AMIGA_PORTS,
371da177e4SLinus Torvalds .cia_irq = IRQ_AMIGA_CIAA,
3874be8d08SRoman Zippel .name = "CIAA"
391da177e4SLinus Torvalds }, ciab_base = {
401da177e4SLinus Torvalds .cia = &ciab,
411da177e4SLinus Torvalds .int_mask = IF_EXTER,
4274be8d08SRoman Zippel .handler_irq = IRQ_AMIGA_EXTER,
431da177e4SLinus Torvalds .cia_irq = IRQ_AMIGA_CIAB,
4474be8d08SRoman Zippel .name = "CIAB"
451da177e4SLinus Torvalds };
461da177e4SLinus Torvalds
471da177e4SLinus Torvalds /*
481da177e4SLinus Torvalds * Cause or clear CIA interrupts, return old interrupt status.
491da177e4SLinus Torvalds */
501da177e4SLinus Torvalds
cia_set_irq(struct ciabase * base,unsigned char mask)511da177e4SLinus Torvalds unsigned char cia_set_irq(struct ciabase *base, unsigned char mask)
521da177e4SLinus Torvalds {
531da177e4SLinus Torvalds unsigned char old;
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds old = (base->icr_data |= base->cia->icr);
561da177e4SLinus Torvalds if (mask & CIA_ICR_SETCLR)
571da177e4SLinus Torvalds base->icr_data |= mask;
581da177e4SLinus Torvalds else
591da177e4SLinus Torvalds base->icr_data &= ~mask;
601da177e4SLinus Torvalds if (base->icr_data & base->icr_mask)
61b4290a23SAl Viro amiga_custom.intreq = IF_SETCLR | base->int_mask;
621da177e4SLinus Torvalds return old & base->icr_mask;
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds
651da177e4SLinus Torvalds /*
661da177e4SLinus Torvalds * Enable or disable CIA interrupts, return old interrupt mask,
671da177e4SLinus Torvalds */
681da177e4SLinus Torvalds
cia_able_irq(struct ciabase * base,unsigned char mask)691da177e4SLinus Torvalds unsigned char cia_able_irq(struct ciabase *base, unsigned char mask)
701da177e4SLinus Torvalds {
7174be8d08SRoman Zippel unsigned char old;
721da177e4SLinus Torvalds
731da177e4SLinus Torvalds old = base->icr_mask;
741da177e4SLinus Torvalds base->icr_data |= base->cia->icr;
751da177e4SLinus Torvalds base->cia->icr = mask;
761da177e4SLinus Torvalds if (mask & CIA_ICR_SETCLR)
771da177e4SLinus Torvalds base->icr_mask |= mask;
781da177e4SLinus Torvalds else
791da177e4SLinus Torvalds base->icr_mask &= ~mask;
801da177e4SLinus Torvalds base->icr_mask &= CIA_ICR_ALL;
811da177e4SLinus Torvalds if (base->icr_data & base->icr_mask)
82b4290a23SAl Viro amiga_custom.intreq = IF_SETCLR | base->int_mask;
831da177e4SLinus Torvalds return old;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds
cia_handler(int irq,void * dev_id)862850bc27SAl Viro static irqreturn_t cia_handler(int irq, void *dev_id)
871da177e4SLinus Torvalds {
8815aafa2fSJeff Garzik struct ciabase *base = dev_id;
8974be8d08SRoman Zippel int mach_irq;
901da177e4SLinus Torvalds unsigned char ints;
91*1efdd4bdSFinn Thain unsigned long flags;
921da177e4SLinus Torvalds
93*1efdd4bdSFinn Thain /* Interrupts get disabled while the timer irq flag is cleared and
94*1efdd4bdSFinn Thain * the timer interrupt serviced.
95*1efdd4bdSFinn Thain */
961da177e4SLinus Torvalds mach_irq = base->cia_irq;
97*1efdd4bdSFinn Thain local_irq_save(flags);
981da177e4SLinus Torvalds ints = cia_set_irq(base, CIA_ICR_ALL);
99b4290a23SAl Viro amiga_custom.intreq = base->int_mask;
100*1efdd4bdSFinn Thain if (ints & 1)
101*1efdd4bdSFinn Thain generic_handle_irq(mach_irq);
102*1efdd4bdSFinn Thain local_irq_restore(flags);
103*1efdd4bdSFinn Thain mach_irq++, ints >>= 1;
10474be8d08SRoman Zippel for (; ints; mach_irq++, ints >>= 1) {
10574be8d08SRoman Zippel if (ints & 1)
1061425df87SGeert Uytterhoeven generic_handle_irq(mach_irq);
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds return IRQ_HANDLED;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds
cia_irq_enable(struct irq_data * data)111e8abf5e7SGeert Uytterhoeven static void cia_irq_enable(struct irq_data *data)
11274be8d08SRoman Zippel {
113e8abf5e7SGeert Uytterhoeven unsigned int irq = data->irq;
11474be8d08SRoman Zippel unsigned char mask;
11574be8d08SRoman Zippel
11674be8d08SRoman Zippel if (irq >= IRQ_AMIGA_CIAB) {
11774be8d08SRoman Zippel mask = 1 << (irq - IRQ_AMIGA_CIAB);
11874be8d08SRoman Zippel cia_set_irq(&ciab_base, mask);
11974be8d08SRoman Zippel cia_able_irq(&ciab_base, CIA_ICR_SETCLR | mask);
12074be8d08SRoman Zippel } else {
12174be8d08SRoman Zippel mask = 1 << (irq - IRQ_AMIGA_CIAA);
12274be8d08SRoman Zippel cia_set_irq(&ciaa_base, mask);
12374be8d08SRoman Zippel cia_able_irq(&ciaa_base, CIA_ICR_SETCLR | mask);
12474be8d08SRoman Zippel }
12574be8d08SRoman Zippel }
12674be8d08SRoman Zippel
cia_irq_disable(struct irq_data * data)127e8abf5e7SGeert Uytterhoeven static void cia_irq_disable(struct irq_data *data)
12874be8d08SRoman Zippel {
129e8abf5e7SGeert Uytterhoeven unsigned int irq = data->irq;
130e8abf5e7SGeert Uytterhoeven
13174be8d08SRoman Zippel if (irq >= IRQ_AMIGA_CIAB)
13274be8d08SRoman Zippel cia_able_irq(&ciab_base, 1 << (irq - IRQ_AMIGA_CIAB));
13374be8d08SRoman Zippel else
13474be8d08SRoman Zippel cia_able_irq(&ciaa_base, 1 << (irq - IRQ_AMIGA_CIAA));
13574be8d08SRoman Zippel }
13674be8d08SRoman Zippel
137c288bf25SGeert Uytterhoeven static struct irq_chip cia_irq_chip = {
13874be8d08SRoman Zippel .name = "cia",
139e8abf5e7SGeert Uytterhoeven .irq_enable = cia_irq_enable,
140e8abf5e7SGeert Uytterhoeven .irq_disable = cia_irq_disable,
14174be8d08SRoman Zippel };
14274be8d08SRoman Zippel
14374be8d08SRoman Zippel /*
14474be8d08SRoman Zippel * Override auto irq 2 & 6 and use them as general chain
14574be8d08SRoman Zippel * for external interrupts, we link the CIA interrupt sources
14674be8d08SRoman Zippel * into this chain.
14774be8d08SRoman Zippel */
14874be8d08SRoman Zippel
auto_irq_enable(struct irq_data * data)149e8abf5e7SGeert Uytterhoeven static void auto_irq_enable(struct irq_data *data)
15074be8d08SRoman Zippel {
151e8abf5e7SGeert Uytterhoeven switch (data->irq) {
15274be8d08SRoman Zippel case IRQ_AUTO_2:
15374be8d08SRoman Zippel amiga_custom.intena = IF_SETCLR | IF_PORTS;
15474be8d08SRoman Zippel break;
15574be8d08SRoman Zippel case IRQ_AUTO_6:
15674be8d08SRoman Zippel amiga_custom.intena = IF_SETCLR | IF_EXTER;
15774be8d08SRoman Zippel break;
15874be8d08SRoman Zippel }
15974be8d08SRoman Zippel }
16074be8d08SRoman Zippel
auto_irq_disable(struct irq_data * data)161e8abf5e7SGeert Uytterhoeven static void auto_irq_disable(struct irq_data *data)
16274be8d08SRoman Zippel {
163e8abf5e7SGeert Uytterhoeven switch (data->irq) {
16474be8d08SRoman Zippel case IRQ_AUTO_2:
16574be8d08SRoman Zippel amiga_custom.intena = IF_PORTS;
16674be8d08SRoman Zippel break;
16774be8d08SRoman Zippel case IRQ_AUTO_6:
16874be8d08SRoman Zippel amiga_custom.intena = IF_EXTER;
16974be8d08SRoman Zippel break;
17074be8d08SRoman Zippel }
17174be8d08SRoman Zippel }
17274be8d08SRoman Zippel
173c288bf25SGeert Uytterhoeven static struct irq_chip auto_irq_chip = {
17474be8d08SRoman Zippel .name = "auto",
175e8abf5e7SGeert Uytterhoeven .irq_enable = auto_irq_enable,
176e8abf5e7SGeert Uytterhoeven .irq_disable = auto_irq_disable,
17774be8d08SRoman Zippel };
17874be8d08SRoman Zippel
cia_init_IRQ(struct ciabase * base)1791da177e4SLinus Torvalds void __init cia_init_IRQ(struct ciabase *base)
1801da177e4SLinus Torvalds {
181edb34725SGeert Uytterhoeven m68k_setup_irq_controller(&cia_irq_chip, handle_simple_irq,
182edb34725SGeert Uytterhoeven base->cia_irq, CIA_IRQS);
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds /* clear any pending interrupt and turn off all interrupts */
1851da177e4SLinus Torvalds cia_set_irq(base, CIA_ICR_ALL);
1861da177e4SLinus Torvalds cia_able_irq(base, CIA_ICR_ALL);
1871da177e4SLinus Torvalds
18874be8d08SRoman Zippel /* override auto int and install CIA handler */
189edb34725SGeert Uytterhoeven m68k_setup_irq_controller(&auto_irq_chip, handle_simple_irq,
190edb34725SGeert Uytterhoeven base->handler_irq, 1);
191e8abf5e7SGeert Uytterhoeven m68k_irq_startup_irq(base->handler_irq);
19266acd258SGeert Uytterhoeven if (request_irq(base->handler_irq, cia_handler, IRQF_SHARED,
19366acd258SGeert Uytterhoeven base->name, base))
19466acd258SGeert Uytterhoeven pr_err("Couldn't register %s interrupt\n", base->name);
1951da177e4SLinus Torvalds }
196