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 .
*
* 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 .
*/
# 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/bootmem.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 ] = {
. 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 ) {
2011-03-25 18:16:30 +03:00
__irq_set_handler_locked ( d - > irq , handle_level_irq ) ;
d - > chip = & ipic_level_irq_chip ;
2006-08-25 20:59:07 +04:00
} else {
2011-03-25 18:16:30 +03:00
__irq_set_handler_locked ( d - > irq , handle_edge_irq ) ;
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
} ;
static int ipic_host_match ( struct irq_host * h , struct device_node * node )
{
/* Exact match, unless ipic node is NULL */
2007-08-28 12:47:54 +04:00
return h - > of_node = = NULL | | h - > of_node = = node ;
2006-08-25 20:59:07 +04:00
}
static int ipic_host_map ( struct irq_host * h , unsigned int virq ,
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 ;
}
static int ipic_host_xlate ( struct irq_host * h , struct device_node * ct ,
2009-12-08 05:39:50 +03:00
const u32 * intspec , unsigned int intsize ,
2006-08-25 20:59:07 +04:00
irq_hw_number_t * out_hwirq , unsigned int * out_flags )
{
/* interrupt sense values coming from the device tree equal either
* LEVEL_LOW ( low assertion ) or EDGE_FALLING ( high - to - low change )
*/
* out_hwirq = intspec [ 0 ] ;
if ( intsize > 1 )
* out_flags = intspec [ 1 ] ;
else
* out_flags = IRQ_TYPE_NONE ;
return 0 ;
}
static struct irq_host_ops ipic_host_ops = {
. match = ipic_host_match ,
. map = ipic_host_map ,
. xlate = ipic_host_xlate ,
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
2008-05-26 06:12:32 +04:00
ipic - > irqhost = irq_alloc_host ( node , IRQ_HOST_MAP_LINEAR ,
2006-08-25 20:59:07 +04:00
NR_IPIC_INTS ,
& ipic_host_ops , 0 ) ;
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
ipic - > regs = ioremap ( res . start , res . end - res . start + 1 ) ;
2005-04-17 02:20:36 +04:00
2006-08-25 20:59:07 +04:00
ipic - > irqhost - > host_data = ipic ;
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
}
2006-08-25 20:59:07 +04:00
int ipic_set_priority ( unsigned int virq , unsigned int priority )
2005-04-17 02:20:36 +04:00
{
2006-08-25 20:59:07 +04:00
struct ipic * ipic = ipic_from_irq ( virq ) ;
2011-05-04 09:02:15 +04:00
unsigned int src = virq_to_hw ( virq ) ;
2005-04-17 02:20:36 +04:00
u32 temp ;
if ( priority > 7 )
return - EINVAL ;
if ( src > 127 )
return - EINVAL ;
if ( ipic_info [ src ] . prio = = 0 )
return - EINVAL ;
temp = ipic_read ( ipic - > regs , ipic_info [ src ] . prio ) ;
if ( priority < 4 ) {
temp & = ~ ( 0x7 < < ( 20 + ( 3 - priority ) * 3 ) ) ;
temp | = ipic_info [ src ] . prio_mask < < ( 20 + ( 3 - priority ) * 3 ) ;
} else {
temp & = ~ ( 0x7 < < ( 4 + ( 7 - priority ) * 3 ) ) ;
temp | = ipic_info [ src ] . prio_mask < < ( 4 + ( 7 - priority ) * 3 ) ;
}
ipic_write ( ipic - > regs , ipic_info [ src ] . prio , temp ) ;
return 0 ;
}
2006-08-25 20:59:07 +04:00
void ipic_set_highest_priority ( unsigned int virq )
2005-04-17 02:20:36 +04:00
{
2006-08-25 20:59:07 +04:00
struct ipic * ipic = ipic_from_irq ( virq ) ;
2011-05-04 09:02:15 +04:00
unsigned int src = virq_to_hw ( virq ) ;
2005-04-17 02:20:36 +04:00
u32 temp ;
temp = ipic_read ( ipic - > regs , IPIC_SICFR ) ;
/* clear and set HPI */
temp & = 0x7f000000 ;
temp | = ( src & 0x7f ) < < 24 ;
ipic_write ( ipic - > regs , IPIC_SICFR , temp ) ;
}
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
}
void ipic_enable_mcp ( enum ipic_mcp_irq mcp_irq )
{
struct ipic * ipic = primary_ipic ;
u32 temp ;
temp = ipic_read ( ipic - > regs , IPIC_SERMR ) ;
temp | = ( 1 < < ( 31 - mcp_irq ) ) ;
ipic_write ( ipic - > regs , IPIC_SERMR , temp ) ;
}
void ipic_disable_mcp ( enum ipic_mcp_irq mcp_irq )
{
struct ipic * ipic = primary_ipic ;
u32 temp ;
temp = ipic_read ( ipic - > regs , IPIC_SERMR ) ;
temp & = ( 1 < < ( 31 - mcp_irq ) ) ;
ipic_write ( ipic - > regs , IPIC_SERMR , temp ) ;
}
u32 ipic_get_mcp_status ( void )
{
return ipic_read ( primary_ipic - > regs , IPIC_SERMR ) ;
}
void ipic_clear_mcp_status ( u32 mask )
{
ipic_write ( primary_ipic - > regs , IPIC_SERMR , mask ) ;
}
2006-08-25 20:59:07 +04:00
/* Return an interrupt vector or NO_IRQ 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 */
2006-08-25 20:59:07 +04:00
return NO_IRQ ;
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 ) ;