2008-05-19 16:53:02 -07:00
/*
2011-01-28 22:08:24 +00:00
* Interrupt request handling routines . On the
* Sparc the IRQs are basically ' cast in stone '
* and you are supposed to probe the prom ' s device
* node trees to find out who ' s got which IRQ .
2005-04-16 15:20:36 -07:00
*
* Copyright ( C ) 1995 David S . Miller ( davem @ caip . rutgers . edu )
* Copyright ( C ) 1995 Miguel de Icaza ( miguel @ nuclecu . unam . mx )
* Copyright ( C ) 1995 , 2002 Pete A . Zaitcev ( zaitcev @ yahoo . com )
* Copyright ( C ) 1996 Dave Redman ( djhr @ tadpole . co . uk )
* Copyright ( C ) 1998 - 2000 Anton Blanchard ( anton @ samba . org )
*/
# include <linux/kernel_stat.h>
# include <linux/seq_file.h>
2011-02-25 22:59:20 -08:00
# include <asm/cacheflush.h>
2011-04-18 11:25:44 +00:00
# include <asm/cpudata.h>
2005-04-16 15:20:36 -07:00
# include <asm/pcic.h>
2009-08-17 00:13:31 +00:00
# include <asm/leon.h>
2005-04-16 15:20:36 -07:00
2008-12-08 01:08:24 -08:00
# include "kernel.h"
2007-07-21 19:18:57 -07:00
# include "irq.h"
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_SMP
# define SMP_NOP2 "nop; nop;\n\t"
# define SMP_NOP3 "nop; nop; nop;\n\t"
# else
# define SMP_NOP2
# define SMP_NOP3
# endif /* SMP */
2011-01-28 22:08:24 +00:00
2011-02-25 23:00:19 -08:00
/* platform specific irq setup */
struct sparc_irq_config sparc_irq_config ;
2010-10-07 14:08:55 +01:00
unsigned long arch_local_irq_save ( void )
2005-04-16 15:20:36 -07:00
{
unsigned long retval ;
unsigned long tmp ;
__asm__ __volatile__ (
" rd %%psr, %0 \n \t "
SMP_NOP3 /* Sun4m + Cypress + SMP bug */
" or %0, %2, %1 \n \t "
" wr %1, 0, %%psr \n \t "
" nop; nop; nop \n "
: " =&r " ( retval ) , " =r " ( tmp )
: " i " ( PSR_PIL )
: " memory " ) ;
return retval ;
}
2010-10-07 14:08:55 +01:00
EXPORT_SYMBOL ( arch_local_irq_save ) ;
2005-04-16 15:20:36 -07:00
2010-10-07 14:08:55 +01:00
void arch_local_irq_enable ( void )
2005-04-16 15:20:36 -07:00
{
unsigned long tmp ;
__asm__ __volatile__ (
" rd %%psr, %0 \n \t "
SMP_NOP3 /* Sun4m + Cypress + SMP bug */
" andn %0, %1, %0 \n \t "
" wr %0, 0, %%psr \n \t "
" nop; nop; nop \n "
: " =&r " ( tmp )
: " i " ( PSR_PIL )
: " memory " ) ;
}
2010-10-07 14:08:55 +01:00
EXPORT_SYMBOL ( arch_local_irq_enable ) ;
2005-04-16 15:20:36 -07:00
2010-10-07 14:08:55 +01:00
void arch_local_irq_restore ( unsigned long old_psr )
2005-04-16 15:20:36 -07:00
{
unsigned long tmp ;
__asm__ __volatile__ (
" rd %%psr, %0 \n \t "
" and %2, %1, %2 \n \t "
SMP_NOP2 /* Sun4m + Cypress + SMP bug */
" andn %0, %1, %0 \n \t "
" wr %0, %2, %%psr \n \t "
" nop; nop; nop \n "
: " =&r " ( tmp )
: " i " ( PSR_PIL ) , " r " ( old_psr )
: " memory " ) ;
}
2010-10-07 14:08:55 +01:00
EXPORT_SYMBOL ( arch_local_irq_restore ) ;
2005-04-16 15:20:36 -07:00
/*
* Dave Redman ( djhr @ tadpole . co . uk )
*
* IRQ numbers . . These are no longer restricted to 15. .
*
* this is done to enable SBUS cards and onboard IO to be masked
* correctly . using the interrupt level isn ' t good enough .
*
* For example :
* A device interrupting at sbus level6 and the Floppy both come in
* at IRQ11 , but enabling and disabling them requires writing to
* different bits in the SLAVIO / SEC .
*
* As a result of these changes sun4m machines could now support
* directed CPU interrupts using the existing enable / disable irq code
* with tweaks .
*
2011-04-18 11:25:44 +00:00
* Sun4d complicates things even further . IRQ numbers are arbitrary
* 32 - bit values in that case . Since this is similar to sparc64 ,
* we adopt a virtual IRQ numbering scheme as is done there .
* Virutal interrupt numbers are allocated by build_irq ( ) . So NR_IRQS
* just becomes a limit of how many interrupt sources we can handle in
* a single system . Even fully loaded SS2000 machines top off at
* about 32 interrupt sources or so , therefore a NR_IRQS value of 64
* is more than enough .
*
* We keep a map of per - PIL enable interrupts . These get wired
* up via the irq_chip - > startup ( ) method which gets invoked by
* the generic IRQ layer during request_irq ( ) .
2005-04-16 15:20:36 -07:00
*/
2011-04-18 11:25:44 +00:00
/* Table of allocated irqs. Unused entries has irq == 0 */
static struct irq_bucket irq_table [ NR_IRQS ] ;
/* Protect access to irq_table */
static DEFINE_SPINLOCK ( irq_table_lock ) ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
/* Map between the irq identifier used in hw to the irq_bucket. */
struct irq_bucket * irq_map [ SUN4D_MAX_IRQ ] ;
/* Protect access to irq_map */
static DEFINE_SPINLOCK ( irq_map_lock ) ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
/* Allocate a new irq from the irq_table */
unsigned int irq_alloc ( unsigned int real_irq , unsigned int pil )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
2011-04-18 11:25:44 +00:00
unsigned int i ;
spin_lock_irqsave ( & irq_table_lock , flags ) ;
for ( i = 1 ; i < NR_IRQS ; i + + ) {
if ( irq_table [ i ] . real_irq = = real_irq & & irq_table [ i ] . pil = = pil )
goto found ;
}
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
for ( i = 1 ; i < NR_IRQS ; i + + ) {
if ( ! irq_table [ i ] . irq )
break ;
}
2011-01-28 22:08:24 +00:00
2005-04-16 15:20:36 -07:00
if ( i < NR_IRQS ) {
2011-04-18 11:25:44 +00:00
irq_table [ i ] . real_irq = real_irq ;
irq_table [ i ] . irq = i ;
irq_table [ i ] . pil = pil ;
} else {
printk ( KERN_ERR " IRQ: Out of virtual IRQs. \n " ) ;
i = 0 ;
2005-04-16 15:20:36 -07:00
}
2011-04-18 11:25:44 +00:00
found :
spin_unlock_irqrestore ( & irq_table_lock , flags ) ;
return i ;
2005-04-16 15:20:36 -07:00
}
2011-04-18 11:25:44 +00:00
/* Based on a single pil handler_irq may need to call several
* interrupt handlers . Use irq_map as entry to irq_table ,
* and let each entry in irq_table point to the next entry .
*/
void irq_link ( unsigned int irq )
2005-04-16 15:20:36 -07:00
{
2011-04-18 11:25:44 +00:00
struct irq_bucket * p ;
2011-01-28 22:08:24 +00:00
unsigned long flags ;
2011-04-18 11:25:44 +00:00
unsigned int pil ;
2011-01-28 22:08:24 +00:00
2011-04-18 11:25:44 +00:00
BUG_ON ( irq > = NR_IRQS ) ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
spin_lock_irqsave ( & irq_map_lock , flags ) ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
p = & irq_table [ irq ] ;
pil = p - > pil ;
BUG_ON ( pil > SUN4D_MAX_IRQ ) ;
p - > next = irq_map [ pil ] ;
irq_map [ pil ] = p ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
spin_unlock_irqrestore ( & irq_map_lock , flags ) ;
}
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
void irq_unlink ( unsigned int irq )
{
struct irq_bucket * p , * * pnext ;
unsigned long flags ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
BUG_ON ( irq > = NR_IRQS ) ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
spin_lock_irqsave ( & irq_map_lock , flags ) ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
p = & irq_table [ irq ] ;
BUG_ON ( p - > pil > SUN4D_MAX_IRQ ) ;
pnext = & irq_map [ p - > pil ] ;
while ( * pnext ! = p )
pnext = & ( * pnext ) - > next ;
* pnext = p - > next ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
spin_unlock_irqrestore ( & irq_map_lock , flags ) ;
2005-04-16 15:20:36 -07:00
}
2006-03-23 22:36:19 -08:00
2011-04-18 11:25:44 +00:00
/* /proc/interrupts printing */
int arch_show_interrupts ( struct seq_file * p , int prec )
2005-04-16 15:20:36 -07:00
{
2011-04-18 11:25:44 +00:00
int j ;
2011-01-28 22:08:24 +00:00
2011-05-02 00:08:51 +00:00
# ifdef CONFIG_SMP
seq_printf ( p , " RES: " ) ;
for_each_online_cpu ( j )
seq_printf ( p , " %10u " , cpu_data ( j ) . irq_resched_count ) ;
seq_printf ( p , " IPI rescheduling interrupts \n " ) ;
seq_printf ( p , " CAL: " ) ;
for_each_online_cpu ( j )
seq_printf ( p , " %10u " , cpu_data ( j ) . irq_call_count ) ;
seq_printf ( p , " IPI function call interrupts \n " ) ;
# endif
2011-04-18 11:25:44 +00:00
seq_printf ( p , " NMI: " ) ;
for_each_online_cpu ( j )
seq_printf ( p , " %10u " , cpu_data ( j ) . counter ) ;
seq_printf ( p , " Non-maskable interrupts \n " ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2011-04-18 11:25:44 +00:00
void handler_irq ( unsigned int pil , struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
2006-10-08 14:30:44 +01:00
struct pt_regs * old_regs ;
2011-04-18 11:25:44 +00:00
struct irq_bucket * p ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
BUG_ON ( pil > 15 ) ;
2006-10-08 14:30:44 +01:00
old_regs = set_irq_regs ( regs ) ;
2005-04-16 15:20:36 -07:00
irq_enter ( ) ;
2011-04-18 11:25:44 +00:00
p = irq_map [ pil ] ;
while ( p ) {
struct irq_bucket * next = p - > next ;
generic_handle_irq ( p - > irq ) ;
p = next ;
}
2005-04-16 15:20:36 -07:00
irq_exit ( ) ;
2006-10-08 14:30:44 +01:00
set_irq_regs ( old_regs ) ;
2005-04-16 15:20:36 -07:00
}
2007-08-02 00:19:14 -07:00
# if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE)
2011-04-18 11:25:44 +00:00
static unsigned int floppy_irq ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
int sparc_floppy_request_irq ( unsigned int irq , irq_handler_t irq_handler )
2005-04-16 15:20:36 -07:00
{
unsigned int cpu_irq ;
2011-04-18 11:25:44 +00:00
int err ;
2010-10-25 05:52:38 +00:00
# if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
2005-04-16 15:20:36 -07:00
struct tt_entry * trap_table ;
# endif
2011-04-18 11:25:44 +00:00
err = request_irq ( irq , irq_handler , 0 , " floppy " , NULL ) ;
if ( err )
return - 1 ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
/* Save for later use in floppy interrupt handler */
floppy_irq = irq ;
2005-04-16 15:20:36 -07:00
2011-04-18 11:25:44 +00:00
cpu_irq = ( irq & ( NR_IRQS - 1 ) ) ;
2005-04-16 15:20:36 -07:00
/* Dork with trap table if we get this far. */
# define INSTANTIATE(table) \
table [ SP_TRAP_IRQ1 + ( cpu_irq - 1 ) ] . inst_one = SPARC_RD_PSR_L0 ; \
table [ SP_TRAP_IRQ1 + ( cpu_irq - 1 ) ] . inst_two = \
2011-04-18 11:25:44 +00:00
SPARC_BRANCH ( ( unsigned long ) floppy_hardint , \
2005-04-16 15:20:36 -07:00
( unsigned long ) & table [ SP_TRAP_IRQ1 + ( cpu_irq - 1 ) ] . inst_two ) ; \
table [ SP_TRAP_IRQ1 + ( cpu_irq - 1 ) ] . inst_three = SPARC_RD_WIM_L3 ; \
table [ SP_TRAP_IRQ1 + ( cpu_irq - 1 ) ] . inst_four = SPARC_NOP ;
INSTANTIATE ( sparc_ttable )
2010-10-25 05:52:38 +00:00
# if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
2011-01-28 22:08:24 +00:00
trap_table = & trapbase_cpu1 ;
INSTANTIATE ( trap_table )
trap_table = & trapbase_cpu2 ;
INSTANTIATE ( trap_table )
trap_table = & trapbase_cpu3 ;
INSTANTIATE ( trap_table )
2005-04-16 15:20:36 -07:00
# endif
# undef INSTANTIATE
/*
* XXX Correct thing whould be to flush only I - and D - cache lines
* which contain the handler in question . But as of time of the
* writing we have no CPU - neutral interface to fine - grained flushes .
*/
flush_cache_all ( ) ;
2011-04-18 11:25:44 +00:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2011-04-18 11:25:44 +00:00
EXPORT_SYMBOL ( sparc_floppy_request_irq ) ;
2005-04-16 15:20:36 -07:00
2011-01-28 22:08:24 +00:00
/*
* These variables are used to access state from the assembler
2007-08-02 00:19:14 -07:00
* interrupt handler , floppy_hardint , so we cannot put these in
* the floppy driver image because that would not work in the
* modular case .
*/
volatile unsigned char * fdc_status ;
EXPORT_SYMBOL ( fdc_status ) ;
char * pdma_vaddr ;
EXPORT_SYMBOL ( pdma_vaddr ) ;
unsigned long pdma_size ;
EXPORT_SYMBOL ( pdma_size ) ;
volatile int doing_pdma ;
EXPORT_SYMBOL ( doing_pdma ) ;
char * pdma_base ;
EXPORT_SYMBOL ( pdma_base ) ;
unsigned long pdma_areasize ;
EXPORT_SYMBOL ( pdma_areasize ) ;
2011-04-18 11:25:44 +00:00
/* Use the generic irq support to call floppy_interrupt
* which was setup using request_irq ( ) in sparc_floppy_request_irq ( ) .
* We only have one floppy interrupt so we do not need to check
* for additional handlers being wired up by irq_link ( )
*/
2007-08-02 00:19:14 -07:00
void sparc_floppy_irq ( int irq , void * dev_id , struct pt_regs * regs )
{
struct pt_regs * old_regs ;
old_regs = set_irq_regs ( regs ) ;
irq_enter ( ) ;
2011-04-18 11:25:44 +00:00
generic_handle_irq ( floppy_irq ) ;
2007-08-02 00:19:14 -07:00
irq_exit ( ) ;
set_irq_regs ( old_regs ) ;
}
# endif
2005-04-16 15:20:36 -07:00
/* djhr
* This could probably be made indirect too and assigned in the CPU
* bits of the code . That would be much nicer I think and would also
* fit in with the idea of being able to tune your kernel for your machine
* by removing unrequired machine and device support .
*
*/
void __init init_IRQ ( void )
{
2011-01-28 22:08:24 +00:00
switch ( sparc_cpu_model ) {
2005-04-16 15:20:36 -07:00
case sun4c :
case sun4 :
sun4c_init_IRQ ( ) ;
break ;
case sun4m :
pcic_probe ( ) ;
2011-04-17 13:49:55 +02:00
if ( pcic_present ( ) )
2005-04-16 15:20:36 -07:00
sun4m_pci_init_IRQ ( ) ;
2011-04-17 13:49:55 +02:00
else
sun4m_init_IRQ ( ) ;
2005-04-16 15:20:36 -07:00
break ;
2011-01-28 22:08:24 +00:00
2005-04-16 15:20:36 -07:00
case sun4d :
sun4d_init_IRQ ( ) ;
break ;
2009-08-17 00:13:31 +00:00
case sparc_leon :
leon_init_IRQ ( ) ;
break ;
2005-04-16 15:20:36 -07:00
default :
2007-05-11 13:51:23 -07:00
prom_printf ( " Cannot initialize IRQs on this Sun machine... " ) ;
2005-04-16 15:20:36 -07:00
break ;
}
btfixup ( ) ;
}