2005-09-26 10:04:21 +04:00
/*
* arch / powerpc / kernel / mpic . c
*
* Driver for interrupt controllers following the OpenPIC standard , the
* common implementation beeing IBM ' s MPIC . This driver also can deal
* with various broken implementations of this HW .
*
* Copyright ( C ) 2004 Benjamin Herrenschmidt , IBM Corp .
*
* 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 .
*/
# undef DEBUG
2005-12-14 05:10:10 +03:00
# undef DEBUG_IPI
# undef DEBUG_IRQ
# undef DEBUG_LOW
2005-09-26 10:04:21 +04:00
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/irq.h>
# include <linux/smp.h>
# include <linux/interrupt.h>
# include <linux/bootmem.h>
# include <linux/spinlock.h>
# include <linux/pci.h>
# include <asm/ptrace.h>
# include <asm/signal.h>
# include <asm/io.h>
# include <asm/pgtable.h>
# include <asm/irq.h>
# include <asm/machdep.h>
# include <asm/mpic.h>
# include <asm/smp.h>
# ifdef DEBUG
# define DBG(fmt...) printk(fmt)
# else
# define DBG(fmt...)
# endif
static struct mpic * mpics ;
static struct mpic * mpic_primary ;
static DEFINE_SPINLOCK ( mpic_lock ) ;
2005-10-01 07:49:08 +04:00
# ifdef CONFIG_PPC32 /* XXX for now */
2005-11-29 22:25:54 +03:00
# ifdef CONFIG_IRQ_ALL_CPUS
# define distribute_irqs (1)
# else
# define distribute_irqs (0)
# endif
2005-10-01 07:49:08 +04:00
# endif
2005-09-26 10:04:21 +04:00
/*
* Register accessor functions
*/
static inline u32 _mpic_read ( unsigned int be , volatile u32 __iomem * base ,
unsigned int reg )
{
if ( be )
return in_be32 ( base + ( reg > > 2 ) ) ;
else
return in_le32 ( base + ( reg > > 2 ) ) ;
}
static inline void _mpic_write ( unsigned int be , volatile u32 __iomem * base ,
unsigned int reg , u32 value )
{
if ( be )
out_be32 ( base + ( reg > > 2 ) , value ) ;
else
out_le32 ( base + ( reg > > 2 ) , value ) ;
}
static inline u32 _mpic_ipi_read ( struct mpic * mpic , unsigned int ipi )
{
unsigned int be = ( mpic - > flags & MPIC_BIG_ENDIAN ) ! = 0 ;
unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + ( ipi * 0x10 ) ;
if ( mpic - > flags & MPIC_BROKEN_IPI )
be = ! be ;
return _mpic_read ( be , mpic - > gregs , offset ) ;
}
static inline void _mpic_ipi_write ( struct mpic * mpic , unsigned int ipi , u32 value )
{
unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + ( ipi * 0x10 ) ;
_mpic_write ( mpic - > flags & MPIC_BIG_ENDIAN , mpic - > gregs , offset , value ) ;
}
static inline u32 _mpic_cpu_read ( struct mpic * mpic , unsigned int reg )
{
unsigned int cpu = 0 ;
if ( mpic - > flags & MPIC_PRIMARY )
cpu = hard_smp_processor_id ( ) ;
2006-07-03 13:32:51 +04:00
return _mpic_read ( mpic - > flags & MPIC_BIG_ENDIAN ,
mpic - > cpuregs [ cpu ] , reg ) ;
2005-09-26 10:04:21 +04:00
}
static inline void _mpic_cpu_write ( struct mpic * mpic , unsigned int reg , u32 value )
{
unsigned int cpu = 0 ;
if ( mpic - > flags & MPIC_PRIMARY )
cpu = hard_smp_processor_id ( ) ;
_mpic_write ( mpic - > flags & MPIC_BIG_ENDIAN , mpic - > cpuregs [ cpu ] , reg , value ) ;
}
static inline u32 _mpic_irq_read ( struct mpic * mpic , unsigned int src_no , unsigned int reg )
{
unsigned int isu = src_no > > mpic - > isu_shift ;
unsigned int idx = src_no & mpic - > isu_mask ;
return _mpic_read ( mpic - > flags & MPIC_BIG_ENDIAN , mpic - > isus [ isu ] ,
reg + ( idx * MPIC_IRQ_STRIDE ) ) ;
}
static inline void _mpic_irq_write ( struct mpic * mpic , unsigned int src_no ,
unsigned int reg , u32 value )
{
unsigned int isu = src_no > > mpic - > isu_shift ;
unsigned int idx = src_no & mpic - > isu_mask ;
_mpic_write ( mpic - > flags & MPIC_BIG_ENDIAN , mpic - > isus [ isu ] ,
reg + ( idx * MPIC_IRQ_STRIDE ) , value ) ;
}
# define mpic_read(b,r) _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))
# define mpic_write(b,r,v) _mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v))
# define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i))
# define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v))
# define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i))
# define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v))
# define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r))
# define mpic_irq_write(s,r,v) _mpic_irq_write(mpic,(s),(r),(v))
/*
* Low level utility functions
*/
/* Check if we have one of those nice broken MPICs with a flipped endian on
* reads from IPI registers
*/
static void __init mpic_test_broken_ipi ( struct mpic * mpic )
{
u32 r ;
mpic_write ( mpic - > gregs , MPIC_GREG_IPI_VECTOR_PRI_0 , MPIC_VECPRI_MASK ) ;
r = mpic_read ( mpic - > gregs , MPIC_GREG_IPI_VECTOR_PRI_0 ) ;
if ( r = = le32_to_cpu ( MPIC_VECPRI_MASK ) ) {
printk ( KERN_INFO " mpic: Detected reversed IPI registers \n " ) ;
mpic - > flags | = MPIC_BROKEN_IPI ;
}
}
# ifdef CONFIG_MPIC_BROKEN_U3
/* Test if an interrupt is sourced from HyperTransport (used on broken U3s)
* to force the edge setting on the MPIC and do the ack workaround .
*/
2005-12-14 05:10:10 +03:00
static inline int mpic_is_ht_interrupt ( struct mpic * mpic , unsigned int source )
2005-09-26 10:04:21 +04:00
{
2005-12-14 05:10:10 +03:00
if ( source > = 128 | | ! mpic - > fixups )
2005-09-26 10:04:21 +04:00
return 0 ;
2005-12-14 05:10:10 +03:00
return mpic - > fixups [ source ] . base ! = NULL ;
2005-09-26 10:04:21 +04:00
}
2005-12-13 10:04:29 +03:00
2005-12-14 05:10:10 +03:00
static inline void mpic_ht_end_irq ( struct mpic * mpic , unsigned int source )
2005-09-26 10:04:21 +04:00
{
2005-12-14 05:10:10 +03:00
struct mpic_irq_fixup * fixup = & mpic - > fixups [ source ] ;
2005-09-26 10:04:21 +04:00
2005-12-14 05:10:10 +03:00
if ( fixup - > applebase ) {
unsigned int soff = ( fixup - > index > > 3 ) & ~ 3 ;
unsigned int mask = 1U < < ( fixup - > index & 0x1f ) ;
writel ( mask , fixup - > applebase + soff ) ;
} else {
spin_lock ( & mpic - > fixup_lock ) ;
writeb ( 0x11 + 2 * fixup - > index , fixup - > base + 2 ) ;
writel ( fixup - > data , fixup - > base + 4 ) ;
spin_unlock ( & mpic - > fixup_lock ) ;
}
2005-09-26 10:04:21 +04:00
}
2005-12-14 05:10:10 +03:00
static void mpic_startup_ht_interrupt ( struct mpic * mpic , unsigned int source ,
unsigned int irqflags )
{
struct mpic_irq_fixup * fixup = & mpic - > fixups [ source ] ;
unsigned long flags ;
u32 tmp ;
if ( fixup - > base = = NULL )
return ;
DBG ( " startup_ht_interrupt(%u, %u) index: %d \n " ,
source , irqflags , fixup - > index ) ;
spin_lock_irqsave ( & mpic - > fixup_lock , flags ) ;
/* Enable and configure */
writeb ( 0x10 + 2 * fixup - > index , fixup - > base + 2 ) ;
tmp = readl ( fixup - > base + 4 ) ;
tmp & = ~ ( 0x23U ) ;
if ( irqflags & IRQ_LEVEL )
tmp | = 0x22 ;
writel ( tmp , fixup - > base + 4 ) ;
spin_unlock_irqrestore ( & mpic - > fixup_lock , flags ) ;
}
static void mpic_shutdown_ht_interrupt ( struct mpic * mpic , unsigned int source ,
unsigned int irqflags )
{
struct mpic_irq_fixup * fixup = & mpic - > fixups [ source ] ;
unsigned long flags ;
u32 tmp ;
if ( fixup - > base = = NULL )
return ;
DBG ( " shutdown_ht_interrupt(%u, %u) \n " , source , irqflags ) ;
/* Disable */
spin_lock_irqsave ( & mpic - > fixup_lock , flags ) ;
writeb ( 0x10 + 2 * fixup - > index , fixup - > base + 2 ) ;
tmp = readl ( fixup - > base + 4 ) ;
2006-02-17 13:25:42 +03:00
tmp | = 1 ;
2005-12-14 05:10:10 +03:00
writel ( tmp , fixup - > base + 4 ) ;
spin_unlock_irqrestore ( & mpic - > fixup_lock , flags ) ;
}
2005-09-26 10:04:21 +04:00
2005-12-14 05:10:10 +03:00
static void __init mpic_scan_ht_pic ( struct mpic * mpic , u8 __iomem * devbase ,
unsigned int devfn , u32 vdid )
2005-09-26 10:04:21 +04:00
{
2005-12-13 10:04:29 +03:00
int i , irq , n ;
2005-12-14 05:10:10 +03:00
u8 __iomem * base ;
2005-09-26 10:04:21 +04:00
u32 tmp ;
2005-12-13 10:04:29 +03:00
u8 pos ;
2005-09-26 10:04:21 +04:00
2005-12-14 05:10:10 +03:00
for ( pos = readb ( devbase + PCI_CAPABILITY_LIST ) ; pos ! = 0 ;
pos = readb ( devbase + pos + PCI_CAP_LIST_NEXT ) ) {
u8 id = readb ( devbase + pos + PCI_CAP_LIST_ID ) ;
if ( id = = PCI_CAP_ID_HT_IRQCONF ) {
2005-12-13 10:04:29 +03:00
id = readb ( devbase + pos + 3 ) ;
if ( id = = 0x80 )
break ;
}
2005-09-26 10:04:21 +04:00
}
2005-12-13 10:04:29 +03:00
if ( pos = = 0 )
return ;
2005-12-14 05:10:10 +03:00
base = devbase + pos ;
writeb ( 0x01 , base + 2 ) ;
n = ( readl ( base + 4 ) > > 16 ) & 0xff ;
2005-09-26 10:04:21 +04:00
2005-12-14 05:10:10 +03:00
printk ( KERN_INFO " mpic: - HT:%02x.%x [0x%02x] vendor %04x device %04x "
" has %d irqs \n " ,
devfn > > 3 , devfn & 0x7 , pos , vdid & 0xffff , vdid > > 16 , n + 1 ) ;
2005-12-13 10:04:29 +03:00
for ( i = 0 ; i < = n ; i + + ) {
2005-12-14 05:10:10 +03:00
writeb ( 0x10 + 2 * i , base + 2 ) ;
tmp = readl ( base + 4 ) ;
2005-09-26 10:04:21 +04:00
irq = ( tmp > > 16 ) & 0xff ;
2005-12-14 05:10:10 +03:00
DBG ( " HT PIC index 0x%x, irq 0x%x, tmp: %08x \n " , i , irq , tmp ) ;
/* mask it , will be unmasked later */
tmp | = 0x1 ;
writel ( tmp , base + 4 ) ;
mpic - > fixups [ irq ] . index = i ;
mpic - > fixups [ irq ] . base = base ;
/* Apple HT PIC has a non-standard way of doing EOIs */
if ( ( vdid & 0xffff ) = = 0x106b )
mpic - > fixups [ irq ] . applebase = devbase + 0x60 ;
else
mpic - > fixups [ irq ] . applebase = NULL ;
writeb ( 0x11 + 2 * i , base + 2 ) ;
mpic - > fixups [ irq ] . data = readl ( base + 4 ) | 0x80000000 ;
2005-09-26 10:04:21 +04:00
}
}
2005-12-13 10:04:29 +03:00
2005-12-14 05:10:10 +03:00
static void __init mpic_scan_ht_pics ( struct mpic * mpic )
2005-09-26 10:04:21 +04:00
{
unsigned int devfn ;
u8 __iomem * cfgspace ;
2005-12-14 05:10:10 +03:00
printk ( KERN_INFO " mpic: Setting up HT PICs workarounds for U3/U4 \n " ) ;
2005-09-26 10:04:21 +04:00
/* Allocate fixups array */
mpic - > fixups = alloc_bootmem ( 128 * sizeof ( struct mpic_irq_fixup ) ) ;
BUG_ON ( mpic - > fixups = = NULL ) ;
memset ( mpic - > fixups , 0 , 128 * sizeof ( struct mpic_irq_fixup ) ) ;
/* Init spinlock */
spin_lock_init ( & mpic - > fixup_lock ) ;
2005-12-13 10:04:29 +03:00
/* Map U3 config space. We assume all IO-APICs are on the primary bus
* so we only need to map 64 kB .
2005-09-26 10:04:21 +04:00
*/
2005-12-13 10:04:29 +03:00
cfgspace = ioremap ( 0xf2000000 , 0x10000 ) ;
2005-09-26 10:04:21 +04:00
BUG_ON ( cfgspace = = NULL ) ;
2005-12-14 05:10:10 +03:00
/* Now we scan all slots. We do a very quick scan, we read the header
* type , vendor ID and device ID only , that ' s plenty enough
2005-09-26 10:04:21 +04:00
*/
2005-12-13 10:04:29 +03:00
for ( devfn = 0 ; devfn < 0x100 ; devfn + + ) {
2005-09-26 10:04:21 +04:00
u8 __iomem * devbase = cfgspace + ( devfn < < 8 ) ;
u8 hdr_type = readb ( devbase + PCI_HEADER_TYPE ) ;
u32 l = readl ( devbase + PCI_VENDOR_ID ) ;
2005-12-14 05:10:10 +03:00
u16 s ;
2005-09-26 10:04:21 +04:00
DBG ( " devfn %x, l: %x \n " , devfn , l ) ;
/* If no device, skip */
if ( l = = 0xffffffff | | l = = 0x00000000 | |
l = = 0x0000ffff | | l = = 0xffff0000 )
goto next ;
2005-12-14 05:10:10 +03:00
/* Check if is supports capability lists */
s = readw ( devbase + PCI_STATUS ) ;
if ( ! ( s & PCI_STATUS_CAP_LIST ) )
goto next ;
2005-09-26 10:04:21 +04:00
2005-12-14 05:10:10 +03:00
mpic_scan_ht_pic ( mpic , devbase , devfn , l ) ;
2005-12-13 10:04:29 +03:00
2005-09-26 10:04:21 +04:00
next :
/* next device, if function 0 */
2005-12-13 10:04:29 +03:00
if ( PCI_FUNC ( devfn ) = = 0 & & ( hdr_type & 0x80 ) = = 0 )
2005-09-26 10:04:21 +04:00
devfn + = 7 ;
}
}
# endif /* CONFIG_MPIC_BROKEN_U3 */
2006-07-03 15:36:01 +04:00
# define mpic_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)
2005-09-26 10:04:21 +04:00
/* Find an mpic associated with a given linux interrupt */
static struct mpic * mpic_find ( unsigned int irq , unsigned int * is_ipi )
{
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
if ( irq < NUM_ISA_INTERRUPTS )
return NULL ;
if ( is_ipi )
* is_ipi = ( src > = MPIC_VEC_IPI_0 & & src < = MPIC_VEC_IPI_3 ) ;
return irq_desc [ irq ] . chip_data ;
2005-09-26 10:04:21 +04:00
}
/* Convert a cpu mask from logical to physical cpu numbers. */
static inline u32 mpic_physmask ( u32 cpumask )
{
int i ;
u32 mask = 0 ;
for ( i = 0 ; i < NR_CPUS ; + + i , cpumask > > = 1 )
mask | = ( cpumask & 1 ) < < get_hard_smp_processor_id ( i ) ;
return mask ;
}
# ifdef CONFIG_SMP
/* Get the mpic structure from the IPI number */
static inline struct mpic * mpic_from_ipi ( unsigned int ipi )
{
2006-07-03 13:32:51 +04:00
return irq_desc [ ipi ] . chip_data ;
2005-09-26 10:04:21 +04:00
}
# endif
/* Get the mpic structure from the irq number */
static inline struct mpic * mpic_from_irq ( unsigned int irq )
{
2006-07-03 13:32:51 +04:00
return irq_desc [ irq ] . chip_data ;
2005-09-26 10:04:21 +04:00
}
/* Send an EOI */
static inline void mpic_eoi ( struct mpic * mpic )
{
mpic_cpu_write ( MPIC_CPU_EOI , 0 ) ;
( void ) mpic_cpu_read ( MPIC_CPU_WHOAMI ) ;
}
# ifdef CONFIG_SMP
static irqreturn_t mpic_ipi_action ( int irq , void * dev_id , struct pt_regs * regs )
{
2006-07-03 15:36:01 +04:00
smp_message_recv ( mpic_irq_to_hw ( irq ) - MPIC_VEC_IPI_0 , regs ) ;
2005-09-26 10:04:21 +04:00
return IRQ_HANDLED ;
}
# endif /* CONFIG_SMP */
/*
* Linux descriptor level callbacks
*/
2006-07-03 13:32:51 +04:00
static void mpic_unmask_irq ( unsigned int irq )
2005-09-26 10:04:21 +04:00
{
unsigned int loops = 100000 ;
struct mpic * mpic = mpic_from_irq ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-09-26 10:04:21 +04:00
2005-10-26 15:55:33 +04:00
DBG ( " %p: %s: enable_irq: %d (src %d) \n " , mpic , mpic - > name , irq , src ) ;
2005-09-26 10:04:21 +04:00
mpic_irq_write ( src , MPIC_IRQ_VECTOR_PRI ,
2005-11-18 09:18:15 +03:00
mpic_irq_read ( src , MPIC_IRQ_VECTOR_PRI ) &
~ MPIC_VECPRI_MASK ) ;
2005-09-26 10:04:21 +04:00
/* make sure mask gets to controller before we return to user */
do {
if ( ! loops - - ) {
printk ( KERN_ERR " mpic_enable_irq timeout \n " ) ;
break ;
}
} while ( mpic_irq_read ( src , MPIC_IRQ_VECTOR_PRI ) & MPIC_VECPRI_MASK ) ;
}
2006-07-03 13:32:51 +04:00
static void mpic_mask_irq ( unsigned int irq )
2005-09-26 10:04:21 +04:00
{
unsigned int loops = 100000 ;
struct mpic * mpic = mpic_from_irq ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-09-26 10:04:21 +04:00
DBG ( " %s: disable_irq: %d (src %d) \n " , mpic - > name , irq , src ) ;
mpic_irq_write ( src , MPIC_IRQ_VECTOR_PRI ,
2005-11-18 09:18:15 +03:00
mpic_irq_read ( src , MPIC_IRQ_VECTOR_PRI ) |
MPIC_VECPRI_MASK ) ;
2005-09-26 10:04:21 +04:00
/* make sure mask gets to controller before we return to user */
do {
if ( ! loops - - ) {
printk ( KERN_ERR " mpic_enable_irq timeout \n " ) ;
break ;
}
} while ( ! ( mpic_irq_read ( src , MPIC_IRQ_VECTOR_PRI ) & MPIC_VECPRI_MASK ) ) ;
}
2006-07-03 13:32:51 +04:00
static void mpic_end_irq ( unsigned int irq )
2005-12-14 05:10:10 +03:00
{
2006-07-03 13:32:51 +04:00
struct mpic * mpic = mpic_from_irq ( irq ) ;
# ifdef DEBUG_IRQ
DBG ( " %s: end_irq: %d \n " , mpic - > name , irq ) ;
# endif
/* We always EOI on end_irq() even for edge interrupts since that
* should only lower the priority , the MPIC should have properly
* latched another edge interrupt coming in anyway
*/
mpic_eoi ( mpic ) ;
}
2005-12-14 05:10:10 +03:00
# ifdef CONFIG_MPIC_BROKEN_U3
2006-07-03 13:32:51 +04:00
static void mpic_unmask_ht_irq ( unsigned int irq )
{
2005-12-14 05:10:10 +03:00
struct mpic * mpic = mpic_from_irq ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-12-14 05:10:10 +03:00
2006-07-03 13:32:51 +04:00
mpic_unmask_irq ( irq ) ;
2005-12-14 05:10:10 +03:00
2006-07-03 13:32:51 +04:00
if ( irq_desc [ irq ] . status & IRQ_LEVEL )
mpic_ht_end_irq ( mpic , src ) ;
}
static unsigned int mpic_startup_ht_irq ( unsigned int irq )
{
struct mpic * mpic = mpic_from_irq ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-12-14 05:10:10 +03:00
2006-07-03 13:32:51 +04:00
mpic_unmask_irq ( irq ) ;
mpic_startup_ht_interrupt ( mpic , src , irq_desc [ irq ] . status ) ;
return 0 ;
2005-12-14 05:10:10 +03:00
}
2006-07-03 13:32:51 +04:00
static void mpic_shutdown_ht_irq ( unsigned int irq )
{
struct mpic * mpic = mpic_from_irq ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2006-07-03 13:32:51 +04:00
mpic_shutdown_ht_interrupt ( mpic , src , irq_desc [ irq ] . status ) ;
mpic_mask_irq ( irq ) ;
}
static void mpic_end_ht_irq ( unsigned int irq )
2005-09-26 10:04:21 +04:00
{
struct mpic * mpic = mpic_from_irq ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-09-26 10:04:21 +04:00
2005-12-14 05:10:10 +03:00
# ifdef DEBUG_IRQ
2005-09-26 10:04:21 +04:00
DBG ( " %s: end_irq: %d \n " , mpic - > name , irq ) ;
2005-12-14 05:10:10 +03:00
# endif
2005-09-26 10:04:21 +04:00
/* We always EOI on end_irq() even for edge interrupts since that
* should only lower the priority , the MPIC should have properly
* latched another edge interrupt coming in anyway
*/
2006-07-03 13:32:51 +04:00
if ( irq_desc [ irq ] . status & IRQ_LEVEL )
mpic_ht_end_irq ( mpic , src ) ;
2005-09-26 10:04:21 +04:00
mpic_eoi ( mpic ) ;
}
2006-07-03 13:32:51 +04:00
# endif /* CONFIG_MPIC_BROKEN_U3 */
2005-09-26 10:04:21 +04:00
# ifdef CONFIG_SMP
2006-07-03 13:32:51 +04:00
static void mpic_unmask_ipi ( unsigned int irq )
2005-09-26 10:04:21 +04:00
{
struct mpic * mpic = mpic_from_ipi ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) - MPIC_VEC_IPI_0 ;
2005-09-26 10:04:21 +04:00
DBG ( " %s: enable_ipi: %d (ipi %d) \n " , mpic - > name , irq , src ) ;
mpic_ipi_write ( src , mpic_ipi_read ( src ) & ~ MPIC_VECPRI_MASK ) ;
}
2006-07-03 13:32:51 +04:00
static void mpic_mask_ipi ( unsigned int irq )
2005-09-26 10:04:21 +04:00
{
/* NEVER disable an IPI... that's just plain wrong! */
}
static void mpic_end_ipi ( unsigned int irq )
{
struct mpic * mpic = mpic_from_ipi ( irq ) ;
/*
* IPIs are marked IRQ_PER_CPU . This has the side effect of
* preventing the IRQ_PENDING / IRQ_INPROGRESS logic from
* applying to them . We EOI them late to avoid re - entering .
2006-07-02 06:29:22 +04:00
* We mark IPI ' s with IRQF_DISABLED as they must run with
2005-09-26 10:04:21 +04:00
* irqs disabled .
*/
mpic_eoi ( mpic ) ;
}
# endif /* CONFIG_SMP */
static void mpic_set_affinity ( unsigned int irq , cpumask_t cpumask )
{
struct mpic * mpic = mpic_from_irq ( irq ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-09-26 10:04:21 +04:00
cpumask_t tmp ;
cpus_and ( tmp , cpumask , cpu_online_map ) ;
2006-07-03 15:36:01 +04:00
mpic_irq_write ( src , MPIC_IRQ_DESTINATION ,
2005-09-26 10:04:21 +04:00
mpic_physmask ( cpus_addr ( tmp ) [ 0 ] ) ) ;
}
2006-07-03 15:36:01 +04:00
static unsigned int mpic_flags_to_vecpri ( unsigned int flags , int * level )
{
unsigned int vecpri ;
/* Now convert sense value */
switch ( flags & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_RISING :
vecpri = MPIC_VECPRI_SENSE_EDGE |
MPIC_VECPRI_POLARITY_POSITIVE ;
* level = 0 ;
break ;
case IRQ_TYPE_EDGE_FALLING :
vecpri = MPIC_VECPRI_SENSE_EDGE |
MPIC_VECPRI_POLARITY_NEGATIVE ;
* level = 0 ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
vecpri = MPIC_VECPRI_SENSE_LEVEL |
MPIC_VECPRI_POLARITY_POSITIVE ;
* level = 1 ;
break ;
case IRQ_TYPE_LEVEL_LOW :
default :
vecpri = MPIC_VECPRI_SENSE_LEVEL |
MPIC_VECPRI_POLARITY_NEGATIVE ;
* level = 1 ;
}
return vecpri ;
}
2006-07-03 13:32:51 +04:00
static struct irq_chip mpic_irq_chip = {
. mask = mpic_mask_irq ,
. unmask = mpic_unmask_irq ,
. eoi = mpic_end_irq ,
} ;
# ifdef CONFIG_SMP
static struct irq_chip mpic_ipi_chip = {
. mask = mpic_mask_ipi ,
. unmask = mpic_unmask_ipi ,
. eoi = mpic_end_ipi ,
} ;
# endif /* CONFIG_SMP */
# ifdef CONFIG_MPIC_BROKEN_U3
static struct irq_chip mpic_irq_ht_chip = {
. startup = mpic_startup_ht_irq ,
. shutdown = mpic_shutdown_ht_irq ,
. mask = mpic_mask_irq ,
. unmask = mpic_unmask_ht_irq ,
. eoi = mpic_end_ht_irq ,
} ;
# endif /* CONFIG_MPIC_BROKEN_U3 */
2005-09-26 10:04:21 +04:00
2006-07-03 15:36:01 +04:00
static int mpic_host_match ( struct irq_host * h , struct device_node * node )
{
struct mpic * mpic = h - > host_data ;
/* Exact match, unless mpic node is NULL */
return mpic - > of_node = = NULL | | mpic - > of_node = = node ;
}
static int mpic_host_map ( struct irq_host * h , unsigned int virq ,
irq_hw_number_t hw , unsigned int flags )
{
struct irq_desc * desc = get_irq_desc ( virq ) ;
struct irq_chip * chip ;
struct mpic * mpic = h - > host_data ;
unsigned int vecpri = MPIC_VECPRI_SENSE_LEVEL |
MPIC_VECPRI_POLARITY_NEGATIVE ;
int level ;
pr_debug ( " mpic: map virq %d, hwirq 0x%lx, flags: 0x%x \n " ,
virq , hw , flags ) ;
if ( hw = = MPIC_VEC_SPURRIOUS )
return - EINVAL ;
# ifdef CONFIG_SMP
else if ( hw > = MPIC_VEC_IPI_0 ) {
WARN_ON ( ! ( mpic - > flags & MPIC_PRIMARY ) ) ;
pr_debug ( " mpic: mapping as IPI \n " ) ;
set_irq_chip_data ( virq , mpic ) ;
set_irq_chip_and_handler ( virq , & mpic - > hc_ipi ,
handle_percpu_irq ) ;
return 0 ;
}
# endif /* CONFIG_SMP */
if ( hw > = mpic - > irq_count )
return - EINVAL ;
/* If no sense provided, check default sense array */
if ( ( ( flags & IRQ_TYPE_SENSE_MASK ) = = IRQ_TYPE_NONE ) & &
mpic - > senses & & hw < mpic - > senses_count )
flags | = mpic - > senses [ hw ] ;
vecpri = mpic_flags_to_vecpri ( flags , & level ) ;
if ( level )
desc - > status | = IRQ_LEVEL ;
chip = & mpic - > hc_irq ;
# ifdef CONFIG_MPIC_BROKEN_U3
/* Check for HT interrupts, override vecpri */
if ( mpic_is_ht_interrupt ( mpic , hw ) ) {
vecpri & = ~ ( MPIC_VECPRI_SENSE_MASK |
MPIC_VECPRI_POLARITY_MASK ) ;
vecpri | = MPIC_VECPRI_POLARITY_POSITIVE ;
chip = & mpic - > hc_ht_irq ;
}
# endif
/* Reconfigure irq */
vecpri | = MPIC_VECPRI_MASK | hw | ( 8 < < MPIC_VECPRI_PRIORITY_SHIFT ) ;
mpic_irq_write ( hw , MPIC_IRQ_VECTOR_PRI , vecpri ) ;
pr_debug ( " mpic: mapping as IRQ \n " ) ;
set_irq_chip_data ( virq , mpic ) ;
set_irq_chip_and_handler ( virq , chip , handle_fasteoi_irq ) ;
return 0 ;
}
static int mpic_host_xlate ( struct irq_host * h , struct device_node * ct ,
u32 * intspec , unsigned int intsize ,
irq_hw_number_t * out_hwirq , unsigned int * out_flags )
{
static unsigned char map_mpic_senses [ 4 ] = {
IRQ_TYPE_EDGE_RISING ,
IRQ_TYPE_LEVEL_LOW ,
IRQ_TYPE_LEVEL_HIGH ,
IRQ_TYPE_EDGE_FALLING ,
} ;
* out_hwirq = intspec [ 0 ] ;
if ( intsize > 1 & & intspec [ 1 ] < 4 )
* out_flags = map_mpic_senses [ intspec [ 1 ] ] ;
else
* out_flags = IRQ_TYPE_NONE ;
return 0 ;
}
static struct irq_host_ops mpic_host_ops = {
. match = mpic_host_match ,
. map = mpic_host_map ,
. xlate = mpic_host_xlate ,
} ;
2005-09-26 10:04:21 +04:00
/*
* Exported functions
*/
2006-07-03 15:36:01 +04:00
struct mpic * __init mpic_alloc ( struct device_node * node ,
unsigned long phys_addr ,
2005-09-26 10:04:21 +04:00
unsigned int flags ,
unsigned int isu_size ,
unsigned int irq_count ,
const char * name )
{
struct mpic * mpic ;
u32 reg ;
const char * vers ;
int i ;
mpic = alloc_bootmem ( sizeof ( struct mpic ) ) ;
if ( mpic = = NULL )
return NULL ;
memset ( mpic , 0 , sizeof ( struct mpic ) ) ;
mpic - > name = name ;
2006-07-03 15:36:01 +04:00
mpic - > of_node = node ? of_node_get ( node ) : NULL ;
2005-09-26 10:04:21 +04:00
2006-07-03 15:36:01 +04:00
mpic - > irqhost = irq_alloc_host ( IRQ_HOST_MAP_LINEAR , 256 ,
& mpic_host_ops ,
MPIC_VEC_SPURRIOUS ) ;
if ( mpic - > irqhost = = NULL ) {
of_node_put ( node ) ;
return NULL ;
}
mpic - > irqhost - > host_data = mpic ;
2006-07-03 13:32:51 +04:00
mpic - > hc_irq = mpic_irq_chip ;
2005-09-26 10:04:21 +04:00
mpic - > hc_irq . typename = name ;
if ( flags & MPIC_PRIMARY )
mpic - > hc_irq . set_affinity = mpic_set_affinity ;
2006-07-03 13:32:51 +04:00
# ifdef CONFIG_MPIC_BROKEN_U3
mpic - > hc_ht_irq = mpic_irq_ht_chip ;
mpic - > hc_ht_irq . typename = name ;
if ( flags & MPIC_PRIMARY )
mpic - > hc_ht_irq . set_affinity = mpic_set_affinity ;
# endif /* CONFIG_MPIC_BROKEN_U3 */
2005-09-26 10:04:21 +04:00
# ifdef CONFIG_SMP
2006-07-03 13:32:51 +04:00
mpic - > hc_ipi = mpic_ipi_chip ;
2006-07-03 15:36:01 +04:00
mpic - > hc_ipi . typename = name ;
2005-09-26 10:04:21 +04:00
# endif /* CONFIG_SMP */
mpic - > flags = flags ;
mpic - > isu_size = isu_size ;
mpic - > irq_count = irq_count ;
mpic - > num_sources = 0 ; /* so far */
/* Map the global registers */
mpic - > gregs = ioremap ( phys_addr + MPIC_GREG_BASE , 0x1000 ) ;
2005-10-26 15:55:33 +04:00
mpic - > tmregs = mpic - > gregs + ( ( MPIC_TIMER_BASE - MPIC_GREG_BASE ) > > 2 ) ;
2005-09-26 10:04:21 +04:00
BUG_ON ( mpic - > gregs = = NULL ) ;
/* Reset */
if ( flags & MPIC_WANTS_RESET ) {
mpic_write ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_0 ,
mpic_read ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_0 )
| MPIC_GREG_GCONF_RESET ) ;
while ( mpic_read ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_0 )
& MPIC_GREG_GCONF_RESET )
mb ( ) ;
}
/* Read feature register, calculate num CPUs and, for non-ISU
* MPICs , num sources as well . On ISU MPICs , sources are counted
* as ISUs are added
*/
reg = mpic_read ( mpic - > gregs , MPIC_GREG_FEATURE_0 ) ;
mpic - > num_cpus = ( ( reg & MPIC_GREG_FEATURE_LAST_CPU_MASK )
> > MPIC_GREG_FEATURE_LAST_CPU_SHIFT ) + 1 ;
if ( isu_size = = 0 )
mpic - > num_sources = ( ( reg & MPIC_GREG_FEATURE_LAST_SRC_MASK )
> > MPIC_GREG_FEATURE_LAST_SRC_SHIFT ) + 1 ;
/* Map the per-CPU registers */
for ( i = 0 ; i < mpic - > num_cpus ; i + + ) {
mpic - > cpuregs [ i ] = ioremap ( phys_addr + MPIC_CPU_BASE +
i * MPIC_CPU_STRIDE , 0x1000 ) ;
BUG_ON ( mpic - > cpuregs [ i ] = = NULL ) ;
}
/* Initialize main ISU if none provided */
if ( mpic - > isu_size = = 0 ) {
mpic - > isu_size = mpic - > num_sources ;
mpic - > isus [ 0 ] = ioremap ( phys_addr + MPIC_IRQ_BASE ,
MPIC_IRQ_STRIDE * mpic - > isu_size ) ;
BUG_ON ( mpic - > isus [ 0 ] = = NULL ) ;
}
mpic - > isu_shift = 1 + __ilog2 ( mpic - > isu_size - 1 ) ;
mpic - > isu_mask = ( 1 < < mpic - > isu_shift ) - 1 ;
/* Display version */
switch ( reg & MPIC_GREG_FEATURE_VERSION_MASK ) {
case 1 :
vers = " 1.0 " ;
break ;
case 2 :
vers = " 1.2 " ;
break ;
case 3 :
vers = " 1.3 " ;
break ;
default :
vers = " <unknown> " ;
break ;
}
printk ( KERN_INFO " mpic: Setting up MPIC \" %s \" version %s at %lx, max %d CPUs \n " ,
name , vers , phys_addr , mpic - > num_cpus ) ;
printk ( KERN_INFO " mpic: ISU size: %d, shift: %d, mask: %x \n " , mpic - > isu_size ,
mpic - > isu_shift , mpic - > isu_mask ) ;
mpic - > next = mpics ;
mpics = mpic ;
2006-07-03 15:36:01 +04:00
if ( flags & MPIC_PRIMARY ) {
2005-09-26 10:04:21 +04:00
mpic_primary = mpic ;
2006-07-03 15:36:01 +04:00
irq_set_default_host ( mpic - > irqhost ) ;
}
2005-09-26 10:04:21 +04:00
return mpic ;
}
void __init mpic_assign_isu ( struct mpic * mpic , unsigned int isu_num ,
unsigned long phys_addr )
{
unsigned int isu_first = isu_num * mpic - > isu_size ;
BUG_ON ( isu_num > = MPIC_MAX_ISU ) ;
mpic - > isus [ isu_num ] = ioremap ( phys_addr , MPIC_IRQ_STRIDE * mpic - > isu_size ) ;
if ( ( isu_first + mpic - > isu_size ) > mpic - > num_sources )
mpic - > num_sources = isu_first + mpic - > isu_size ;
}
2006-07-03 15:36:01 +04:00
void __init mpic_set_default_senses ( struct mpic * mpic , u8 * senses , int count )
{
mpic - > senses = senses ;
mpic - > senses_count = count ;
}
2005-09-26 10:04:21 +04:00
void __init mpic_init ( struct mpic * mpic )
{
int i ;
BUG_ON ( mpic - > num_sources = = 0 ) ;
2006-07-03 15:36:01 +04:00
WARN_ON ( mpic - > num_sources > MPIC_VEC_IPI_0 ) ;
/* Sanitize source count */
if ( mpic - > num_sources > MPIC_VEC_IPI_0 )
mpic - > num_sources = MPIC_VEC_IPI_0 ;
2005-09-26 10:04:21 +04:00
printk ( KERN_INFO " mpic: Initializing for %d sources \n " , mpic - > num_sources ) ;
/* Set current processor priority to max */
mpic_cpu_write ( MPIC_CPU_CURRENT_TASK_PRI , 0xf ) ;
/* Initialize timers: just disable them all */
for ( i = 0 ; i < 4 ; i + + ) {
mpic_write ( mpic - > tmregs ,
i * MPIC_TIMER_STRIDE + MPIC_TIMER_DESTINATION , 0 ) ;
mpic_write ( mpic - > tmregs ,
i * MPIC_TIMER_STRIDE + MPIC_TIMER_VECTOR_PRI ,
MPIC_VECPRI_MASK |
( MPIC_VEC_TIMER_0 + i ) ) ;
}
/* Initialize IPIs to our reserved vectors and mark them disabled for now */
mpic_test_broken_ipi ( mpic ) ;
for ( i = 0 ; i < 4 ; i + + ) {
mpic_ipi_write ( i ,
MPIC_VECPRI_MASK |
( 10 < < MPIC_VECPRI_PRIORITY_SHIFT ) |
( MPIC_VEC_IPI_0 + i ) ) ;
}
/* Initialize interrupt sources */
if ( mpic - > irq_count = = 0 )
mpic - > irq_count = mpic - > num_sources ;
# ifdef CONFIG_MPIC_BROKEN_U3
2005-12-14 05:10:10 +03:00
/* Do the HT PIC fixups on U3 broken mpic */
2005-09-26 10:04:21 +04:00
DBG ( " MPIC flags: %x \n " , mpic - > flags ) ;
if ( ( mpic - > flags & MPIC_BROKEN_U3 ) & & ( mpic - > flags & MPIC_PRIMARY ) )
2006-07-03 13:32:51 +04:00
mpic_scan_ht_pics ( mpic ) ;
2005-09-26 10:04:21 +04:00
# endif /* CONFIG_MPIC_BROKEN_U3 */
for ( i = 0 ; i < mpic - > num_sources ; i + + ) {
/* start with vector = source number, and masked */
u32 vecpri = MPIC_VECPRI_MASK | i | ( 8 < < MPIC_VECPRI_PRIORITY_SHIFT ) ;
2006-07-03 15:36:01 +04:00
int level = 1 ;
2005-09-26 10:04:21 +04:00
/* do senses munging */
2006-07-03 15:36:01 +04:00
if ( mpic - > senses & & i < mpic - > senses_count )
vecpri = mpic_flags_to_vecpri ( mpic - > senses [ i ] ,
& level ) ;
else
2005-09-26 10:04:21 +04:00
vecpri | = MPIC_VECPRI_SENSE_LEVEL ;
/* deal with broken U3 */
if ( mpic - > flags & MPIC_BROKEN_U3 ) {
# ifdef CONFIG_MPIC_BROKEN_U3
if ( mpic_is_ht_interrupt ( mpic , i ) ) {
vecpri & = ~ ( MPIC_VECPRI_SENSE_MASK |
MPIC_VECPRI_POLARITY_MASK ) ;
vecpri | = MPIC_VECPRI_POLARITY_POSITIVE ;
}
# else
printk ( KERN_ERR " mpic: BROKEN_U3 set, but CONFIG doesn't match \n " ) ;
# endif
}
DBG ( " setup source %d, vecpri: %08x, level: %d \n " , i , vecpri ,
( level ! = 0 ) ) ;
/* init hw */
mpic_irq_write ( i , MPIC_IRQ_VECTOR_PRI , vecpri ) ;
mpic_irq_write ( i , MPIC_IRQ_DESTINATION ,
1 < < hard_smp_processor_id ( ) ) ;
}
/* Init spurrious vector */
mpic_write ( mpic - > gregs , MPIC_GREG_SPURIOUS , MPIC_VEC_SPURRIOUS ) ;
/* Disable 8259 passthrough */
mpic_write ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_0 ,
mpic_read ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_0 )
| MPIC_GREG_GCONF_8259_PTHROU_DIS ) ;
/* Set current processor priority to 0 */
mpic_cpu_write ( MPIC_CPU_CURRENT_TASK_PRI , 0 ) ;
}
2006-06-21 01:15:36 +04:00
void __init mpic_set_clk_ratio ( struct mpic * mpic , u32 clock_ratio )
{
u32 v ;
v = mpic_read ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_1 ) ;
v & = ~ MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO_MASK ;
v | = MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO ( clock_ratio ) ;
mpic_write ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_1 , v ) ;
}
2005-09-26 10:04:21 +04:00
2006-06-21 01:15:36 +04:00
void __init mpic_set_serial_int ( struct mpic * mpic , int enable )
{
u32 v ;
v = mpic_read ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_1 ) ;
if ( enable )
v | = MPIC_GREG_GLOBAL_CONF_1_SIE ;
else
v & = ~ MPIC_GREG_GLOBAL_CONF_1_SIE ;
mpic_write ( mpic - > gregs , MPIC_GREG_GLOBAL_CONF_1 , v ) ;
}
2005-09-26 10:04:21 +04:00
void mpic_irq_set_priority ( unsigned int irq , unsigned int pri )
{
int is_ipi ;
struct mpic * mpic = mpic_find ( irq , & is_ipi ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-09-26 10:04:21 +04:00
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & mpic_lock , flags ) ;
if ( is_ipi ) {
2006-07-03 15:36:01 +04:00
reg = mpic_ipi_read ( src - MPIC_VEC_IPI_0 ) &
2005-11-18 09:18:15 +03:00
~ MPIC_VECPRI_PRIORITY_MASK ;
2006-07-03 15:36:01 +04:00
mpic_ipi_write ( src - MPIC_VEC_IPI_0 ,
2005-09-26 10:04:21 +04:00
reg | ( pri < < MPIC_VECPRI_PRIORITY_SHIFT ) ) ;
} else {
2006-07-03 15:36:01 +04:00
reg = mpic_irq_read ( src , MPIC_IRQ_VECTOR_PRI )
2005-11-18 09:18:15 +03:00
& ~ MPIC_VECPRI_PRIORITY_MASK ;
2006-07-03 15:36:01 +04:00
mpic_irq_write ( src , MPIC_IRQ_VECTOR_PRI ,
2005-09-26 10:04:21 +04:00
reg | ( pri < < MPIC_VECPRI_PRIORITY_SHIFT ) ) ;
}
spin_unlock_irqrestore ( & mpic_lock , flags ) ;
}
unsigned int mpic_irq_get_priority ( unsigned int irq )
{
int is_ipi ;
struct mpic * mpic = mpic_find ( irq , & is_ipi ) ;
2006-07-03 15:36:01 +04:00
unsigned int src = mpic_irq_to_hw ( irq ) ;
2005-09-26 10:04:21 +04:00
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & mpic_lock , flags ) ;
if ( is_ipi )
2006-07-03 15:36:01 +04:00
reg = mpic_ipi_read ( src = MPIC_VEC_IPI_0 ) ;
2005-09-26 10:04:21 +04:00
else
2006-07-03 15:36:01 +04:00
reg = mpic_irq_read ( src , MPIC_IRQ_VECTOR_PRI ) ;
2005-09-26 10:04:21 +04:00
spin_unlock_irqrestore ( & mpic_lock , flags ) ;
return ( reg & MPIC_VECPRI_PRIORITY_MASK ) > > MPIC_VECPRI_PRIORITY_SHIFT ;
}
void mpic_setup_this_cpu ( void )
{
# ifdef CONFIG_SMP
struct mpic * mpic = mpic_primary ;
unsigned long flags ;
u32 msk = 1 < < hard_smp_processor_id ( ) ;
unsigned int i ;
BUG_ON ( mpic = = NULL ) ;
DBG ( " %s: setup_this_cpu(%d) \n " , mpic - > name , hard_smp_processor_id ( ) ) ;
spin_lock_irqsave ( & mpic_lock , flags ) ;
/* let the mpic know we want intrs. default affinity is 0xffffffff
* until changed via / proc . That ' s how it ' s done on x86 . If we want
* it differently , then we should make sure we also change the default
2006-06-29 13:24:38 +04:00
* values of irq_desc [ ] . affinity in irq . c .
2005-09-26 10:04:21 +04:00
*/
if ( distribute_irqs ) {
for ( i = 0 ; i < mpic - > num_sources ; i + + )
mpic_irq_write ( i , MPIC_IRQ_DESTINATION ,
mpic_irq_read ( i , MPIC_IRQ_DESTINATION ) | msk ) ;
}
/* Set current processor priority to 0 */
mpic_cpu_write ( MPIC_CPU_CURRENT_TASK_PRI , 0 ) ;
spin_unlock_irqrestore ( & mpic_lock , flags ) ;
# endif /* CONFIG_SMP */
}
int mpic_cpu_get_priority ( void )
{
struct mpic * mpic = mpic_primary ;
return mpic_cpu_read ( MPIC_CPU_CURRENT_TASK_PRI ) ;
}
void mpic_cpu_set_priority ( int prio )
{
struct mpic * mpic = mpic_primary ;
prio & = MPIC_CPU_TASKPRI_MASK ;
mpic_cpu_write ( MPIC_CPU_CURRENT_TASK_PRI , prio ) ;
}
/*
* XXX : someone who knows mpic should check this .
* do we need to eoi the ipi including for kexec cpu here ( see xics comments ) ?
* or can we reset the mpic in the new kernel ?
*/
void mpic_teardown_this_cpu ( int secondary )
{
struct mpic * mpic = mpic_primary ;
unsigned long flags ;
u32 msk = 1 < < hard_smp_processor_id ( ) ;
unsigned int i ;
BUG_ON ( mpic = = NULL ) ;
DBG ( " %s: teardown_this_cpu(%d) \n " , mpic - > name , hard_smp_processor_id ( ) ) ;
spin_lock_irqsave ( & mpic_lock , flags ) ;
/* let the mpic know we don't want intrs. */
for ( i = 0 ; i < mpic - > num_sources ; i + + )
mpic_irq_write ( i , MPIC_IRQ_DESTINATION ,
mpic_irq_read ( i , MPIC_IRQ_DESTINATION ) & ~ msk ) ;
/* Set current processor priority to max */
mpic_cpu_write ( MPIC_CPU_CURRENT_TASK_PRI , 0xf ) ;
spin_unlock_irqrestore ( & mpic_lock , flags ) ;
}
void mpic_send_ipi ( unsigned int ipi_no , unsigned int cpu_mask )
{
struct mpic * mpic = mpic_primary ;
BUG_ON ( mpic = = NULL ) ;
2005-12-14 05:10:10 +03:00
# ifdef DEBUG_IPI
2005-09-26 10:04:21 +04:00
DBG ( " %s: send_ipi(ipi_no: %d) \n " , mpic - > name , ipi_no ) ;
2005-12-14 05:10:10 +03:00
# endif
2005-09-26 10:04:21 +04:00
mpic_cpu_write ( MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10 ,
mpic_physmask ( cpu_mask & cpus_addr ( cpu_online_map ) [ 0 ] ) ) ;
}
2006-07-03 15:36:01 +04:00
unsigned int mpic_get_one_irq ( struct mpic * mpic , struct pt_regs * regs )
2005-09-26 10:04:21 +04:00
{
2006-07-03 15:36:01 +04:00
u32 src ;
2005-09-26 10:04:21 +04:00
2006-07-03 15:36:01 +04:00
src = mpic_cpu_read ( MPIC_CPU_INTACK ) & MPIC_VECPRI_VECTOR_MASK ;
2005-12-14 05:10:10 +03:00
# ifdef DEBUG_LOW
2006-07-03 15:36:01 +04:00
DBG ( " %s: get_one_irq(): %d \n " , mpic - > name , src ) ;
2005-12-14 05:10:10 +03:00
# endif
2006-07-03 15:36:01 +04:00
if ( unlikely ( src = = MPIC_VEC_SPURRIOUS ) )
return NO_IRQ ;
return irq_linear_revmap ( mpic - > irqhost , src ) ;
2005-09-26 10:04:21 +04:00
}
2006-07-03 15:36:01 +04:00
unsigned int mpic_get_irq ( struct pt_regs * regs )
2005-09-26 10:04:21 +04:00
{
struct mpic * mpic = mpic_primary ;
BUG_ON ( mpic = = NULL ) ;
return mpic_get_one_irq ( mpic , regs ) ;
}
# ifdef CONFIG_SMP
void mpic_request_ipis ( void )
{
struct mpic * mpic = mpic_primary ;
2006-07-03 15:36:01 +04:00
int i ;
static char * ipi_names [ ] = {
" IPI0 (call function) " ,
" IPI1 (reschedule) " ,
" IPI2 (unused) " ,
" IPI3 (debugger break) " ,
} ;
2005-09-26 10:04:21 +04:00
BUG_ON ( mpic = = NULL ) ;
2006-07-03 15:36:01 +04:00
printk ( KERN_INFO " mpic: requesting IPIs ... \n " ) ;
for ( i = 0 ; i < 4 ; i + + ) {
unsigned int vipi = irq_create_mapping ( mpic - > irqhost ,
MPIC_VEC_IPI_0 + i , 0 ) ;
if ( vipi = = NO_IRQ ) {
printk ( KERN_ERR " Failed to map IPI %d \n " , i ) ;
break ;
}
request_irq ( vipi , mpic_ipi_action , IRQF_DISABLED ,
ipi_names [ i ] , mpic ) ;
}
2005-09-26 10:04:21 +04:00
}
2005-10-20 11:09:51 +04:00
void smp_mpic_message_pass ( int target , int msg )
{
/* make sure we're sending something that translates to an IPI */
if ( ( unsigned int ) msg > 3 ) {
printk ( " SMP %d: smp_message_pass: unknown msg %d \n " ,
smp_processor_id ( ) , msg ) ;
return ;
}
switch ( target ) {
case MSG_ALL :
mpic_send_ipi ( msg , 0xffffffff ) ;
break ;
case MSG_ALL_BUT_SELF :
mpic_send_ipi ( msg , 0xffffffff & ~ ( 1 < < smp_processor_id ( ) ) ) ;
break ;
default :
mpic_send_ipi ( msg , 1 < < target ) ;
break ;
}
}
2005-09-26 10:04:21 +04:00
# endif /* CONFIG_SMP */