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>
2014-03-19 14:17:40 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# 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 ;
2018-12-01 03:53:10 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2018-12-01 03:53:10 +03:00
/* Interrupts get disabled while the timer irq flag is cleared and
* the timer interrupt serviced .
*/
2005-04-17 02:20:36 +04:00
mach_irq = base - > cia_irq ;
2018-12-01 03:53:10 +03:00
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
ints = cia_set_irq ( base , CIA_ICR_ALL ) ;
2006-01-12 12:06:12 +03:00
amiga_custom . intreq = base - > int_mask ;
2018-12-01 03:53:10 +03:00
if ( ints & 1 )
generic_handle_irq ( mach_irq ) ;
local_irq_restore ( flags ) ;
mach_irq + + , ints > > = 1 ;
2006-06-25 16:47:01 +04:00
for ( ; ints ; mach_irq + + , ints > > = 1 ) {
if ( ints & 1 )
2011-07-01 22:39:19 +04:00
generic_handle_irq ( mach_irq ) ;
2005-04-17 02:20:36 +04:00
}
return IRQ_HANDLED ;
}
2011-04-18 00:53:04 +04:00
static void cia_irq_enable ( struct irq_data * data )
2005-04-17 02:20:36 +04:00
{
2011-04-18 00:53:04 +04:00
unsigned int irq = data - > irq ;
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
2011-04-18 00:53:04 +04:00
static void cia_irq_disable ( struct irq_data * data )
2006-06-25 16:47:01 +04:00
{
2011-04-18 00:53:04 +04:00
unsigned int irq = data - > irq ;
2006-06-25 16:47:01 +04:00
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
2011-04-14 00:31:28 +04:00
static struct irq_chip cia_irq_chip = {
2006-06-25 16:47:01 +04:00
. name = " cia " ,
2011-04-18 00:53:04 +04:00
. irq_enable = cia_irq_enable ,
. irq_disable = cia_irq_disable ,
2006-06-25 16:47:01 +04:00
} ;
/*
* 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
2011-04-18 00:53:04 +04:00
static void auto_irq_enable ( struct irq_data * data )
2006-06-25 16:47:01 +04:00
{
2011-04-18 00:53:04 +04:00
switch ( data - > irq ) {
2006-06-25 16:47:01 +04:00
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
}
2011-04-18 00:53:04 +04:00
static void auto_irq_disable ( struct irq_data * data )
2005-04-17 02:20:36 +04:00
{
2011-04-18 00:53:04 +04:00
switch ( data - > irq ) {
2006-06-25 16:47:01 +04:00
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
}
2011-04-14 00:31:28 +04:00
static struct irq_chip auto_irq_chip = {
2006-06-25 16:47:01 +04:00
. name = " auto " ,
2011-04-18 00:53:04 +04:00
. irq_enable = auto_irq_enable ,
. irq_disable = auto_irq_disable ,
2006-06-25 16:47:01 +04:00
} ;
void __init cia_init_IRQ ( struct ciabase * base )
{
2011-06-01 13:15:21 +04:00
m68k_setup_irq_controller ( & cia_irq_chip , handle_simple_irq ,
base - > cia_irq , CIA_IRQS ) ;
2006-06-25 16:47:01 +04:00
/* 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 */
2011-06-01 13:15:21 +04:00
m68k_setup_irq_controller ( & auto_irq_chip , handle_simple_irq ,
base - > handler_irq , 1 ) ;
2011-04-18 00:53:04 +04:00
m68k_irq_startup_irq ( base - > handler_irq ) ;
2008-12-30 16:00:34 +03:00
if ( request_irq ( base - > handler_irq , cia_handler , IRQF_SHARED ,
base - > name , base ) )
pr_err ( " Couldn't register %s interrupt \n " , base - > name ) ;
2005-04-17 02:20:36 +04:00
}