2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* arch / powerpc / sysdev / ipic . c
2005-04-17 02:20:36 +04:00
*
* IPIC routines implementations .
*
* Copyright 2005 Freescale Semiconductor , Inc .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/reboot.h>
# include <linux/slab.h>
# include <linux/stddef.h>
# include <linux/sched.h>
# include <linux/signal.h>
2011-04-26 21:14:57 +04:00
# include <linux/syscore_ops.h>
2006-08-25 20:59:07 +04:00
# include <linux/device.h>
# include <linux/spinlock.h>
2007-10-09 21:37:13 +04:00
# include <linux/fsl_devices.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <asm/io.h>
2006-08-25 20:59:07 +04:00
# include <asm/prom.h>
2005-04-17 02:20:36 +04:00
# include <asm/ipic.h>
# include "ipic.h"
static struct ipic * primary_ipic ;
2007-12-04 14:01:40 +03:00
static struct irq_chip ipic_level_irq_chip , ipic_edge_irq_chip ;
2010-02-18 05:23:14 +03:00
static DEFINE_RAW_SPINLOCK ( ipic_lock ) ;
2005-04-17 02:20:36 +04:00
static struct ipic_info ipic_info [ ] = {
2007-10-19 15:38:43 +04:00
[ 1 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 16 ,
. prio_mask = 0 ,
} ,
[ 2 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 17 ,
. prio_mask = 1 ,
} ,
2008-01-18 03:05:32 +03:00
[ 3 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 18 ,
. prio_mask = 2 ,
} ,
2007-10-19 15:38:43 +04:00
[ 4 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 19 ,
. prio_mask = 3 ,
} ,
2008-01-18 03:05:32 +03:00
[ 5 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 20 ,
. prio_mask = 4 ,
} ,
[ 6 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 21 ,
. prio_mask = 5 ,
} ,
[ 7 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 22 ,
. prio_mask = 6 ,
} ,
[ 8 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_C ,
. force = IPIC_SIFCR_H ,
. bit = 23 ,
. prio_mask = 7 ,
} ,
2005-04-17 02:20:36 +04:00
[ 9 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 24 ,
. prio_mask = 0 ,
} ,
[ 10 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 25 ,
. prio_mask = 1 ,
} ,
[ 11 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 26 ,
. prio_mask = 2 ,
} ,
2007-10-19 15:38:43 +04:00
[ 12 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 27 ,
. prio_mask = 3 ,
} ,
[ 13 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 28 ,
. prio_mask = 4 ,
} ,
2005-04-17 02:20:36 +04:00
[ 14 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 29 ,
. prio_mask = 5 ,
} ,
[ 15 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 30 ,
. prio_mask = 6 ,
} ,
[ 16 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_D ,
. force = IPIC_SIFCR_H ,
. bit = 31 ,
. prio_mask = 7 ,
} ,
[ 17 ] = {
2007-12-04 14:01:40 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SEFCR ,
. bit = 1 ,
. prio_mask = 5 ,
} ,
[ 18 ] = {
2007-12-04 14:01:40 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SEFCR ,
. bit = 2 ,
. prio_mask = 6 ,
} ,
[ 19 ] = {
2007-12-04 14:01:40 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SEFCR ,
. bit = 3 ,
. prio_mask = 7 ,
} ,
[ 20 ] = {
2007-12-04 14:01:40 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SEFCR ,
. bit = 4 ,
. prio_mask = 4 ,
} ,
[ 21 ] = {
2007-12-04 14:01:40 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SEFCR ,
. bit = 5 ,
. prio_mask = 5 ,
} ,
[ 22 ] = {
2007-12-04 14:01:40 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SEFCR ,
. bit = 6 ,
. prio_mask = 6 ,
} ,
[ 23 ] = {
2007-12-04 14:01:40 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SEFCR ,
. bit = 7 ,
. prio_mask = 7 ,
} ,
[ 32 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 0 ,
. prio_mask = 0 ,
} ,
[ 33 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 1 ,
. prio_mask = 1 ,
} ,
[ 34 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 2 ,
. prio_mask = 2 ,
} ,
[ 35 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 3 ,
. prio_mask = 3 ,
} ,
[ 36 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 4 ,
. prio_mask = 4 ,
} ,
[ 37 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 5 ,
. prio_mask = 5 ,
} ,
[ 38 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 6 ,
. prio_mask = 6 ,
} ,
[ 39 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_A ,
. force = IPIC_SIFCR_H ,
. bit = 7 ,
. prio_mask = 7 ,
} ,
2008-01-18 03:05:32 +03:00
[ 40 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 8 ,
. prio_mask = 0 ,
} ,
[ 41 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 9 ,
. prio_mask = 1 ,
} ,
2007-10-19 15:38:43 +04:00
[ 42 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 10 ,
. prio_mask = 2 ,
} ,
2008-01-18 03:05:32 +03:00
[ 43 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 11 ,
. prio_mask = 3 ,
} ,
2007-10-19 15:38:43 +04:00
[ 44 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 12 ,
. prio_mask = 4 ,
} ,
[ 45 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 13 ,
. prio_mask = 5 ,
} ,
[ 46 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 14 ,
. prio_mask = 6 ,
} ,
[ 47 ] = {
. mask = IPIC_SIMSR_H ,
. prio = IPIC_SIPRR_B ,
. force = IPIC_SIFCR_H ,
. bit = 15 ,
. prio_mask = 7 ,
} ,
2005-04-17 02:20:36 +04:00
[ 48 ] = {
2017-06-25 05:39:05 +03:00
. ack = IPIC_SEPNR ,
2005-04-17 02:20:36 +04:00
. mask = IPIC_SEMSR ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SEFCR ,
. bit = 0 ,
. prio_mask = 4 ,
} ,
[ 64 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SIFCR_L ,
. bit = 0 ,
. prio_mask = 0 ,
} ,
[ 65 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SIFCR_L ,
. bit = 1 ,
. prio_mask = 1 ,
} ,
[ 66 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SIFCR_L ,
. bit = 2 ,
. prio_mask = 2 ,
} ,
[ 67 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_A ,
. force = IPIC_SIFCR_L ,
. bit = 3 ,
. prio_mask = 3 ,
} ,
[ 68 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SIFCR_L ,
. bit = 4 ,
. prio_mask = 0 ,
} ,
[ 69 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SIFCR_L ,
. bit = 5 ,
. prio_mask = 1 ,
} ,
[ 70 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SIFCR_L ,
. bit = 6 ,
. prio_mask = 2 ,
} ,
[ 71 ] = {
. mask = IPIC_SIMSR_L ,
. prio = IPIC_SMPRR_B ,
. force = IPIC_SIFCR_L ,
. bit = 7 ,
. prio_mask = 3 ,
} ,
[ 72 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 8 ,
} ,
[ 73 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 9 ,
} ,
[ 74 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 10 ,
} ,
[ 75 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 11 ,
} ,
[ 76 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 12 ,
} ,
[ 77 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 13 ,
} ,
[ 78 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 14 ,
} ,
[ 79 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 15 ,
} ,
[ 80 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 16 ,
} ,
2007-10-19 15:38:43 +04:00
[ 81 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 17 ,
} ,
[ 82 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 18 ,
} ,
2008-01-18 03:05:32 +03:00
[ 83 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 19 ,
} ,
2005-04-17 02:20:36 +04:00
[ 84 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 20 ,
} ,
[ 85 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 21 ,
} ,
2007-10-19 15:38:43 +04:00
[ 86 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 22 ,
} ,
[ 87 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 23 ,
} ,
[ 88 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 24 ,
} ,
[ 89 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 25 ,
} ,
2005-04-17 02:20:36 +04:00
[ 90 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 26 ,
} ,
[ 91 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 27 ,
} ,
2008-01-25 05:46:50 +03:00
[ 94 ] = {
. mask = IPIC_SIMSR_L ,
. prio = 0 ,
. force = IPIC_SIFCR_L ,
. bit = 30 ,
} ,
2005-04-17 02:20:36 +04:00
} ;
static inline u32 ipic_read ( volatile u32 __iomem * base , unsigned int reg )
{
return in_be32 ( base + ( reg > > 2 ) ) ;
}
static inline void ipic_write ( volatile u32 __iomem * base , unsigned int reg , u32 value )
{
out_be32 ( base + ( reg > > 2 ) , value ) ;
}
2006-08-25 20:59:07 +04:00
static inline struct ipic * ipic_from_irq ( unsigned int virq )
2005-04-17 02:20:36 +04:00
{
return primary_ipic ;
}
2011-03-07 16:59:58 +03:00
static void ipic_unmask_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-07 16:59:58 +03:00
struct ipic * ipic = ipic_from_irq ( d - > irq ) ;
2011-05-04 09:02:15 +04:00
unsigned int src = irqd_to_hwirq ( d ) ;
2006-08-25 20:59:07 +04:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
u32 temp ;
2010-02-18 05:23:14 +03:00
raw_spin_lock_irqsave ( & ipic_lock , flags ) ;
2006-08-25 20:59:07 +04:00
2005-04-17 02:20:36 +04:00
temp = ipic_read ( ipic - > regs , ipic_info [ src ] . mask ) ;
temp | = ( 1 < < ( 31 - ipic_info [ src ] . bit ) ) ;
ipic_write ( ipic - > regs , ipic_info [ src ] . mask , temp ) ;
2006-08-25 20:59:07 +04:00
2010-02-18 05:23:14 +03:00
raw_spin_unlock_irqrestore ( & ipic_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-07 16:59:58 +03:00
static void ipic_mask_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-07 16:59:58 +03:00
struct ipic * ipic = ipic_from_irq ( d - > irq ) ;
2011-05-04 09:02:15 +04:00
unsigned int src = irqd_to_hwirq ( d ) ;
2006-08-25 20:59:07 +04:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
u32 temp ;
2010-02-18 05:23:14 +03:00
raw_spin_lock_irqsave ( & ipic_lock , flags ) ;
2006-08-25 20:59:07 +04:00
2005-04-17 02:20:36 +04:00
temp = ipic_read ( ipic - > regs , ipic_info [ src ] . mask ) ;
temp & = ~ ( 1 < < ( 31 - ipic_info [ src ] . bit ) ) ;
ipic_write ( ipic - > regs , ipic_info [ src ] . mask , temp ) ;
2006-08-25 20:59:07 +04:00
2007-12-04 14:01:40 +03:00
/* mb() can't guarantee that masking is finished. But it does finish
* for nearly all cases . */
mb ( ) ;
2010-02-18 05:23:14 +03:00
raw_spin_unlock_irqrestore ( & ipic_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-07 16:59:58 +03:00
static void ipic_ack_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-07 16:59:58 +03:00
struct ipic * ipic = ipic_from_irq ( d - > irq ) ;
2011-05-04 09:02:15 +04:00
unsigned int src = irqd_to_hwirq ( d ) ;
2006-08-25 20:59:07 +04:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
u32 temp ;
2010-02-18 05:23:14 +03:00
raw_spin_lock_irqsave ( & ipic_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2009-02-18 08:47:42 +03:00
temp = 1 < < ( 31 - ipic_info [ src ] . bit ) ;
2007-12-04 14:01:40 +03:00
ipic_write ( ipic - > regs , ipic_info [ src ] . ack , temp ) ;
/* mb() can't guarantee that ack is finished. But it does finish
* for nearly all cases . */
mb ( ) ;
2006-08-25 20:59:07 +04:00
2010-02-18 05:23:14 +03:00
raw_spin_unlock_irqrestore ( & ipic_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-07 16:59:58 +03:00
static void ipic_mask_irq_and_ack ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2011-03-07 16:59:58 +03:00
struct ipic * ipic = ipic_from_irq ( d - > irq ) ;
2011-05-04 09:02:15 +04:00
unsigned int src = irqd_to_hwirq ( d ) ;
2006-08-25 20:59:07 +04:00
unsigned long flags ;
u32 temp ;
2010-02-18 05:23:14 +03:00
raw_spin_lock_irqsave ( & ipic_lock , flags ) ;
2006-08-25 20:59:07 +04:00
temp = ipic_read ( ipic - > regs , ipic_info [ src ] . mask ) ;
temp & = ~ ( 1 < < ( 31 - ipic_info [ src ] . bit ) ) ;
ipic_write ( ipic - > regs , ipic_info [ src ] . mask , temp ) ;
2009-02-18 08:47:42 +03:00
temp = 1 < < ( 31 - ipic_info [ src ] . bit ) ;
2007-12-04 14:01:40 +03:00
ipic_write ( ipic - > regs , ipic_info [ src ] . ack , temp ) ;
/* mb() can't guarantee that ack is finished. But it does finish
* for nearly all cases . */
mb ( ) ;
2006-08-25 20:59:07 +04:00
2010-02-18 05:23:14 +03:00
raw_spin_unlock_irqrestore ( & ipic_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-07 16:59:58 +03:00
static int ipic_set_irq_type ( struct irq_data * d , unsigned int flow_type )
2006-08-25 20:59:07 +04:00
{
2011-03-07 16:59:58 +03:00
struct ipic * ipic = ipic_from_irq ( d - > irq ) ;
2011-05-04 09:02:15 +04:00
unsigned int src = irqd_to_hwirq ( d ) ;
2006-08-25 20:59:07 +04:00
unsigned int vold , vnew , edibit ;
if ( flow_type = = IRQ_TYPE_NONE )
flow_type = IRQ_TYPE_LEVEL_LOW ;
/* ipic supports only low assertion and high-to-low change senses
*/
if ( ! ( flow_type & ( IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING ) ) ) {
printk ( KERN_ERR " ipic: sense type 0x%x not supported \n " ,
flow_type ) ;
return - EINVAL ;
}
2007-12-04 14:01:40 +03:00
/* ipic supports only edge mode on external interrupts */
if ( ( flow_type & IRQ_TYPE_EDGE_FALLING ) & & ! ipic_info [ src ] . ack ) {
printk ( KERN_ERR " ipic: edge sense not supported on internal "
" interrupts \n " ) ;
return - EINVAL ;
2011-03-25 18:16:30 +03:00
2007-12-04 14:01:40 +03:00
}
2006-08-25 20:59:07 +04:00
2011-03-25 18:16:30 +03:00
irqd_set_trigger_type ( d , flow_type ) ;
2006-08-25 20:59:07 +04:00
if ( flow_type & IRQ_TYPE_LEVEL_LOW ) {
2015-06-23 16:52:34 +03:00
irq_set_handler_locked ( d , handle_level_irq ) ;
2011-03-25 18:16:30 +03:00
d - > chip = & ipic_level_irq_chip ;
2006-08-25 20:59:07 +04:00
} else {
2015-06-23 16:52:34 +03:00
irq_set_handler_locked ( d , handle_edge_irq ) ;
2011-03-25 18:16:30 +03:00
d - > chip = & ipic_edge_irq_chip ;
2006-08-25 20:59:07 +04:00
}
/* only EXT IRQ senses are programmable on ipic
* internal IRQ senses are LEVEL_LOW
*/
if ( src = = IPIC_IRQ_EXT0 )
edibit = 15 ;
else
if ( src > = IPIC_IRQ_EXT1 & & src < = IPIC_IRQ_EXT7 )
edibit = ( 14 - ( src - IPIC_IRQ_EXT1 ) ) ;
else
return ( flow_type & IRQ_TYPE_LEVEL_LOW ) ? 0 : - EINVAL ;
vold = ipic_read ( ipic - > regs , IPIC_SECNR ) ;
if ( ( flow_type & IRQ_TYPE_SENSE_MASK ) = = IRQ_TYPE_EDGE_FALLING ) {
vnew = vold | ( 1 < < edibit ) ;
} else {
vnew = vold & ~ ( 1 < < edibit ) ;
}
if ( vold ! = vnew )
ipic_write ( ipic - > regs , IPIC_SECNR , vnew ) ;
2011-03-25 18:16:30 +03:00
return IRQ_SET_MASK_OK_NOCOPY ;
2006-08-25 20:59:07 +04:00
}
2007-12-04 14:01:40 +03:00
/* level interrupts and edge interrupts have different ack operations */
static struct irq_chip ipic_level_irq_chip = {
2010-01-31 23:33:41 +03:00
. name = " IPIC " ,
2011-03-07 16:59:58 +03:00
. irq_unmask = ipic_unmask_irq ,
. irq_mask = ipic_mask_irq ,
. irq_mask_ack = ipic_mask_irq ,
. irq_set_type = ipic_set_irq_type ,
2007-12-04 14:01:40 +03:00
} ;
static struct irq_chip ipic_edge_irq_chip = {
2010-01-31 23:33:41 +03:00
. name = " IPIC " ,
2011-03-07 16:59:58 +03:00
. irq_unmask = ipic_unmask_irq ,
. irq_mask = ipic_mask_irq ,
. irq_mask_ack = ipic_mask_irq_and_ack ,
. irq_ack = ipic_ack_irq ,
. irq_set_type = ipic_set_irq_type ,
2006-08-25 20:59:07 +04:00
} ;
genirq/irqdomain: Allow irq domain aliasing
It is not uncommon (at least with the ARM stuff) to have a piece
of hardware that implements different flavours of "interrupts".
A typical example of this is the GICv3 ITS, which implements
standard PCI/MSI support, but also some form of "generic MSI".
So far, the PCI/MSI domain is registered using the ITS device_node,
so that irq_find_host can return it. On the contrary, the raw MSI
domain is not registered with an device_node, making it impossible
to be looked up by another subsystem (obviously, using the same
device_node twice would only result in confusion, as it is not
defined which one irq_find_host would return).
A solution to this is to "type" domains that may be aliasing, and
to be able to lookup an device_node that matches a given type.
For this, we introduce irq_find_matching_host() as a superset
of irq_find_host:
struct irq_domain *irq_find_matching_host(struct device_node *node,
enum irq_domain_bus_token bus_token);
where bus_token is the "type" we want to match the domain against
(so far, only DOMAIN_BUS_ANY is defined). This result in some
moderately invasive changes on the PPC side (which is the only
user of the .match method).
This has otherwise no functionnal change.
Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: <linux-arm-kernel@lists.infradead.org>
Cc: Yijing Wang <wangyijing@huawei.com>
Cc: Ma Jun <majun258@huawei.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Link: http://lkml.kernel.org/r/1438091186-10244-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2015-07-28 16:46:08 +03:00
static int ipic_host_match ( struct irq_domain * h , struct device_node * node ,
enum irq_domain_bus_token bus_token )
2006-08-25 20:59:07 +04:00
{
/* Exact match, unless ipic node is NULL */
2015-10-13 14:51:29 +03:00
struct device_node * of_node = irq_domain_get_of_node ( h ) ;
return of_node = = NULL | | of_node = = node ;
2006-08-25 20:59:07 +04:00
}
2012-02-15 01:06:50 +04:00
static int ipic_host_map ( struct irq_domain * h , unsigned int virq ,
2006-08-25 20:59:07 +04:00
irq_hw_number_t hw )
{
struct ipic * ipic = h - > host_data ;
2011-03-25 18:45:20 +03:00
irq_set_chip_data ( virq , ipic ) ;
irq_set_chip_and_handler ( virq , & ipic_level_irq_chip , handle_level_irq ) ;
2006-08-25 20:59:07 +04:00
/* Set default irq type */
2011-03-25 18:45:20 +03:00
irq_set_irq_type ( virq , IRQ_TYPE_NONE ) ;
2006-08-25 20:59:07 +04:00
return 0 ;
}
2015-04-27 15:48:47 +03:00
static const struct irq_domain_ops ipic_host_ops = {
2006-08-25 20:59:07 +04:00
. match = ipic_host_match ,
. map = ipic_host_map ,
2012-01-25 04:09:13 +04:00
. xlate = irq_domain_xlate_onetwocell ,
2005-04-17 02:20:36 +04:00
} ;
2007-01-26 10:45:32 +03:00
struct ipic * __init ipic_init ( struct device_node * node , unsigned int flags )
2005-04-17 02:20:36 +04:00
{
2006-08-25 20:59:07 +04:00
struct ipic * ipic ;
struct resource res ;
u32 temp = 0 , ret ;
2008-05-26 06:12:30 +04:00
ret = of_address_to_resource ( node , 0 , & res ) ;
if ( ret )
return NULL ;
2009-07-01 14:59:57 +04:00
ipic = kzalloc ( sizeof ( * ipic ) , GFP_KERNEL ) ;
2006-08-25 20:59:07 +04:00
if ( ipic = = NULL )
2007-01-26 10:45:32 +03:00
return NULL ;
2006-08-25 20:59:07 +04:00
2012-02-15 01:06:54 +04:00
ipic - > irqhost = irq_domain_add_linear ( node , NR_IPIC_INTS ,
& ipic_host_ops , ipic ) ;
2009-08-02 12:44:53 +04:00
if ( ipic - > irqhost = = NULL ) {
kfree ( ipic ) ;
2007-01-26 10:45:32 +03:00
return NULL ;
2009-08-02 12:44:53 +04:00
}
2006-08-25 20:59:07 +04:00
2011-06-09 20:13:32 +04:00
ipic - > regs = ioremap ( res . start , resource_size ( & res ) ) ;
2005-04-17 02:20:36 +04:00
2006-08-25 20:59:07 +04:00
/* init hw */
ipic_write ( ipic - > regs , IPIC_SICNR , 0x0 ) ;
2005-04-17 02:20:36 +04:00
/* default priority scheme is grouped. If spread mode is required
* configure SICFR accordingly */
if ( flags & IPIC_SPREADMODE_GRP_A )
temp | = SICFR_IPSA ;
2007-10-19 15:38:43 +04:00
if ( flags & IPIC_SPREADMODE_GRP_B )
temp | = SICFR_IPSB ;
if ( flags & IPIC_SPREADMODE_GRP_C )
temp | = SICFR_IPSC ;
2005-04-17 02:20:36 +04:00
if ( flags & IPIC_SPREADMODE_GRP_D )
temp | = SICFR_IPSD ;
if ( flags & IPIC_SPREADMODE_MIX_A )
temp | = SICFR_MPSA ;
if ( flags & IPIC_SPREADMODE_MIX_B )
temp | = SICFR_MPSB ;
2007-10-19 15:38:43 +04:00
ipic_write ( ipic - > regs , IPIC_SICFR , temp ) ;
2005-04-17 02:20:36 +04:00
/* handle MCP route */
temp = 0 ;
if ( flags & IPIC_DISABLE_MCP_OUT )
temp = SERCR_MCPR ;
2006-08-25 20:59:07 +04:00
ipic_write ( ipic - > regs , IPIC_SERCR , temp ) ;
2005-04-17 02:20:36 +04:00
/* handle routing of IRQ0 to MCP */
2006-08-25 20:59:07 +04:00
temp = ipic_read ( ipic - > regs , IPIC_SEMSR ) ;
2005-04-17 02:20:36 +04:00
if ( flags & IPIC_IRQ0_MCP )
temp | = SEMSR_SIRQ0 ;
else
temp & = ~ SEMSR_SIRQ0 ;
2006-08-25 20:59:07 +04:00
ipic_write ( ipic - > regs , IPIC_SEMSR , temp ) ;
2005-04-17 02:20:36 +04:00
2006-08-25 20:59:07 +04:00
primary_ipic = ipic ;
irq_set_default_host ( primary_ipic - > irqhost ) ;
2005-04-17 02:20:36 +04:00
2009-08-05 23:41:12 +04:00
ipic_write ( ipic - > regs , IPIC_SIMSR_H , 0 ) ;
ipic_write ( ipic - > regs , IPIC_SIMSR_L , 0 ) ;
2006-08-25 20:59:07 +04:00
printk ( " IPIC (%d IRQ sources) at %p \n " , NR_IPIC_INTS ,
primary_ipic - > regs ) ;
2007-01-26 10:45:32 +03:00
return ipic ;
2005-04-17 02:20:36 +04:00
}
void ipic_set_default_priority ( void )
{
2007-10-19 15:38:43 +04:00
ipic_write ( primary_ipic - > regs , IPIC_SIPRR_A , IPIC_PRIORITY_DEFAULT ) ;
ipic_write ( primary_ipic - > regs , IPIC_SIPRR_B , IPIC_PRIORITY_DEFAULT ) ;
ipic_write ( primary_ipic - > regs , IPIC_SIPRR_C , IPIC_PRIORITY_DEFAULT ) ;
ipic_write ( primary_ipic - > regs , IPIC_SIPRR_D , IPIC_PRIORITY_DEFAULT ) ;
ipic_write ( primary_ipic - > regs , IPIC_SMPRR_A , IPIC_PRIORITY_DEFAULT ) ;
ipic_write ( primary_ipic - > regs , IPIC_SMPRR_B , IPIC_PRIORITY_DEFAULT ) ;
2005-04-17 02:20:36 +04:00
}
u32 ipic_get_mcp_status ( void )
{
2018-08-27 11:27:27 +03:00
return primary_ipic ? ipic_read ( primary_ipic - > regs , IPIC_SERSR ) : 0 ;
2005-04-17 02:20:36 +04:00
}
void ipic_clear_mcp_status ( u32 mask )
{
2017-10-18 12:16:47 +03:00
ipic_write ( primary_ipic - > regs , IPIC_SERSR , mask ) ;
2005-04-17 02:20:36 +04:00
}
2016-09-06 14:53:24 +03:00
/* Return an interrupt vector or 0 if no interrupt is pending. */
2006-10-07 16:08:26 +04:00
unsigned int ipic_get_irq ( void )
2005-04-17 02:20:36 +04:00
{
int irq ;
2006-08-25 20:59:07 +04:00
BUG_ON ( primary_ipic = = NULL ) ;
# define IPIC_SIVCR_VECTOR_MASK 0x7f
irq = ipic_read ( primary_ipic - > regs , IPIC_SIVCR ) & IPIC_SIVCR_VECTOR_MASK ;
2005-04-17 02:20:36 +04:00
if ( irq = = 0 ) /* 0 --> no irq is pending */
2016-09-06 14:53:24 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
2006-08-25 20:59:07 +04:00
return irq_linear_revmap ( primary_ipic - > irqhost , irq ) ;
2005-04-17 02:20:36 +04:00
}
2009-02-06 03:10:27 +03:00
# ifdef CONFIG_SUSPEND
2007-10-09 21:37:13 +04:00
static struct {
u32 sicfr ;
u32 siprr [ 2 ] ;
u32 simsr [ 2 ] ;
u32 sicnr ;
u32 smprr [ 2 ] ;
u32 semsr ;
u32 secnr ;
u32 sermr ;
u32 sercr ;
} ipic_saved_state ;
2011-04-26 21:14:57 +04:00
static int ipic_suspend ( void )
2007-10-09 21:37:13 +04:00
{
struct ipic * ipic = primary_ipic ;
ipic_saved_state . sicfr = ipic_read ( ipic - > regs , IPIC_SICFR ) ;
ipic_saved_state . siprr [ 0 ] = ipic_read ( ipic - > regs , IPIC_SIPRR_A ) ;
ipic_saved_state . siprr [ 1 ] = ipic_read ( ipic - > regs , IPIC_SIPRR_D ) ;
ipic_saved_state . simsr [ 0 ] = ipic_read ( ipic - > regs , IPIC_SIMSR_H ) ;
ipic_saved_state . simsr [ 1 ] = ipic_read ( ipic - > regs , IPIC_SIMSR_L ) ;
ipic_saved_state . sicnr = ipic_read ( ipic - > regs , IPIC_SICNR ) ;
ipic_saved_state . smprr [ 0 ] = ipic_read ( ipic - > regs , IPIC_SMPRR_A ) ;
ipic_saved_state . smprr [ 1 ] = ipic_read ( ipic - > regs , IPIC_SMPRR_B ) ;
ipic_saved_state . semsr = ipic_read ( ipic - > regs , IPIC_SEMSR ) ;
ipic_saved_state . secnr = ipic_read ( ipic - > regs , IPIC_SECNR ) ;
ipic_saved_state . sermr = ipic_read ( ipic - > regs , IPIC_SERMR ) ;
ipic_saved_state . sercr = ipic_read ( ipic - > regs , IPIC_SERCR ) ;
if ( fsl_deep_sleep ( ) ) {
/* In deep sleep, make sure there can be no
* pending interrupts , as this can cause
* problems on 831 x .
*/
ipic_write ( ipic - > regs , IPIC_SIMSR_H , 0 ) ;
ipic_write ( ipic - > regs , IPIC_SIMSR_L , 0 ) ;
ipic_write ( ipic - > regs , IPIC_SEMSR , 0 ) ;
ipic_write ( ipic - > regs , IPIC_SERMR , 0 ) ;
}
return 0 ;
}
2011-04-26 21:14:57 +04:00
static void ipic_resume ( void )
2007-10-09 21:37:13 +04:00
{
struct ipic * ipic = primary_ipic ;
ipic_write ( ipic - > regs , IPIC_SICFR , ipic_saved_state . sicfr ) ;
ipic_write ( ipic - > regs , IPIC_SIPRR_A , ipic_saved_state . siprr [ 0 ] ) ;
ipic_write ( ipic - > regs , IPIC_SIPRR_D , ipic_saved_state . siprr [ 1 ] ) ;
ipic_write ( ipic - > regs , IPIC_SIMSR_H , ipic_saved_state . simsr [ 0 ] ) ;
ipic_write ( ipic - > regs , IPIC_SIMSR_L , ipic_saved_state . simsr [ 1 ] ) ;
ipic_write ( ipic - > regs , IPIC_SICNR , ipic_saved_state . sicnr ) ;
ipic_write ( ipic - > regs , IPIC_SMPRR_A , ipic_saved_state . smprr [ 0 ] ) ;
ipic_write ( ipic - > regs , IPIC_SMPRR_B , ipic_saved_state . smprr [ 1 ] ) ;
ipic_write ( ipic - > regs , IPIC_SEMSR , ipic_saved_state . semsr ) ;
ipic_write ( ipic - > regs , IPIC_SECNR , ipic_saved_state . secnr ) ;
ipic_write ( ipic - > regs , IPIC_SERMR , ipic_saved_state . sermr ) ;
ipic_write ( ipic - > regs , IPIC_SERCR , ipic_saved_state . sercr ) ;
}
# else
# define ipic_suspend NULL
# define ipic_resume NULL
# endif
2011-04-26 21:14:57 +04:00
static struct syscore_ops ipic_syscore_ops = {
2007-10-09 21:37:13 +04:00
. suspend = ipic_suspend ,
. resume = ipic_resume ,
2005-04-17 02:20:36 +04:00
} ;
2011-04-26 21:14:57 +04:00
static int __init init_ipic_syscore ( void )
2005-04-17 02:20:36 +04:00
{
2008-03-17 22:53:05 +03:00
if ( ! primary_ipic | | ! primary_ipic - > regs )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2011-04-26 21:14:57 +04:00
printk ( KERN_DEBUG " Registering ipic system core operations \n " ) ;
register_syscore_ops ( & ipic_syscore_ops ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-04-26 21:14:57 +04:00
subsys_initcall ( init_ipic_syscore ) ;