2007-02-02 16:37:30 +09:00
/*
* Celleb / Beat Interrupt controller
*
* ( C ) Copyright 2006 - 2007 TOSHIBA CORPORATION
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/percpu.h>
# include <linux/types.h>
# include <asm/machdep.h>
2008-04-24 19:31:40 +10:00
# include "beat_interrupt.h"
# include "beat_wrapper.h"
2007-02-02 16:37:30 +09:00
# define MAX_IRQS NR_IRQS
2010-02-18 02:22:52 +00:00
static DEFINE_RAW_SPINLOCK ( beatic_irq_mask_lock ) ;
2007-02-02 16:37:30 +09:00
static uint64_t beatic_irq_mask_enable [ ( MAX_IRQS + 255 ) / 64 ] ;
static uint64_t beatic_irq_mask_ack [ ( MAX_IRQS + 255 ) / 64 ] ;
2012-02-14 14:06:50 -07:00
static struct irq_domain * beatic_host ;
2007-02-02 16:37:30 +09:00
/*
* In this implementation , " virq " = = " IRQ plug number " ,
* " (irq_hw_number_t)hwirq " = = " IRQ outlet number " .
*/
/* assumption: locked */
static inline void beatic_update_irq_mask ( unsigned int irq_plug )
{
int off ;
unsigned long masks [ 4 ] ;
off = ( irq_plug / 256 ) * 4 ;
masks [ 0 ] = beatic_irq_mask_enable [ off + 0 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ off + 0 ] ;
2007-02-02 16:37:30 +09:00
masks [ 1 ] = beatic_irq_mask_enable [ off + 1 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ off + 1 ] ;
2007-02-02 16:37:30 +09:00
masks [ 2 ] = beatic_irq_mask_enable [ off + 2 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ off + 2 ] ;
2007-02-02 16:37:30 +09:00
masks [ 3 ] = beatic_irq_mask_enable [ off + 3 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ off + 3 ] ;
2007-02-02 16:37:30 +09:00
if ( beat_set_interrupt_mask ( irq_plug & ~ 255UL ,
masks [ 0 ] , masks [ 1 ] , masks [ 2 ] , masks [ 3 ] ) ! = 0 )
panic ( " Failed to set mask IRQ! " ) ;
}
2011-03-07 13:59:28 +00:00
static void beatic_mask_irq ( struct irq_data * d )
2007-02-02 16:37:30 +09:00
{
unsigned long flags ;
2010-02-18 02:22:52 +00:00
raw_spin_lock_irqsave ( & beatic_irq_mask_lock , flags ) ;
2011-03-07 13:59:28 +00:00
beatic_irq_mask_enable [ d - > irq / 64 ] & = ~ ( 1UL < < ( 63 - ( d - > irq % 64 ) ) ) ;
beatic_update_irq_mask ( d - > irq ) ;
2010-02-18 02:22:52 +00:00
raw_spin_unlock_irqrestore ( & beatic_irq_mask_lock , flags ) ;
2007-02-02 16:37:30 +09:00
}
2011-03-07 13:59:28 +00:00
static void beatic_unmask_irq ( struct irq_data * d )
2007-02-02 16:37:30 +09:00
{
unsigned long flags ;
2010-02-18 02:22:52 +00:00
raw_spin_lock_irqsave ( & beatic_irq_mask_lock , flags ) ;
2011-03-07 13:59:28 +00:00
beatic_irq_mask_enable [ d - > irq / 64 ] | = 1UL < < ( 63 - ( d - > irq % 64 ) ) ;
beatic_update_irq_mask ( d - > irq ) ;
2010-02-18 02:22:52 +00:00
raw_spin_unlock_irqrestore ( & beatic_irq_mask_lock , flags ) ;
2007-02-02 16:37:30 +09:00
}
2011-03-07 13:59:28 +00:00
static void beatic_ack_irq ( struct irq_data * d )
2007-02-02 16:37:30 +09:00
{
unsigned long flags ;
2010-02-18 02:22:52 +00:00
raw_spin_lock_irqsave ( & beatic_irq_mask_lock , flags ) ;
2011-03-07 13:59:28 +00:00
beatic_irq_mask_ack [ d - > irq / 64 ] & = ~ ( 1UL < < ( 63 - ( d - > irq % 64 ) ) ) ;
beatic_update_irq_mask ( d - > irq ) ;
2010-02-18 02:22:52 +00:00
raw_spin_unlock_irqrestore ( & beatic_irq_mask_lock , flags ) ;
2007-02-02 16:37:30 +09:00
}
2011-03-07 13:59:28 +00:00
static void beatic_end_irq ( struct irq_data * d )
2007-02-02 16:37:30 +09:00
{
s64 err ;
unsigned long flags ;
2011-03-07 13:59:28 +00:00
err = beat_downcount_of_interrupt ( d - > irq ) ;
2008-03-14 23:19:34 +11:00
if ( err ! = 0 ) {
2007-02-02 16:37:30 +09:00
if ( ( err & 0xFFFFFFFF ) ! = 0xFFFFFFF5 ) /* -11: wrong state */
2009-01-06 14:26:03 +00:00
panic ( " Failed to downcount IRQ! Error = %16llx " , err ) ;
2007-02-02 16:37:30 +09:00
2011-03-07 13:59:28 +00:00
printk ( KERN_ERR " IRQ over-downcounted, plug %d \n " , d - > irq ) ;
2007-02-02 16:37:30 +09:00
}
2010-02-18 02:22:52 +00:00
raw_spin_lock_irqsave ( & beatic_irq_mask_lock , flags ) ;
2011-03-07 13:59:28 +00:00
beatic_irq_mask_ack [ d - > irq / 64 ] | = 1UL < < ( 63 - ( d - > irq % 64 ) ) ;
beatic_update_irq_mask ( d - > irq ) ;
2010-02-18 02:22:52 +00:00
raw_spin_unlock_irqrestore ( & beatic_irq_mask_lock , flags ) ;
2007-02-02 16:37:30 +09:00
}
static struct irq_chip beatic_pic = {
2010-01-31 20:33:41 +00:00
. name = " CELL-BEAT " ,
2011-03-07 13:59:28 +00:00
. irq_unmask = beatic_unmask_irq ,
. irq_mask = beatic_mask_irq ,
. irq_eoi = beatic_end_irq ,
2007-02-02 16:37:30 +09:00
} ;
/*
* Dispose binding hardware IRQ number ( hw ) and Virtuql IRQ number ( virq ) ,
* update flags .
*
* Note that the number ( virq ) is already assigned at upper layer .
*/
2012-02-14 14:06:50 -07:00
static void beatic_pic_host_unmap ( struct irq_domain * h , unsigned int virq )
2007-02-02 16:37:30 +09:00
{
beat_destruct_irq_plug ( virq ) ;
}
/*
* Create or update binding hardware IRQ number ( hw ) and Virtuql
* IRQ number ( virq ) . This is called only once for a given mapping .
*
* Note that the number ( virq ) is already assigned at upper layer .
*/
2012-02-14 14:06:50 -07:00
static int beatic_pic_host_map ( struct irq_domain * h , unsigned int virq ,
2007-02-02 16:37:30 +09:00
irq_hw_number_t hw )
{
int64_t err ;
2008-03-14 23:19:34 +11:00
err = beat_construct_and_connect_irq_plug ( virq , hw ) ;
if ( err < 0 )
2007-02-02 16:37:30 +09:00
return - EIO ;
2011-03-25 15:43:57 +01:00
irq_set_status_flags ( virq , IRQ_LEVEL ) ;
2011-03-25 16:45:20 +01:00
irq_set_chip_and_handler ( virq , & beatic_pic , handle_fasteoi_irq ) ;
2007-02-02 16:37:30 +09:00
return 0 ;
}
/*
* Translate device - tree interrupt spec to irq_hw_number_t style ( ulong ) ,
* to pass away to irq_create_mapping ( ) .
*
* Called from irq_create_of_mapping ( ) only .
* Note : We have only 1 entry to translate .
*/
2012-02-14 14:06:50 -07:00
static int beatic_pic_host_xlate ( struct irq_domain * h , struct device_node * ct ,
2009-12-08 02:39:50 +00:00
const u32 * intspec , unsigned int intsize ,
2007-02-02 16:37:30 +09:00
irq_hw_number_t * out_hwirq ,
unsigned int * out_flags )
{
2009-12-08 02:39:50 +00:00
const u64 * intspec2 = ( const u64 * ) intspec ;
2007-02-02 16:37:30 +09:00
* out_hwirq = * intspec2 ;
* out_flags | = IRQ_TYPE_LEVEL_LOW ;
return 0 ;
}
2012-02-14 14:06:50 -07:00
static int beatic_pic_host_match ( struct irq_domain * h , struct device_node * np )
2007-08-28 18:47:55 +10:00
{
/* Match all */
return 1 ;
}
2012-01-26 12:24:34 -07:00
static const struct irq_domain_ops beatic_pic_host_ops = {
2007-02-02 16:37:30 +09:00
. map = beatic_pic_host_map ,
. unmap = beatic_pic_host_unmap ,
. xlate = beatic_pic_host_xlate ,
2007-08-28 18:47:55 +10:00
. match = beatic_pic_host_match ,
2007-02-02 16:37:30 +09:00
} ;
/*
* Get an IRQ number
* Note : returns VIRQ
*/
static inline unsigned int beatic_get_irq_plug ( void )
{
int i ;
uint64_t pending [ 4 ] , ub ;
for ( i = 0 ; i < MAX_IRQS ; i + = 256 ) {
beat_detect_pending_interrupts ( i , pending ) ;
__asm__ ( " cntlzd %0,%1 " : " =r " ( ub ) :
" r " ( pending [ 0 ] & beatic_irq_mask_enable [ i / 64 + 0 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ i / 64 + 0 ] ) ) ;
2007-02-02 16:37:30 +09:00
if ( ub ! = 64 )
return i + ub + 0 ;
__asm__ ( " cntlzd %0,%1 " : " =r " ( ub ) :
" r " ( pending [ 1 ] & beatic_irq_mask_enable [ i / 64 + 1 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ i / 64 + 1 ] ) ) ;
2007-02-02 16:37:30 +09:00
if ( ub ! = 64 )
return i + ub + 64 ;
__asm__ ( " cntlzd %0,%1 " : " =r " ( ub ) :
" r " ( pending [ 2 ] & beatic_irq_mask_enable [ i / 64 + 2 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ i / 64 + 2 ] ) ) ;
2007-02-02 16:37:30 +09:00
if ( ub ! = 64 )
return i + ub + 128 ;
__asm__ ( " cntlzd %0,%1 " : " =r " ( ub ) :
" r " ( pending [ 3 ] & beatic_irq_mask_enable [ i / 64 + 3 ]
2008-03-14 23:19:34 +11:00
& beatic_irq_mask_ack [ i / 64 + 3 ] ) ) ;
2007-02-02 16:37:30 +09:00
if ( ub ! = 64 )
return i + ub + 192 ;
}
return NO_IRQ ;
}
unsigned int beatic_get_irq ( void )
{
unsigned int ret ;
ret = beatic_get_irq_plug ( ) ;
if ( ret ! = NO_IRQ )
2011-03-07 13:59:28 +00:00
beatic_ack_irq ( irq_get_irq_data ( ret ) ) ;
2007-02-02 16:37:30 +09:00
return ret ;
}
/*
*/
void __init beatic_init_IRQ ( void )
{
int i ;
memset ( beatic_irq_mask_enable , 0 , sizeof ( beatic_irq_mask_enable ) ) ;
memset ( beatic_irq_mask_ack , 255 , sizeof ( beatic_irq_mask_ack ) ) ;
for ( i = 0 ; i < MAX_IRQS ; i + = 256 )
beat_set_interrupt_mask ( i , 0L , 0L , 0L , 0L ) ;
/* Set out get_irq function */
ppc_md . get_irq = beatic_get_irq ;
/* Allocate an irq host */
2012-02-15 15:06:08 -07:00
beatic_host = irq_domain_add_nomap ( NULL , 0 , & beatic_pic_host_ops , NULL ) ;
2007-02-02 16:37:30 +09:00
BUG_ON ( beatic_host = = NULL ) ;
irq_set_default_host ( beatic_host ) ;
}
void beatic_deinit_IRQ ( void )
{
int i ;
for ( i = 1 ; i < NR_IRQS ; i + + )
beat_destruct_irq_plug ( i ) ;
}