2010-10-05 17:10:30 +04:00
/*
* IRQ chip definitions for INTC IRQs .
*
* Copyright ( C ) 2007 , 2008 Magnus Damm
2012-01-24 12:41:55 +04:00
* Copyright ( C ) 2009 - 2012 Paul Mundt
2010-10-05 17:10:30 +04:00
*
* 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/cpumask.h>
2012-01-24 12:41:55 +04:00
# include <linux/bsearch.h>
2010-10-05 17:10:30 +04:00
# include <linux/io.h>
# include "internals.h"
2010-10-27 10:42:10 +04:00
void _intc_enable ( struct irq_data * data , unsigned long handle )
2010-10-05 17:10:30 +04:00
{
2010-10-27 10:42:10 +04:00
unsigned int irq = data - > irq ;
2010-10-05 17:10:30 +04:00
struct intc_desc_int * d = get_intc_desc ( irq ) ;
unsigned long addr ;
unsigned int cpu ;
for ( cpu = 0 ; cpu < SMP_NR ( d , _INTC_ADDR_E ( handle ) ) ; cpu + + ) {
# ifdef CONFIG_SMP
2015-07-13 23:51:23 +03:00
if ( ! cpumask_test_cpu ( cpu , irq_data_get_affinity_mask ( data ) ) )
2010-10-05 17:10:30 +04:00
continue ;
# endif
addr = INTC_REG ( d , _INTC_ADDR_E ( handle ) , cpu ) ;
intc_enable_fns [ _INTC_MODE ( handle ) ] ( addr , handle , intc_reg_fns \
[ _INTC_FN ( handle ) ] , irq ) ;
}
intc_balancing_enable ( irq ) ;
}
2010-10-27 10:42:10 +04:00
static void intc_enable ( struct irq_data * data )
2010-10-05 17:10:30 +04:00
{
2010-10-27 10:42:10 +04:00
_intc_enable ( data , ( unsigned long ) irq_data_get_irq_chip_data ( data ) ) ;
2010-10-05 17:10:30 +04:00
}
2010-10-27 10:42:10 +04:00
static void intc_disable ( struct irq_data * data )
2010-10-05 17:10:30 +04:00
{
2010-10-27 10:42:10 +04:00
unsigned int irq = data - > irq ;
2010-10-05 17:10:30 +04:00
struct intc_desc_int * d = get_intc_desc ( irq ) ;
2010-10-27 10:42:10 +04:00
unsigned long handle = ( unsigned long ) irq_data_get_irq_chip_data ( data ) ;
2010-10-05 17:10:30 +04:00
unsigned long addr ;
unsigned int cpu ;
intc_balancing_disable ( irq ) ;
for ( cpu = 0 ; cpu < SMP_NR ( d , _INTC_ADDR_D ( handle ) ) ; cpu + + ) {
# ifdef CONFIG_SMP
2015-07-13 23:51:23 +03:00
if ( ! cpumask_test_cpu ( cpu , irq_data_get_affinity_mask ( data ) ) )
2010-10-05 17:10:30 +04:00
continue ;
# endif
addr = INTC_REG ( d , _INTC_ADDR_D ( handle ) , cpu ) ;
intc_disable_fns [ _INTC_MODE ( handle ) ] ( addr , handle , intc_reg_fns \
[ _INTC_FN ( handle ) ] , irq ) ;
}
}
# ifdef CONFIG_SMP
/*
* This is held with the irq desc lock held , so we don ' t require any
* additional locking here at the intc desc level . The affinity mask is
* later tested in the enable / disable paths .
*/
2010-10-27 10:42:10 +04:00
static int intc_set_affinity ( struct irq_data * data ,
const struct cpumask * cpumask ,
bool force )
2010-10-05 17:10:30 +04:00
{
if ( ! cpumask_intersects ( cpumask , cpu_online_mask ) )
return - 1 ;
2015-07-13 23:51:23 +03:00
cpumask_copy ( irq_data_get_affinity_mask ( data ) , cpumask ) ;
2010-10-05 17:10:30 +04:00
2012-01-24 11:55:57 +04:00
return IRQ_SET_MASK_OK_NOCOPY ;
2010-10-05 17:10:30 +04:00
}
# endif
2010-10-27 10:42:10 +04:00
static void intc_mask_ack ( struct irq_data * data )
2010-10-05 17:10:30 +04:00
{
2010-10-27 10:42:10 +04:00
unsigned int irq = data - > irq ;
2010-10-05 17:10:30 +04:00
struct intc_desc_int * d = get_intc_desc ( irq ) ;
unsigned long handle = intc_get_ack_handle ( irq ) ;
2012-10-15 09:08:48 +04:00
void __iomem * addr ;
2010-10-05 17:10:30 +04:00
2010-10-27 10:42:10 +04:00
intc_disable ( data ) ;
2010-10-05 17:10:30 +04:00
/* read register and write zero only to the associated bit */
if ( handle ) {
unsigned int value ;
2012-10-15 09:08:48 +04:00
addr = ( void __iomem * ) INTC_REG ( d , _INTC_ADDR_D ( handle ) , 0 ) ;
2010-10-05 17:10:30 +04:00
value = intc_set_field_from_handle ( 0 , 1 , handle ) ;
switch ( _INTC_FN ( handle ) ) {
case REG_FN_MODIFY_BASE + 0 : /* 8bit */
__raw_readb ( addr ) ;
__raw_writeb ( 0xff ^ value , addr ) ;
break ;
case REG_FN_MODIFY_BASE + 1 : /* 16bit */
__raw_readw ( addr ) ;
__raw_writew ( 0xffff ^ value , addr ) ;
break ;
case REG_FN_MODIFY_BASE + 3 : /* 32bit */
__raw_readl ( addr ) ;
__raw_writel ( 0xffffffff ^ value , addr ) ;
break ;
default :
BUG ( ) ;
break ;
}
}
}
static struct intc_handle_int * intc_find_irq ( struct intc_handle_int * hp ,
unsigned int nr_hp ,
unsigned int irq )
{
2012-01-24 12:41:55 +04:00
struct intc_handle_int key ;
2010-10-05 17:10:30 +04:00
2012-01-24 12:41:55 +04:00
key . irq = irq ;
key . handle = 0 ;
2010-10-05 17:10:30 +04:00
2012-01-24 12:41:55 +04:00
return bsearch ( & key , hp , nr_hp , sizeof ( * hp ) , intc_handle_int_cmp ) ;
2010-10-05 17:10:30 +04:00
}
int intc_set_priority ( unsigned int irq , unsigned int prio )
{
struct intc_desc_int * d = get_intc_desc ( irq ) ;
2010-10-27 10:42:10 +04:00
struct irq_data * data = irq_get_irq_data ( irq ) ;
2010-10-05 17:10:30 +04:00
struct intc_handle_int * ihp ;
if ( ! intc_get_prio_level ( irq ) | | prio < = 1 )
return - EINVAL ;
ihp = intc_find_irq ( d - > prio , d - > nr_prio , irq ) ;
if ( ihp ) {
if ( prio > = ( 1 < < _INTC_WIDTH ( ihp - > handle ) ) )
return - EINVAL ;
intc_set_prio_level ( irq , prio ) ;
/*
* only set secondary masking method directly
* primary masking method is using intc_prio_level [ irq ]
* priority level will be set during next enable ( )
*/
if ( _INTC_FN ( ihp - > handle ) ! = REG_FN_ERR )
2010-10-27 10:42:10 +04:00
_intc_enable ( data , ihp - > handle ) ;
2010-10-05 17:10:30 +04:00
}
return 0 ;
}
2011-01-19 11:16:29 +03:00
# define SENSE_VALID_FLAG 0x80
# define VALID(x) (x | SENSE_VALID_FLAG)
2010-10-05 17:10:30 +04:00
static unsigned char intc_irq_sense_table [ IRQ_TYPE_SENSE_MASK + 1 ] = {
[ IRQ_TYPE_EDGE_FALLING ] = VALID ( 0 ) ,
[ IRQ_TYPE_EDGE_RISING ] = VALID ( 1 ) ,
[ IRQ_TYPE_LEVEL_LOW ] = VALID ( 2 ) ,
/* SH7706, SH7707 and SH7709 do not support high level triggered */
# if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
! defined ( CONFIG_CPU_SUBTYPE_SH7707 ) & & \
! defined ( CONFIG_CPU_SUBTYPE_SH7709 )
[ IRQ_TYPE_LEVEL_HIGH ] = VALID ( 3 ) ,
# endif
2011-10-12 11:21:08 +04:00
# if defined(CONFIG_ARM) /* all recent SH-Mobile / R-Mobile ARM support this */
2011-07-15 14:58:55 +04:00
[ IRQ_TYPE_EDGE_BOTH ] = VALID ( 4 ) ,
# endif
2010-10-05 17:10:30 +04:00
} ;
2010-10-27 10:42:10 +04:00
static int intc_set_type ( struct irq_data * data , unsigned int type )
2010-10-05 17:10:30 +04:00
{
2010-10-27 10:42:10 +04:00
unsigned int irq = data - > irq ;
2010-10-05 17:10:30 +04:00
struct intc_desc_int * d = get_intc_desc ( irq ) ;
unsigned char value = intc_irq_sense_table [ type & IRQ_TYPE_SENSE_MASK ] ;
struct intc_handle_int * ihp ;
unsigned long addr ;
if ( ! value )
return - EINVAL ;
2011-10-17 12:59:54 +04:00
value & = ~ SENSE_VALID_FLAG ;
2010-10-05 17:10:30 +04:00
ihp = intc_find_irq ( d - > sense , d - > nr_sense , irq ) ;
if ( ihp ) {
2011-10-17 12:59:54 +04:00
/* PINT has 2-bit sense registers, should fail on EDGE_BOTH */
if ( value > = ( 1 < < _INTC_WIDTH ( ihp - > handle ) ) )
return - EINVAL ;
2010-10-05 17:10:30 +04:00
addr = INTC_REG ( d , _INTC_ADDR_E ( ihp - > handle ) , 0 ) ;
2011-10-17 12:59:54 +04:00
intc_reg_fns [ _INTC_FN ( ihp - > handle ) ] ( addr , ihp - > handle , value ) ;
2010-10-05 17:10:30 +04:00
}
return 0 ;
}
struct irq_chip intc_irq_chip = {
2010-10-27 10:42:10 +04:00
. irq_mask = intc_disable ,
. irq_unmask = intc_enable ,
. irq_mask_ack = intc_mask_ack ,
. irq_enable = intc_enable ,
. irq_disable = intc_disable ,
. irq_set_type = intc_set_type ,
2010-10-05 17:10:30 +04:00
# ifdef CONFIG_SMP
2010-10-27 10:42:10 +04:00
. irq_set_affinity = intc_set_affinity ,
2010-10-05 17:10:30 +04:00
# endif
2012-01-24 09:54:10 +04:00
. flags = IRQCHIP_SKIP_SET_WAKE ,
2010-10-05 17:10:30 +04:00
} ;