2005-04-16 15:20:36 -07:00
/*
* linux / arch / alpha / kernel / sys_eiger . c
*
* Copyright ( C ) 1995 David A Rusling
* Copyright ( C ) 1996 , 1999 Jay A Estabrook
* Copyright ( C ) 1998 , 1999 Richard Henderson
* Copyright ( C ) 1999 Iain Grant
*
* Code supporting the EIGER ( EV6 + TSUNAMI ) .
*/
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/mm.h>
# include <linux/sched.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/bitops.h>
# include <asm/ptrace.h>
# include <asm/system.h>
# include <asm/dma.h>
# include <asm/irq.h>
# include <asm/mmu_context.h>
# include <asm/io.h>
# include <asm/pci.h>
# include <asm/pgtable.h>
# include <asm/core_tsunami.h>
# include <asm/hwrpb.h>
# include <asm/tlbflush.h>
# include "proto.h"
# include "irq_impl.h"
# include "pci_impl.h"
# include "machvec_impl.h"
/* Note that this interrupt code is identical to TAKARA. */
/* Note mask bit is true for DISABLED irqs. */
static unsigned long cached_irq_mask [ 2 ] = { - 1 , - 1 } ;
static inline void
eiger_update_irq_hw ( unsigned long irq , unsigned long mask )
{
int regaddr ;
mask = ( irq > = 64 ? mask < < 16 : mask > > ( ( irq - 16 ) & 0x30 ) ) ;
regaddr = 0x510 + ( ( ( irq - 16 ) > > 2 ) & 0x0c ) ;
outl ( mask & 0xffff0000UL , regaddr ) ;
}
static inline void
2011-02-06 14:32:37 +00:00
eiger_enable_irq ( struct irq_data * d )
2005-04-16 15:20:36 -07:00
{
2011-02-06 14:32:37 +00:00
unsigned int irq = d - > irq ;
2005-04-16 15:20:36 -07:00
unsigned long mask ;
mask = ( cached_irq_mask [ irq > = 64 ] & = ~ ( 1UL < < ( irq & 63 ) ) ) ;
eiger_update_irq_hw ( irq , mask ) ;
}
static void
2011-02-06 14:32:37 +00:00
eiger_disable_irq ( struct irq_data * d )
2005-04-16 15:20:36 -07:00
{
2011-02-06 14:32:37 +00:00
unsigned int irq = d - > irq ;
2005-04-16 15:20:36 -07:00
unsigned long mask ;
mask = ( cached_irq_mask [ irq > = 64 ] | = 1UL < < ( irq & 63 ) ) ;
eiger_update_irq_hw ( irq , mask ) ;
}
2009-06-16 15:33:25 -07:00
static struct irq_chip eiger_irq_type = {
2009-11-30 22:51:31 -05:00
. name = " EIGER " ,
2011-02-06 14:32:37 +00:00
. irq_unmask = eiger_enable_irq ,
. irq_mask = eiger_disable_irq ,
. irq_mask_ack = eiger_disable_irq ,
2005-04-16 15:20:36 -07:00
} ;
static void
2006-10-08 14:36:08 +01:00
eiger_device_interrupt ( unsigned long vector )
2005-04-16 15:20:36 -07:00
{
unsigned intstatus ;
/*
* The PALcode will have passed us vectors 0x800 or 0x810 ,
* which are fairly arbitrary values and serve only to tell
* us whether an interrupt has come in on IRQ0 or IRQ1 . If
* it ' s IRQ1 it ' s a PCI interrupt ; if it ' s IRQ0 , it ' s
* probably ISA , but PCI interrupts can come through IRQ0
* as well if the interrupt controller isn ' t in accelerated
* mode .
*
* OTOH , the accelerator thing doesn ' t seem to be working
* overly well , so what we ' ll do instead is try directly
* examining the Master Interrupt Register to see if it ' s a
* PCI interrupt , and if _not_ then we ' ll pass it on to the
* ISA handler .
*/
intstatus = inw ( 0x500 ) & 15 ;
if ( intstatus ) {
/*
* This is a PCI interrupt . Check each bit and
* despatch an interrupt if it ' s set .
*/
2006-10-08 14:37:32 +01:00
if ( intstatus & 8 ) handle_irq ( 16 + 3 ) ;
if ( intstatus & 4 ) handle_irq ( 16 + 2 ) ;
if ( intstatus & 2 ) handle_irq ( 16 + 1 ) ;
if ( intstatus & 1 ) handle_irq ( 16 + 0 ) ;
2005-04-16 15:20:36 -07:00
} else {
2006-10-08 14:36:08 +01:00
isa_device_interrupt ( vector ) ;
2005-04-16 15:20:36 -07:00
}
}
static void
2006-10-08 14:36:08 +01:00
eiger_srm_device_interrupt ( unsigned long vector )
2005-04-16 15:20:36 -07:00
{
int irq = ( vector - 0x800 ) > > 4 ;
2006-10-08 14:37:32 +01:00
handle_irq ( irq ) ;
2005-04-16 15:20:36 -07:00
}
static void __init
eiger_init_irq ( void )
{
long i ;
outb ( 0 , DMA1_RESET_REG ) ;
outb ( 0 , DMA2_RESET_REG ) ;
outb ( DMA_MODE_CASCADE , DMA2_MODE_REG ) ;
outb ( 0 , DMA2_MASK_REG ) ;
if ( alpha_using_srm )
alpha_mv . device_interrupt = eiger_srm_device_interrupt ;
for ( i = 16 ; i < 128 ; i + = 16 )
eiger_update_irq_hw ( i , - 1 ) ;
init_i8259a_irqs ( ) ;
for ( i = 16 ; i < 128 ; + + i ) {
2011-03-25 22:17:31 +01:00
irq_set_chip_and_handler ( i , & eiger_irq_type , handle_level_irq ) ;
2011-02-06 14:32:37 +00:00
irq_set_status_flags ( i , IRQ_LEVEL ) ;
2005-04-16 15:20:36 -07:00
}
}
static int __init
eiger_map_irq ( struct pci_dev * dev , u8 slot , u8 pin )
{
u8 irq_orig ;
/* The SRM console has already calculated out the IRQ value's for
option cards . As this works lets just read in the value already
set and change it to a useable value by Linux .
All the IRQ values generated by the console are greater than 90 ,
so we subtract 80 because it is ( 90 - allocated ISA IRQ ' s ) . */
pci_read_config_byte ( dev , PCI_INTERRUPT_LINE , & irq_orig ) ;
return irq_orig - 0x80 ;
}
static u8 __init
eiger_swizzle ( struct pci_dev * dev , u8 * pinp )
{
struct pci_controller * hose = dev - > sysdata ;
int slot , pin = * pinp ;
int bridge_count = 0 ;
/* Find the number of backplane bridges. */
int backplane = inw ( 0x502 ) & 0x0f ;
switch ( backplane )
{
case 0x00 : bridge_count = 0 ; break ; /* No bridges */
case 0x01 : bridge_count = 1 ; break ; /* 1 */
case 0x03 : bridge_count = 2 ; break ; /* 2 */
case 0x07 : bridge_count = 3 ; break ; /* 3 */
case 0x0f : bridge_count = 4 ; break ; /* 4 */
} ;
slot = PCI_SLOT ( dev - > devfn ) ;
while ( dev - > bus - > self ) {
/* Check for built-in bridges on hose 0. */
if ( hose - > index = = 0
& & ( PCI_SLOT ( dev - > bus - > self - > devfn )
> 20 - bridge_count ) ) {
slot = PCI_SLOT ( dev - > devfn ) ;
break ;
}
/* Must be a card-based bridge. */
2008-12-09 16:12:07 -07:00
pin = pci_swizzle_interrupt_pin ( dev , pin ) ;
2005-04-16 15:20:36 -07:00
/* Move up the chain of bridges. */
dev = dev - > bus - > self ;
}
* pinp = pin ;
return slot ;
}
/*
* The System Vectors
*/
struct alpha_machine_vector eiger_mv __initmv = {
. vector_name = " Eiger " ,
DO_EV6_MMU ,
DO_DEFAULT_RTC ,
DO_TSUNAMI_IO ,
. machine_check = tsunami_machine_check ,
. max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS ,
. min_io_address = DEFAULT_IO_BASE ,
. min_mem_address = DEFAULT_MEM_BASE ,
. pci_dac_offset = TSUNAMI_DAC_OFFSET ,
. nr_irqs = 128 ,
. device_interrupt = eiger_device_interrupt ,
. init_arch = tsunami_init_arch ,
. init_irq = eiger_init_irq ,
. init_rtc = common_init_rtc ,
. init_pci = common_init_pci ,
. kill_arch = tsunami_kill_arch ,
. pci_map_irq = eiger_map_irq ,
. pci_swizzle = eiger_swizzle ,
} ;
ALIAS_MV ( eiger )