2005-04-17 02:20:36 +04:00
/*
* Interrupt management for most GSC and related devices .
*
* ( c ) Copyright 1999 Alex deVries for The Puffin Group
* ( c ) Copyright 1999 Grant Grundler for Hewlett - Packard
* ( c ) Copyright 1999 Matthew Wilcox
* ( c ) Copyright 2000 Helge Deller
* ( c ) Copyright 2001 Matthew Wilcox for Hewlett - Packard
*
* 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/bitops.h>
# include <linux/config.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <asm/hardware.h>
# include <asm/io.h>
# include "gsc.h"
# undef DEBUG
# ifdef DEBUG
# define DEBPRINTK printk
# else
# define DEBPRINTK(x,...)
# endif
int gsc_alloc_irq ( struct gsc_irq * i )
{
int irq = txn_alloc_irq ( GSC_EIM_WIDTH ) ;
if ( irq < 0 ) {
printk ( " cannot get irq \n " ) ;
return irq ;
}
i - > txn_addr = txn_alloc_addr ( irq ) ;
i - > txn_data = txn_alloc_data ( irq ) ;
i - > irq = irq ;
return irq ;
}
int gsc_claim_irq ( struct gsc_irq * i , int irq )
{
int c = irq ;
irq + = CPU_IRQ_BASE ; /* virtualize the IRQ first */
irq = txn_claim_irq ( irq ) ;
if ( irq < 0 ) {
printk ( " cannot claim irq %d \n " , c ) ;
return irq ;
}
i - > txn_addr = txn_alloc_addr ( irq ) ;
i - > txn_data = txn_alloc_data ( irq ) ;
i - > irq = irq ;
return irq ;
}
EXPORT_SYMBOL ( gsc_alloc_irq ) ;
EXPORT_SYMBOL ( gsc_claim_irq ) ;
/* Common interrupt demultiplexer used by Asp, Lasi & Wax. */
irqreturn_t gsc_asic_intr ( int gsc_asic_irq , void * dev , struct pt_regs * regs )
{
unsigned long irr ;
struct gsc_asic * gsc_asic = dev ;
irr = gsc_readl ( gsc_asic - > hpa + OFFSET_IRR ) ;
if ( irr = = 0 )
return IRQ_NONE ;
DEBPRINTK ( " %s intr, mask=0x%x \n " , gsc_asic - > name , irr ) ;
do {
int local_irq = __ffs ( irr ) ;
unsigned int irq = gsc_asic - > global_irq [ local_irq ] ;
__do_IRQ ( irq , regs ) ;
irr & = ~ ( 1 < < local_irq ) ;
} while ( irr ) ;
return IRQ_HANDLED ;
}
int gsc_find_local_irq ( unsigned int irq , int * global_irqs , int limit )
{
int local_irq ;
for ( local_irq = 0 ; local_irq < limit ; local_irq + + ) {
if ( global_irqs [ local_irq ] = = irq )
return local_irq ;
}
return NO_IRQ ;
}
static void gsc_asic_disable_irq ( unsigned int irq )
{
struct gsc_asic * irq_dev = irq_desc [ irq ] . handler_data ;
int local_irq = gsc_find_local_irq ( irq , irq_dev - > global_irq , 32 ) ;
u32 imr ;
DEBPRINTK ( KERN_DEBUG " %s(%d) %s: IMR 0x%x \n " , __FUNCTION__ , irq ,
irq_dev - > name , imr ) ;
/* Disable the IRQ line by clearing the bit in the IMR */
imr = gsc_readl ( irq_dev - > hpa + OFFSET_IMR ) ;
imr & = ~ ( 1 < < local_irq ) ;
gsc_writel ( imr , irq_dev - > hpa + OFFSET_IMR ) ;
}
static void gsc_asic_enable_irq ( unsigned int irq )
{
struct gsc_asic * irq_dev = irq_desc [ irq ] . handler_data ;
int local_irq = gsc_find_local_irq ( irq , irq_dev - > global_irq , 32 ) ;
u32 imr ;
DEBPRINTK ( KERN_DEBUG " %s(%d) %s: IMR 0x%x \n " , __FUNCTION__ , irq ,
irq_dev - > name , imr ) ;
/* Enable the IRQ line by setting the bit in the IMR */
imr = gsc_readl ( irq_dev - > hpa + OFFSET_IMR ) ;
imr | = 1 < < local_irq ;
gsc_writel ( imr , irq_dev - > hpa + OFFSET_IMR ) ;
/*
* FIXME : read IPR to make sure the IRQ isn ' t already pending .
* If so , we need to read IRR and manually call do_irq ( ) .
*/
}
static unsigned int gsc_asic_startup_irq ( unsigned int irq )
{
gsc_asic_enable_irq ( irq ) ;
return 0 ;
}
static struct hw_interrupt_type gsc_asic_interrupt_type = {
. typename = " GSC-ASIC " ,
. startup = gsc_asic_startup_irq ,
. shutdown = gsc_asic_disable_irq ,
. enable = gsc_asic_enable_irq ,
. disable = gsc_asic_disable_irq ,
. ack = no_ack_irq ,
. end = no_end_irq ,
} ;
int gsc_assign_irq ( struct hw_interrupt_type * type , void * data )
{
static int irq = GSC_IRQ_BASE ;
if ( irq > GSC_IRQ_MAX )
return NO_IRQ ;
irq_desc [ irq ] . handler = type ;
irq_desc [ irq ] . handler_data = data ;
return irq + + ;
}
void gsc_asic_assign_irq ( struct gsc_asic * asic , int local_irq , int * irqp )
{
int irq = asic - > global_irq [ local_irq ] ;
if ( irq < = 0 ) {
irq = gsc_assign_irq ( & gsc_asic_interrupt_type , asic ) ;
if ( irq = = NO_IRQ )
return ;
asic - > global_irq [ local_irq ] = irq ;
}
* irqp = irq ;
}
2005-10-22 06:33:38 +04:00
static struct device * next_device ( struct klist_iter * i )
{
struct klist_node * n = klist_next ( i ) ;
return n ? container_of ( n , struct device , knode_parent ) : NULL ;
}
2005-04-17 02:20:36 +04:00
void gsc_fixup_irqs ( struct parisc_device * parent , void * ctrl ,
void ( * choose_irq ) ( struct parisc_device * , void * ) )
{
struct device * dev ;
2005-10-22 06:33:38 +04:00
struct klist_iter i ;
2005-04-17 02:20:36 +04:00
2005-10-22 06:33:38 +04:00
klist_iter_init ( & parent - > dev . klist_children , & i ) ;
while ( ( dev = next_device ( & i ) ) ) {
2005-04-17 02:20:36 +04:00
struct parisc_device * padev = to_parisc_device ( dev ) ;
/* work-around for 715/64 and others which have parent
at path [ 5 ] and children at path [ 5 / 0 / x ] */
if ( padev - > id . hw_type = = HPHW_FAULTY )
return gsc_fixup_irqs ( padev , ctrl , choose_irq ) ;
choose_irq ( padev , ctrl ) ;
}
2005-10-22 06:33:38 +04:00
klist_iter_exit ( & i ) ;
2005-04-17 02:20:36 +04:00
}
int gsc_common_setup ( struct parisc_device * parent , struct gsc_asic * gsc_asic )
{
struct resource * res ;
int i ;
gsc_asic - > gsc = parent ;
/* Initialise local irq -> global irq mapping */
for ( i = 0 ; i < 32 ; i + + ) {
gsc_asic - > global_irq [ i ] = NO_IRQ ;
}
/* allocate resource region */
res = request_mem_region ( gsc_asic - > hpa , 0x100000 , gsc_asic - > name ) ;
if ( res ) {
res - > flags = IORESOURCE_MEM ; /* do not mark it busy ! */
}
#if 0
printk ( KERN_WARNING " %s IRQ %d EIM 0x%x " , gsc_asic - > name ,
parent - > irq , gsc_asic - > eim ) ;
if ( gsc_readl ( gsc_asic - > hpa + OFFSET_IMR ) )
printk ( " IMR is non-zero! (0x%x) " ,
gsc_readl ( gsc_asic - > hpa + OFFSET_IMR ) ) ;
printk ( " \n " ) ;
# endif
return 0 ;
}
extern struct parisc_driver lasi_driver ;
extern struct parisc_driver asp_driver ;
extern struct parisc_driver wax_driver ;
void __init gsc_init ( void )
{
# ifdef CONFIG_GSC_LASI
register_parisc_driver ( & lasi_driver ) ;
register_parisc_driver ( & asp_driver ) ;
# endif
# ifdef CONFIG_GSC_WAX
register_parisc_driver ( & wax_driver ) ;
# endif
}