2005-04-16 15:20:36 -07:00
/* sun4c_irq.c
* arch / sparc / kernel / sun4c_irq . c :
*
* djhr : Hacked out of irq . c into a CPU dependent version .
*
* Copyright ( C ) 1995 David S . Miller ( davem @ caip . rutgers . edu )
* Copyright ( C ) 1995 Miguel de Icaza ( miguel @ nuclecu . unam . mx )
* Copyright ( C ) 1995 Pete A . Zaitcev ( zaitcev @ yahoo . com )
* Copyright ( C ) 1996 Dave Redman ( djhr @ tadpole . co . uk )
*/
# include <linux/errno.h>
# include <linux/linkage.h>
# include <linux/kernel_stat.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/ptrace.h>
# include <linux/interrupt.h>
# include <linux/init.h>
2008-08-27 04:05:35 -07:00
# include <linux/of.h>
# include <linux/of_device.h>
2007-07-21 19:18:57 -07:00
# include "irq.h"
2005-04-16 15:20:36 -07:00
# include <asm/ptrace.h>
# include <asm/processor.h>
# include <asm/system.h>
# include <asm/psr.h>
# include <asm/vaddrs.h>
# include <asm/timer.h>
# include <asm/openprom.h>
# include <asm/oplib.h>
# include <asm/traps.h>
# include <asm/irq.h>
# include <asm/io.h>
# include <asm/idprom.h>
# include <asm/machines.h>
2007-07-21 19:18:57 -07:00
/*
* Bit field defines for the interrupt registers on various
* Sparc machines .
*/
/* The sun4c interrupt register. */
# define SUN4C_INT_ENABLE 0x01 /* Allow interrupts. */
# define SUN4C_INT_E14 0x80 /* Enable level 14 IRQ. */
# define SUN4C_INT_E10 0x20 /* Enable level 10 IRQ. */
# define SUN4C_INT_E8 0x10 /* Enable level 8 IRQ. */
# define SUN4C_INT_E6 0x08 /* Enable level 6 IRQ. */
# define SUN4C_INT_E4 0x04 /* Enable level 4 IRQ. */
# define SUN4C_INT_E1 0x02 /* Enable level 1 IRQ. */
2005-04-16 15:20:36 -07:00
/* Pointer to the interrupt enable byte
*
* Dave Redman ( djhr @ tadpole . co . uk )
* What you may not be aware of is that entry . S requires this variable .
*
* - - - linux_trap_nmi_sun4c - -
*
* so don ' t go making it static , like I tried . sigh .
*/
2008-09-13 22:43:48 -07:00
unsigned char __iomem * interrupt_enable = NULL ;
2005-04-16 15:20:36 -07:00
static void sun4c_disable_irq ( unsigned int irq_nr )
{
unsigned long flags ;
unsigned char current_mask , new_mask ;
local_irq_save ( flags ) ;
irq_nr & = ( NR_IRQS - 1 ) ;
2008-09-13 22:43:48 -07:00
current_mask = sbus_readb ( interrupt_enable ) ;
2005-04-16 15:20:36 -07:00
switch ( irq_nr ) {
case 1 :
new_mask = ( ( current_mask ) & ( ~ ( SUN4C_INT_E1 ) ) ) ;
break ;
case 8 :
new_mask = ( ( current_mask ) & ( ~ ( SUN4C_INT_E8 ) ) ) ;
break ;
case 10 :
new_mask = ( ( current_mask ) & ( ~ ( SUN4C_INT_E10 ) ) ) ;
break ;
case 14 :
new_mask = ( ( current_mask ) & ( ~ ( SUN4C_INT_E14 ) ) ) ;
break ;
default :
local_irq_restore ( flags ) ;
return ;
}
2008-09-13 22:43:48 -07:00
sbus_writeb ( new_mask , interrupt_enable ) ;
2005-04-16 15:20:36 -07:00
local_irq_restore ( flags ) ;
}
static void sun4c_enable_irq ( unsigned int irq_nr )
{
unsigned long flags ;
unsigned char current_mask , new_mask ;
local_irq_save ( flags ) ;
irq_nr & = ( NR_IRQS - 1 ) ;
2008-09-13 22:43:48 -07:00
current_mask = sbus_readb ( interrupt_enable ) ;
2005-04-16 15:20:36 -07:00
switch ( irq_nr ) {
case 1 :
new_mask = ( ( current_mask ) | SUN4C_INT_E1 ) ;
break ;
case 8 :
new_mask = ( ( current_mask ) | SUN4C_INT_E8 ) ;
break ;
case 10 :
new_mask = ( ( current_mask ) | SUN4C_INT_E10 ) ;
break ;
case 14 :
new_mask = ( ( current_mask ) | SUN4C_INT_E14 ) ;
break ;
default :
local_irq_restore ( flags ) ;
return ;
}
2008-09-13 22:43:48 -07:00
sbus_writeb ( new_mask , interrupt_enable ) ;
2005-04-16 15:20:36 -07:00
local_irq_restore ( flags ) ;
}
2008-09-13 22:47:43 -07:00
struct sun4c_timer_info {
u32 l10_count ;
u32 l10_limit ;
u32 l14_count ;
u32 l14_limit ;
} ;
2005-04-16 15:20:36 -07:00
2008-09-13 22:47:43 -07:00
static struct sun4c_timer_info __iomem * sun4c_timers ;
2005-04-16 15:20:36 -07:00
static void sun4c_clear_clock_irq ( void )
{
2008-09-13 22:47:43 -07:00
sbus_readl ( & sun4c_timers - > l10_limit ) ;
2005-04-16 15:20:36 -07:00
}
static void sun4c_load_profile_irq ( int cpu , unsigned int limit )
{
/* Errm.. not sure how to do this.. */
}
2006-10-09 12:19:47 +01:00
static void __init sun4c_init_timers ( irq_handler_t counter_fn )
2005-04-16 15:20:36 -07:00
{
2008-09-13 22:47:43 -07:00
const struct linux_prom_irqs * irq ;
struct device_node * dp ;
const u32 * addr ;
int err ;
2005-04-16 15:20:36 -07:00
2008-09-13 22:47:43 -07:00
dp = of_find_node_by_name ( NULL , " counter-timer " ) ;
if ( ! dp ) {
prom_printf ( " sun4c_init_timers: Unable to find counter-timer \n " ) ;
prom_halt ( ) ;
}
addr = of_get_property ( dp , " address " , NULL ) ;
if ( ! addr ) {
prom_printf ( " sun4c_init_timers: No address property \n " ) ;
prom_halt ( ) ;
}
sun4c_timers = ( void __iomem * ) ( unsigned long ) addr [ 0 ] ;
irq = of_get_property ( dp , " intr " , NULL ) ;
2008-12-03 21:10:57 -08:00
of_node_put ( dp ) ;
2008-09-13 22:47:43 -07:00
if ( ! irq ) {
prom_printf ( " sun4c_init_timers: No intr property \n " ) ;
prom_halt ( ) ;
}
2005-04-16 15:20:36 -07:00
/* Have the level 10 timer tick at 100HZ. We don't touch the
* level 14 timer limit since we are letting the prom handle
* them until we have a real console driver so L1 - A works .
*/
2008-09-13 22:47:43 -07:00
sbus_writel ( ( ( ( 1000000 / HZ ) + 1 ) < < 10 ) , & sun4c_timers - > l10_limit ) ;
master_l10_counter = & sun4c_timers - > l10_count ;
2005-04-16 15:20:36 -07:00
2008-09-13 22:47:43 -07:00
err = request_irq ( irq [ 0 ] . pri , counter_fn ,
2006-07-01 19:29:26 -07:00
( IRQF_DISABLED | SA_STATIC_ALLOC ) ,
2005-04-16 15:20:36 -07:00
" timer " , NULL ) ;
2008-09-13 22:47:43 -07:00
if ( err ) {
prom_printf ( " sun4c_init_timers: request_irq() fails with %d \n " , err ) ;
2005-04-16 15:20:36 -07:00
prom_halt ( ) ;
}
2008-09-13 22:47:43 -07:00
sun4c_disable_irq ( irq [ 1 ] . pri ) ;
2005-04-16 15:20:36 -07:00
}
# ifdef CONFIG_SMP
static void sun4c_nop ( void ) { }
# endif
void __init sun4c_init_IRQ ( void )
{
2008-09-13 22:43:48 -07:00
struct device_node * dp ;
const u32 * addr ;
dp = of_find_node_by_name ( NULL , " interrupt-enable " ) ;
if ( ! dp ) {
prom_printf ( " sun4c_init_IRQ: Unable to find interrupt-enable \n " ) ;
prom_halt ( ) ;
}
addr = of_get_property ( dp , " address " , NULL ) ;
2008-12-03 21:10:57 -08:00
of_node_put ( dp ) ;
2008-09-13 22:43:48 -07:00
if ( ! addr ) {
prom_printf ( " sun4c_init_IRQ: No address property \n " ) ;
prom_halt ( ) ;
2005-04-16 15:20:36 -07:00
}
2008-09-13 22:43:48 -07:00
interrupt_enable = ( void __iomem * ) ( unsigned long ) addr [ 0 ] ;
2005-04-16 15:20:36 -07:00
BTFIXUPSET_CALL ( enable_irq , sun4c_enable_irq , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( disable_irq , sun4c_disable_irq , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( enable_pil_irq , sun4c_enable_irq , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( disable_pil_irq , sun4c_disable_irq , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( clear_clock_irq , sun4c_clear_clock_irq , BTFIXUPCALL_NORM ) ;
BTFIXUPSET_CALL ( load_profile_irq , sun4c_load_profile_irq , BTFIXUPCALL_NOP ) ;
sparc_init_timers = sun4c_init_timers ;
# ifdef CONFIG_SMP
BTFIXUPSET_CALL ( set_cpu_int , sun4c_nop , BTFIXUPCALL_NOP ) ;
BTFIXUPSET_CALL ( clear_cpu_int , sun4c_nop , BTFIXUPCALL_NOP ) ;
BTFIXUPSET_CALL ( set_irq_udt , sun4c_nop , BTFIXUPCALL_NOP ) ;
# endif
2008-09-13 22:43:48 -07:00
sbus_writeb ( SUN4C_INT_ENABLE , interrupt_enable ) ;
2005-04-16 15:20:36 -07:00
/* Cannot enable interrupts until OBP ticker is disabled. */
}