xref: /openbmc/linux/arch/m68k/amiga/cia.c (revision c39f2d9db0fd81ea20bb5cce9b3f082ca63753e2)
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