2005-04-17 02:20:36 +04:00
/*
* linux / arch / m68k / amiga / cia . c - CIA support
*
* Copyright ( C ) 1996 Roman Zippel
*
* The concept of some functions bases on the original Amiga OS function
*
* 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/types.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/errno.h>
# include <linux/kernel_stat.h>
# include <linux/init.h>
# include <linux/seq_file.h>
# include <linux/interrupt.h>
# include <asm/irq.h>
# include <asm/amigahw.h>
# include <asm/amigaints.h>
struct ciabase {
volatile struct CIA * cia ;
unsigned char icr_mask , icr_data ;
unsigned short int_mask ;
int handler_irq , cia_irq , server_irq ;
char * name ;
} ciaa_base = {
. cia = & ciaa ,
. int_mask = IF_PORTS ,
2006-06-25 16:47:01 +04:00
. handler_irq = IRQ_AMIGA_PORTS ,
2005-04-17 02:20:36 +04:00
. cia_irq = IRQ_AMIGA_CIAA ,
2006-06-25 16:47:01 +04:00
. name = " CIAA "
2005-04-17 02:20:36 +04:00
} , ciab_base = {
. cia = & ciab ,
. int_mask = IF_EXTER ,
2006-06-25 16:47:01 +04:00
. handler_irq = IRQ_AMIGA_EXTER ,
2005-04-17 02:20:36 +04:00
. cia_irq = IRQ_AMIGA_CIAB ,
2006-06-25 16:47:01 +04:00
. name = " CIAB "
2005-04-17 02:20:36 +04:00
} ;
/*
* Cause or clear CIA interrupts , return old interrupt status .
*/
unsigned char cia_set_irq ( struct ciabase * base , unsigned char mask )
{
unsigned char old ;
old = ( base - > icr_data | = base - > cia - > icr ) ;
if ( mask & CIA_ICR_SETCLR )
base - > icr_data | = mask ;
else
base - > icr_data & = ~ mask ;
if ( base - > icr_data & base - > icr_mask )
2006-01-12 12:06:12 +03:00
amiga_custom . intreq = IF_SETCLR | base - > int_mask ;
2005-04-17 02:20:36 +04:00
return old & base - > icr_mask ;
}
/*
* Enable or disable CIA interrupts , return old interrupt mask ,
*/
unsigned char cia_able_irq ( struct ciabase * base , unsigned char mask )
{
2006-06-25 16:47:01 +04:00
unsigned char old ;
2005-04-17 02:20:36 +04:00
old = base - > icr_mask ;
base - > icr_data | = base - > cia - > icr ;
base - > cia - > icr = mask ;
if ( mask & CIA_ICR_SETCLR )
base - > icr_mask | = mask ;
else
base - > icr_mask & = ~ mask ;
base - > icr_mask & = CIA_ICR_ALL ;
if ( base - > icr_data & base - > icr_mask )
2006-01-12 12:06:12 +03:00
amiga_custom . intreq = IF_SETCLR | base - > int_mask ;
2005-04-17 02:20:36 +04:00
return old ;
}
2006-10-07 17:16:45 +04:00
static irqreturn_t cia_handler ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2008-02-06 12:36:20 +03:00
struct ciabase * base = dev_id ;
2006-06-25 16:47:01 +04:00
int mach_irq ;
2005-04-17 02:20:36 +04:00
unsigned char ints ;
mach_irq = base - > cia_irq ;
ints = cia_set_irq ( base , CIA_ICR_ALL ) ;
2006-01-12 12:06:12 +03:00
amiga_custom . intreq = base - > int_mask ;
2006-06-25 16:47:01 +04:00
for ( ; ints ; mach_irq + + , ints > > = 1 ) {
if ( ints & 1 )
2006-10-07 17:16:45 +04:00
m68k_handle_int ( mach_irq ) ;
2005-04-17 02:20:36 +04:00
}
return IRQ_HANDLED ;
}
2006-06-25 16:47:01 +04:00
static void cia_enable_irq ( unsigned int irq )
2005-04-17 02:20:36 +04:00
{
2006-06-25 16:47:01 +04:00
unsigned char mask ;
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:01 +04:00
if ( irq > = IRQ_AMIGA_CIAB ) {
mask = 1 < < ( irq - IRQ_AMIGA_CIAB ) ;
cia_set_irq ( & ciab_base , mask ) ;
cia_able_irq ( & ciab_base , CIA_ICR_SETCLR | mask ) ;
} else {
mask = 1 < < ( irq - IRQ_AMIGA_CIAA ) ;
cia_set_irq ( & ciaa_base , mask ) ;
cia_able_irq ( & ciaa_base , CIA_ICR_SETCLR | mask ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-25 16:47:01 +04:00
}
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:01 +04:00
static void cia_disable_irq ( unsigned int irq )
{
if ( irq > = IRQ_AMIGA_CIAB )
cia_able_irq ( & ciab_base , 1 < < ( irq - IRQ_AMIGA_CIAB ) ) ;
else
cia_able_irq ( & ciaa_base , 1 < < ( irq - IRQ_AMIGA_CIAA ) ) ;
}
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:01 +04:00
static struct irq_controller cia_irq_controller = {
. name = " cia " ,
2007-05-07 01:50:54 +04:00
. lock = __SPIN_LOCK_UNLOCKED ( cia_irq_controller . lock ) ,
2006-06-25 16:47:01 +04:00
. enable = cia_enable_irq ,
. disable = cia_disable_irq ,
} ;
/*
* Override auto irq 2 & 6 and use them as general chain
* for external interrupts , we link the CIA interrupt sources
* into this chain .
*/
2005-04-17 02:20:36 +04:00
2006-06-25 16:47:01 +04:00
static void auto_enable_irq ( unsigned int irq )
{
switch ( irq ) {
case IRQ_AUTO_2 :
amiga_custom . intena = IF_SETCLR | IF_PORTS ;
break ;
case IRQ_AUTO_6 :
amiga_custom . intena = IF_SETCLR | IF_EXTER ;
break ;
}
2005-04-17 02:20:36 +04:00
}
2006-06-25 16:47:01 +04:00
static void auto_disable_irq ( unsigned int irq )
2005-04-17 02:20:36 +04:00
{
2006-06-25 16:47:01 +04:00
switch ( irq ) {
case IRQ_AUTO_2 :
amiga_custom . intena = IF_PORTS ;
break ;
case IRQ_AUTO_6 :
amiga_custom . intena = IF_EXTER ;
break ;
2005-04-17 02:20:36 +04:00
}
2006-06-25 16:47:01 +04:00
}
static struct irq_controller auto_irq_controller = {
. name = " auto " ,
2007-05-07 01:50:54 +04:00
. lock = __SPIN_LOCK_UNLOCKED ( auto_irq_controller . lock ) ,
2006-06-25 16:47:01 +04:00
. enable = auto_enable_irq ,
. disable = auto_disable_irq ,
} ;
void __init cia_init_IRQ ( struct ciabase * base )
{
m68k_setup_irq_controller ( & cia_irq_controller , base - > cia_irq , CIA_IRQS ) ;
/* clear any pending interrupt and turn off all interrupts */
cia_set_irq ( base , CIA_ICR_ALL ) ;
cia_able_irq ( base , CIA_ICR_ALL ) ;
/* override auto int and install CIA handler */
m68k_setup_irq_controller ( & auto_irq_controller , base - > handler_irq , 1 ) ;
m68k_irq_startup ( base - > handler_irq ) ;
2006-07-02 06:29:19 +04:00
request_irq ( base - > handler_irq , cia_handler , IRQF_SHARED , base - > name , base ) ;
2005-04-17 02:20:36 +04:00
}