2005-04-16 15:20:36 -07:00
/*
* arch / sh / boards / dreamcast / irq . c
*
* Holly IRQ support for the Sega Dreamcast .
*
* Copyright ( c ) 2001 , 2002 M . R . Brown < mrbrown @ 0xd6 . org >
*
* This file is part of the LinuxDC project ( www . linuxdc . org )
* Released under the terms of the GNU GPL v2 .0
*/
# include <linux/irq.h>
2008-12-14 12:02:26 +00:00
# include <linux/io.h>
2012-05-18 23:36:44 +09:00
# include <linux/irq.h>
# include <linux/export.h>
# include <linux/err.h>
2008-07-29 08:09:44 +09:00
# include <mach/sysasic.h>
2005-04-16 15:20:36 -07:00
2008-12-14 12:02:26 +00:00
/*
* Dreamcast System ASIC Hardware Events -
*
* The Dreamcast ' s System ASIC ( a . k . a . Holly ) is responsible for receiving
* hardware events from system peripherals and triggering an SH7750 IRQ .
* Hardware events can trigger IRQs 13 , 11 , or 9 depending on which bits are
* set in the Event Mask Registers ( EMRs ) . When a hardware event is
* triggered , its corresponding bit in the Event Status Registers ( ESRs )
* is set , and that bit should be rewritten to the ESR to acknowledge that
* event .
*
* There are three 32 - bit ESRs located at 0xa05f6900 - 0xa05f6908 . Event
* types can be found in arch / sh / include / mach - dreamcast / mach / sysasic . h .
* There are three groups of EMRs that parallel the ESRs . Each EMR group
* corresponds to an IRQ , so 0xa05f6910 - 0xa05f6918 triggers IRQ 13 ,
* 0xa05f6920 - 0xa05f6928 triggers IRQ 11 , and 0xa05f6930 - 0xa05f6938
* triggers IRQ 9.
*
* In the kernel , these events are mapped to virtual IRQs so that drivers can
* respond to them as they would a normal interrupt . In order to keep this
* mapping simple , the events are mapped as :
*
* 6900 / 6910 - Events 0 - 31 , IRQ 13
* 6904 / 6924 - Events 32 - 63 , IRQ 11
* 6908 / 6938 - Events 64 - 95 , IRQ 9
*
*/
2005-04-16 15:20:36 -07:00
# define ESR_BASE 0x005f6900 /* Base event status register */
# define EMR_BASE 0x005f6910 /* Base event mask register */
2008-12-14 12:02:26 +00:00
/*
* Helps us determine the EMR group that this event belongs to : 0 = 0x6910 ,
* 1 = 0x6920 , 2 = 0x6930 ; also determine the event offset .
*/
2005-04-16 15:20:36 -07:00
# define LEVEL(event) (((event) - HW_EVENT_IRQ_BASE) / 32)
2011-03-30 22:57:33 -03:00
/* Return the hardware event's bit position within the EMR/ESR */
2005-04-16 15:20:36 -07:00
# define EVENT_BIT(event) (((event) - HW_EVENT_IRQ_BASE) & 31)
2008-12-14 12:02:26 +00:00
/*
* For each of these * _irq routines , the IRQ passed in is the virtual IRQ
* ( logically mapped to the corresponding bit for the hardware event ) .
*/
2005-04-16 15:20:36 -07:00
/* Disable the hardware event by masking its bit in its EMR */
2010-10-27 14:36:28 +09:00
static inline void disable_systemasic_irq ( struct irq_data * data )
2005-04-16 15:20:36 -07:00
{
2010-10-27 14:36:28 +09:00
unsigned int irq = data - > irq ;
2008-12-14 12:02:26 +00:00
__u32 emr = EMR_BASE + ( LEVEL ( irq ) < < 4 ) + ( LEVEL ( irq ) < < 2 ) ;
__u32 mask ;
2005-04-16 15:20:36 -07:00
2008-12-14 12:02:26 +00:00
mask = inl ( emr ) ;
mask & = ~ ( 1 < < EVENT_BIT ( irq ) ) ;
outl ( mask , emr ) ;
2005-04-16 15:20:36 -07:00
}
/* Enable the hardware event by setting its bit in its EMR */
2010-10-27 14:36:28 +09:00
static inline void enable_systemasic_irq ( struct irq_data * data )
2005-04-16 15:20:36 -07:00
{
2010-10-27 14:36:28 +09:00
unsigned int irq = data - > irq ;
2008-12-14 12:02:26 +00:00
__u32 emr = EMR_BASE + ( LEVEL ( irq ) < < 4 ) + ( LEVEL ( irq ) < < 2 ) ;
__u32 mask ;
2005-04-16 15:20:36 -07:00
2008-12-14 12:02:26 +00:00
mask = inl ( emr ) ;
mask | = ( 1 < < EVENT_BIT ( irq ) ) ;
outl ( mask , emr ) ;
2005-04-16 15:20:36 -07:00
}
/* Acknowledge a hardware event by writing its bit back to its ESR */
2010-10-27 14:36:28 +09:00
static void mask_ack_systemasic_irq ( struct irq_data * data )
2005-04-16 15:20:36 -07:00
{
2010-10-27 14:36:28 +09:00
unsigned int irq = data - > irq ;
2008-12-14 12:02:26 +00:00
__u32 esr = ESR_BASE + ( LEVEL ( irq ) < < 2 ) ;
2010-10-27 14:36:28 +09:00
disable_systemasic_irq ( data ) ;
2008-12-14 12:02:26 +00:00
outl ( ( 1 < < EVENT_BIT ( irq ) ) , esr ) ;
2005-04-16 15:20:36 -07:00
}
2008-12-14 12:02:26 +00:00
struct irq_chip systemasic_int = {
. name = " System ASIC " ,
2010-10-27 14:36:28 +09:00
. irq_mask = disable_systemasic_irq ,
. irq_mask_ack = mask_ack_systemasic_irq ,
. irq_unmask = enable_systemasic_irq ,
2005-04-16 15:20:36 -07:00
} ;
/*
* Map the hardware event indicated by the processor IRQ to a virtual IRQ .
*/
int systemasic_irq_demux ( int irq )
{
2008-12-14 12:02:26 +00:00
__u32 emr , esr , status , level ;
__u32 j , bit ;
switch ( irq ) {
case 13 :
level = 0 ;
break ;
case 11 :
level = 1 ;
break ;
case 9 :
level = 2 ;
break ;
default :
return irq ;
}
emr = EMR_BASE + ( level < < 4 ) + ( level < < 2 ) ;
esr = ESR_BASE + ( level < < 2 ) ;
/* Mask the ESR to filter any spurious, unwanted interrupts */
status = inl ( esr ) ;
status & = inl ( emr ) ;
/* Now scan and find the first set bit as the event to map */
for ( bit = 1 , j = 0 ; j < 32 ; bit < < = 1 , j + + ) {
if ( status & bit ) {
irq = HW_EVENT_IRQ_BASE + j + ( level < < 5 ) ;
return irq ;
}
}
/* Not reached */
return irq ;
2005-04-16 15:20:36 -07:00
}
2010-02-02 18:01:55 +09:00
void systemasic_irq_init ( void )
{
2012-05-18 23:36:44 +09:00
int irq_base , i ;
2010-02-02 18:01:55 +09:00
2012-05-18 23:36:44 +09:00
irq_base = irq_alloc_descs ( HW_EVENT_IRQ_BASE , HW_EVENT_IRQ_BASE ,
HW_EVENT_IRQ_MAX - HW_EVENT_IRQ_BASE , - 1 ) ;
if ( IS_ERR_VALUE ( irq_base ) ) {
pr_err ( " %s: failed hooking irqs \n " , __func__ ) ;
return ;
}
2010-02-02 18:01:55 +09:00
2012-05-18 23:36:44 +09:00
for ( i = HW_EVENT_IRQ_BASE ; i < HW_EVENT_IRQ_MAX ; i + + )
2011-03-24 16:31:17 +01:00
irq_set_chip_and_handler ( i , & systemasic_int , handle_level_irq ) ;
2010-02-02 18:01:55 +09:00
}