2005-04-17 02:20:36 +04:00
/*
2005-11-01 22:44:27 +03:00
* arch / arm / mach - ixp2000 / core . c
2005-04-17 02:20:36 +04:00
*
* Common routines used by all IXP2400 / 2800 based platforms .
*
* Author : Deepak Saxena < dsaxena @ plexity . net >
*
* Copyright 2004 ( C ) MontaVista Software , Inc .
*
* Based on work Copyright ( C ) 2002 - 2003 Intel Corporation
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/sched.h>
# include <linux/interrupt.h>
2006-07-02 01:32:18 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# include <linux/serial.h>
# include <linux/tty.h>
# include <linux/bitops.h>
2005-07-10 22:44:53 +04:00
# include <linux/serial_8250.h>
2005-04-17 02:20:36 +04:00
# include <linux/mm.h>
# include <asm/types.h>
# include <asm/setup.h>
# include <asm/memory.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <asm/system.h>
# include <asm/tlbflush.h>
# include <asm/pgtable.h>
# include <asm/mach/map.h>
# include <asm/mach/time.h>
# include <asm/mach/irq.h>
2008-08-05 19:14:15 +04:00
# include <mach/gpio.h>
2005-06-24 23:54:35 +04:00
2005-04-17 02:20:36 +04:00
static DEFINE_SPINLOCK ( ixp2000_slowport_lock ) ;
static unsigned long ixp2000_slowport_irq_flags ;
/*************************************************************************
* Slowport access routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void ixp2000_acquire_slowport ( struct slowport_cfg * new_cfg , struct slowport_cfg * old_cfg )
{
spin_lock_irqsave ( & ixp2000_slowport_lock , ixp2000_slowport_irq_flags ) ;
old_cfg - > CCR = * IXP2000_SLOWPORT_CCR ;
old_cfg - > WTC = * IXP2000_SLOWPORT_WTC2 ;
old_cfg - > RTC = * IXP2000_SLOWPORT_RTC2 ;
old_cfg - > PCR = * IXP2000_SLOWPORT_PCR ;
old_cfg - > ADC = * IXP2000_SLOWPORT_ADC ;
ixp2000_reg_write ( IXP2000_SLOWPORT_CCR , new_cfg - > CCR ) ;
ixp2000_reg_write ( IXP2000_SLOWPORT_WTC2 , new_cfg - > WTC ) ;
ixp2000_reg_write ( IXP2000_SLOWPORT_RTC2 , new_cfg - > RTC ) ;
ixp2000_reg_write ( IXP2000_SLOWPORT_PCR , new_cfg - > PCR ) ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_SLOWPORT_ADC , new_cfg - > ADC ) ;
2005-04-17 02:20:36 +04:00
}
void ixp2000_release_slowport ( struct slowport_cfg * old_cfg )
{
ixp2000_reg_write ( IXP2000_SLOWPORT_CCR , old_cfg - > CCR ) ;
ixp2000_reg_write ( IXP2000_SLOWPORT_WTC2 , old_cfg - > WTC ) ;
ixp2000_reg_write ( IXP2000_SLOWPORT_RTC2 , old_cfg - > RTC ) ;
ixp2000_reg_write ( IXP2000_SLOWPORT_PCR , old_cfg - > PCR ) ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_SLOWPORT_ADC , old_cfg - > ADC ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & ixp2000_slowport_lock ,
ixp2000_slowport_irq_flags ) ;
}
/*************************************************************************
* Chip specific mappings shared by all IXP2000 systems
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct map_desc ixp2000_io_desc [ ] __initdata = {
{
. virtual = IXP2000_CAP_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_CAP_PHYS_BASE ) ,
2005-04-17 02:20:36 +04:00
. length = IXP2000_CAP_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-04-17 02:20:36 +04:00
} , {
. virtual = IXP2000_INTCTL_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_INTCTL_PHYS_BASE ) ,
2005-04-17 02:20:36 +04:00
. length = IXP2000_INTCTL_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-04-17 02:20:36 +04:00
} , {
. virtual = IXP2000_PCI_CREG_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_PCI_CREG_PHYS_BASE ) ,
2005-04-17 02:20:36 +04:00
. length = IXP2000_PCI_CREG_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-04-17 02:20:36 +04:00
} , {
. virtual = IXP2000_PCI_CSR_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_PCI_CSR_PHYS_BASE ) ,
2005-04-17 02:20:36 +04:00
. length = IXP2000_PCI_CSR_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-06-27 01:24:17 +04:00
} , {
. virtual = IXP2000_MSF_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_MSF_PHYS_BASE ) ,
2005-06-27 01:24:17 +04:00
. length = IXP2000_MSF_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2006-01-13 23:51:43 +03:00
} , {
. virtual = IXP2000_SCRATCH_RING_VIRT_BASE ,
. pfn = __phys_to_pfn ( IXP2000_SCRATCH_RING_PHYS_BASE ) ,
. length = IXP2000_SCRATCH_RING_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2006-01-13 23:51:43 +03:00
} , {
. virtual = IXP2000_SRAM0_VIRT_BASE ,
. pfn = __phys_to_pfn ( IXP2000_SRAM0_PHYS_BASE ) ,
. length = IXP2000_SRAM0_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-04-17 02:20:36 +04:00
} , {
. virtual = IXP2000_PCI_IO_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_PCI_IO_PHYS_BASE ) ,
2005-04-17 02:20:36 +04:00
. length = IXP2000_PCI_IO_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-04-17 02:20:36 +04:00
} , {
. virtual = IXP2000_PCI_CFG0_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_PCI_CFG0_PHYS_BASE ) ,
2005-04-17 02:20:36 +04:00
. length = IXP2000_PCI_CFG0_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-04-17 02:20:36 +04:00
} , {
. virtual = IXP2000_PCI_CFG1_VIRT_BASE ,
2005-10-28 18:18:58 +04:00
. pfn = __phys_to_pfn ( IXP2000_PCI_CFG1_PHYS_BASE ) ,
2005-04-17 02:20:36 +04:00
. length = IXP2000_PCI_CFG1_SIZE ,
2008-09-07 15:42:51 +04:00
. type = MT_DEVICE ,
2005-04-17 02:20:36 +04:00
}
} ;
void __init ixp2000_map_io ( void )
{
iotable_init ( ixp2000_io_desc , ARRAY_SIZE ( ixp2000_io_desc ) ) ;
/* Set slowport to 8-bit mode. */
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_SLOWPORT_FRM , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2005-07-10 22:44:53 +04:00
/*************************************************************************
* Serial port support for IXP2000
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct plat_serial8250_port ixp2000_serial_port [ ] = {
{
. mapbase = IXP2000_UART_PHYS_BASE ,
. membase = ( char * ) ( IXP2000_UART_VIRT_BASE + 3 ) ,
. irq = IRQ_IXP2000_UART ,
. flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST ,
. iotype = UPIO_MEM ,
. regshift = 2 ,
. uartclk = 50000000 ,
} ,
{ } ,
} ;
static struct resource ixp2000_uart_resource = {
. start = IXP2000_UART_PHYS_BASE ,
2005-10-01 03:20:22 +04:00
. end = IXP2000_UART_PHYS_BASE + 0x1f ,
2005-07-10 22:44:53 +04:00
. flags = IORESOURCE_MEM ,
} ;
static struct platform_device ixp2000_serial_device = {
. name = " serial8250 " ,
2005-09-08 19:04:41 +04:00
. id = PLAT8250_DEV_PLATFORM ,
2005-07-10 22:44:53 +04:00
. dev = {
. platform_data = ixp2000_serial_port ,
} ,
. num_resources = 1 ,
. resource = & ixp2000_uart_resource ,
} ;
void __init ixp2000_uart_init ( void )
{
platform_device_register ( & ixp2000_serial_device ) ;
}
2005-04-17 02:20:36 +04:00
/*************************************************************************
* Timer - tick functions for IXP2000
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static unsigned ticks_per_jiffy ;
static unsigned ticks_per_usec ;
static unsigned next_jiffy_time ;
2005-06-20 21:51:07 +04:00
static volatile unsigned long * missing_jiffy_timer_csr ;
2005-04-17 02:20:36 +04:00
unsigned long ixp2000_gettimeoffset ( void )
{
unsigned long offset ;
2005-06-20 21:51:07 +04:00
offset = next_jiffy_time - * missing_jiffy_timer_csr ;
2005-04-17 02:20:36 +04:00
return offset / ticks_per_usec ;
}
2010-10-19 12:43:59 +04:00
static irqreturn_t ixp2000_timer_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
/* clear timer 1 */
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_T1_CLR , 1 ) ;
2005-06-24 23:54:35 +04:00
2006-06-22 13:30:53 +04:00
while ( ( signed long ) ( next_jiffy_time - * missing_jiffy_timer_csr )
> = ticks_per_jiffy ) {
2006-10-06 21:53:39 +04:00
timer_tick ( ) ;
2005-04-17 02:20:36 +04:00
next_jiffy_time - = ticks_per_jiffy ;
}
return IRQ_HANDLED ;
}
static struct irqaction ixp2000_timer_irq = {
. name = " IXP2000 Timer Tick " ,
2007-05-08 11:35:39 +04:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2005-06-26 20:06:36 +04:00
. handler = ixp2000_timer_interrupt ,
2005-04-17 02:20:36 +04:00
} ;
void __init ixp2000_init_time ( unsigned long tick_rate )
{
ticks_per_jiffy = ( tick_rate + HZ / 2 ) / HZ ;
ticks_per_usec = tick_rate / 1000000 ;
2005-06-20 21:51:07 +04:00
/*
* We use timer 1 as our timer interrupt .
*/
ixp2000_reg_write ( IXP2000_T1_CLR , 0 ) ;
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_T1_CLD , ticks_per_jiffy - 1 ) ;
ixp2000_reg_write ( IXP2000_T1_CTL , ( 1 < < 7 ) ) ;
/*
2005-06-20 21:51:07 +04:00
* We use a second timer as a monotonic counter for tracking
* missed jiffies . The IXP2000 has four timers , but if we ' re
* on an A - step IXP2800 , timer 2 and 3 don ' t work , so on those
* chips we use timer 4. Timer 4 is the only timer that can
* be used for the watchdog , so we use timer 2 if we ' re on a
* non - buggy chip .
2005-04-17 02:20:36 +04:00
*/
2005-06-20 21:51:07 +04:00
if ( ( * IXP2000_PRODUCT_ID & 0x001ffef0 ) = = 0x00000000 ) {
printk ( KERN_INFO " Enabling IXP2800 erratum #25 workaround \n " ) ;
ixp2000_reg_write ( IXP2000_T4_CLR , 0 ) ;
ixp2000_reg_write ( IXP2000_T4_CLD , - 1 ) ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_T4_CTL , ( 1 < < 7 ) ) ;
2005-06-20 21:51:07 +04:00
missing_jiffy_timer_csr = IXP2000_T4_CSR ;
} else {
ixp2000_reg_write ( IXP2000_T2_CLR , 0 ) ;
ixp2000_reg_write ( IXP2000_T2_CLD , - 1 ) ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_T2_CTL , ( 1 < < 7 ) ) ;
2005-06-20 21:51:07 +04:00
missing_jiffy_timer_csr = IXP2000_T2_CSR ;
}
2005-04-17 02:20:36 +04:00
next_jiffy_time = 0xffffffff ;
/* register for interrupt */
setup_irq ( IRQ_IXP2000_TIMER1 , & ixp2000_timer_irq ) ;
}
/*************************************************************************
* GPIO helpers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static unsigned long GPIO_IRQ_falling_edge ;
2005-06-24 23:54:35 +04:00
static unsigned long GPIO_IRQ_rising_edge ;
2005-04-17 02:20:36 +04:00
static unsigned long GPIO_IRQ_level_low ;
static unsigned long GPIO_IRQ_level_high ;
2005-06-24 23:54:35 +04:00
static void update_gpio_int_csrs ( void )
{
ixp2000_reg_write ( IXP2000_GPIO_FEDR , GPIO_IRQ_falling_edge ) ;
ixp2000_reg_write ( IXP2000_GPIO_REDR , GPIO_IRQ_rising_edge ) ;
ixp2000_reg_write ( IXP2000_GPIO_LSLR , GPIO_IRQ_level_low ) ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_GPIO_LSHR , GPIO_IRQ_level_high ) ;
2005-06-24 23:54:35 +04:00
}
void gpio_line_config ( int line , int direction )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
local_irq_save ( flags ) ;
2005-06-24 23:54:35 +04:00
if ( direction = = GPIO_OUT ) {
2005-04-17 02:20:36 +04:00
/* if it's an output, it ain't an interrupt anymore */
GPIO_IRQ_falling_edge & = ~ ( 1 < < line ) ;
GPIO_IRQ_rising_edge & = ~ ( 1 < < line ) ;
GPIO_IRQ_level_low & = ~ ( 1 < < line ) ;
GPIO_IRQ_level_high & = ~ ( 1 < < line ) ;
2005-06-24 23:54:35 +04:00
update_gpio_int_csrs ( ) ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_GPIO_PDSR , 1 < < line ) ;
2005-06-24 23:54:35 +04:00
} else if ( direction = = GPIO_IN ) {
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_GPIO_PDCR , 1 < < line ) ;
2005-04-17 02:20:36 +04:00
}
local_irq_restore ( flags ) ;
2005-06-24 23:54:35 +04:00
}
2006-06-24 12:57:14 +04:00
EXPORT_SYMBOL ( gpio_line_config ) ;
2005-04-17 02:20:36 +04:00
/*************************************************************************
* IRQ handling IXP2000
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-11-23 14:41:32 +03:00
static void ixp2000_GPIO_irq_handler ( unsigned int irq , struct irq_desc * desc )
2005-04-17 02:20:36 +04:00
{
int i ;
unsigned long status = * IXP2000_GPIO_INST ;
for ( i = 0 ; i < = 7 ; i + + ) {
if ( status & ( 1 < < i ) ) {
2008-10-09 16:36:24 +04:00
generic_handle_irq ( i + IRQ_IXP2000_GPIO0 ) ;
2005-04-17 02:20:36 +04:00
}
}
}
2010-11-29 12:32:55 +03:00
static int ixp2000_GPIO_irq_type ( struct irq_data * d , unsigned int type )
2005-06-24 23:54:35 +04:00
{
2010-11-29 12:32:55 +03:00
int line = d - > irq - IRQ_IXP2000_GPIO0 ;
2005-06-24 23:54:35 +04:00
/*
* First , configure this GPIO line as an input .
*/
ixp2000_reg_write ( IXP2000_GPIO_PDCR , 1 < < line ) ;
/*
* Then , set the proper trigger type .
*/
2008-07-27 07:23:31 +04:00
if ( type & IRQ_TYPE_EDGE_FALLING )
2005-06-24 23:54:35 +04:00
GPIO_IRQ_falling_edge | = 1 < < line ;
else
GPIO_IRQ_falling_edge & = ~ ( 1 < < line ) ;
2008-07-27 07:23:31 +04:00
if ( type & IRQ_TYPE_EDGE_RISING )
2005-06-24 23:54:35 +04:00
GPIO_IRQ_rising_edge | = 1 < < line ;
else
GPIO_IRQ_rising_edge & = ~ ( 1 < < line ) ;
2008-07-27 07:23:31 +04:00
if ( type & IRQ_TYPE_LEVEL_LOW )
2005-06-24 23:54:35 +04:00
GPIO_IRQ_level_low | = 1 < < line ;
else
GPIO_IRQ_level_low & = ~ ( 1 < < line ) ;
2008-07-27 07:23:31 +04:00
if ( type & IRQ_TYPE_LEVEL_HIGH )
2005-06-24 23:54:35 +04:00
GPIO_IRQ_level_high | = 1 < < line ;
else
GPIO_IRQ_level_high & = ~ ( 1 < < line ) ;
update_gpio_int_csrs ( ) ;
return 0 ;
}
2010-11-29 12:32:55 +03:00
static void ixp2000_GPIO_irq_mask_ack ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 12:32:55 +03:00
unsigned int irq = d - > irq ;
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_GPIO_INCR , ( 1 < < ( irq - IRQ_IXP2000_GPIO0 ) ) ) ;
2005-06-24 23:54:35 +04:00
ixp2000_reg_write ( IXP2000_GPIO_EDSR , ( 1 < < ( irq - IRQ_IXP2000_GPIO0 ) ) ) ;
ixp2000_reg_write ( IXP2000_GPIO_LDSR , ( 1 < < ( irq - IRQ_IXP2000_GPIO0 ) ) ) ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_GPIO_INST , ( 1 < < ( irq - IRQ_IXP2000_GPIO0 ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-29 12:32:55 +03:00
static void ixp2000_GPIO_irq_mask ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 12:32:55 +03:00
unsigned int irq = d - > irq ;
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_GPIO_INCR , ( 1 < < ( irq - IRQ_IXP2000_GPIO0 ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-29 12:32:55 +03:00
static void ixp2000_GPIO_irq_unmask ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 12:32:55 +03:00
unsigned int irq = d - > irq ;
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_GPIO_INSR , ( 1 < < ( irq - IRQ_IXP2000_GPIO0 ) ) ) ;
}
2006-11-23 14:41:32 +03:00
static struct irq_chip ixp2000_GPIO_irq_chip = {
2010-11-29 12:32:55 +03:00
. irq_ack = ixp2000_GPIO_irq_mask_ack ,
. irq_mask = ixp2000_GPIO_irq_mask ,
. irq_unmask = ixp2000_GPIO_irq_unmask ,
. irq_set_type = ixp2000_GPIO_irq_type ,
2005-04-17 02:20:36 +04:00
} ;
2010-11-29 12:32:55 +03:00
static void ixp2000_pci_irq_mask ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
unsigned long temp = * IXP2000_PCI_XSCALE_INT_ENABLE ;
2010-11-29 12:32:55 +03:00
if ( d - > irq = = IRQ_IXP2000_PCIA )
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_PCI_XSCALE_INT_ENABLE , ( temp & ~ ( 1 < < 26 ) ) ) ;
2010-11-29 12:32:55 +03:00
else if ( d - > irq = = IRQ_IXP2000_PCIB )
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_PCI_XSCALE_INT_ENABLE , ( temp & ~ ( 1 < < 27 ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-29 12:32:55 +03:00
static void ixp2000_pci_irq_unmask ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
unsigned long temp = * IXP2000_PCI_XSCALE_INT_ENABLE ;
2010-11-29 12:32:55 +03:00
if ( d - > irq = = IRQ_IXP2000_PCIA )
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_PCI_XSCALE_INT_ENABLE , ( temp | ( 1 < < 26 ) ) ) ;
2010-11-29 12:32:55 +03:00
else if ( d - > irq = = IRQ_IXP2000_PCIB )
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_PCI_XSCALE_INT_ENABLE , ( temp | ( 1 < < 27 ) ) ) ;
}
2005-11-04 20:15:44 +03:00
/*
* Error interrupts . These are used extensively by the microengine drivers
*/
2006-11-23 14:41:32 +03:00
static void ixp2000_err_irq_handler ( unsigned int irq , struct irq_desc * desc )
2005-11-04 20:15:44 +03:00
{
int i ;
unsigned long status = * IXP2000_IRQ_ERR_STATUS ;
for ( i = 31 ; i > = 0 ; i - - ) {
if ( status & ( 1 < < i ) ) {
2008-10-09 16:36:24 +04:00
generic_handle_irq ( IRQ_IXP2000_DRAM0_MIN_ERR + i ) ;
2005-11-04 20:15:44 +03:00
}
}
}
2010-11-29 12:32:55 +03:00
static void ixp2000_err_irq_mask ( struct irq_data * d )
2005-11-04 20:15:44 +03:00
{
ixp2000_reg_write ( IXP2000_IRQ_ERR_ENABLE_CLR ,
2010-11-29 12:32:55 +03:00
( 1 < < ( d - > irq - IRQ_IXP2000_DRAM0_MIN_ERR ) ) ) ;
2005-11-04 20:15:44 +03:00
}
2010-11-29 12:32:55 +03:00
static void ixp2000_err_irq_unmask ( struct irq_data * d )
2005-11-04 20:15:44 +03:00
{
ixp2000_reg_write ( IXP2000_IRQ_ERR_ENABLE_SET ,
2010-11-29 12:32:55 +03:00
( 1 < < ( d - > irq - IRQ_IXP2000_DRAM0_MIN_ERR ) ) ) ;
2005-11-04 20:15:44 +03:00
}
2006-11-23 14:41:32 +03:00
static struct irq_chip ixp2000_err_irq_chip = {
2010-11-29 12:32:55 +03:00
. irq_ack = ixp2000_err_irq_mask ,
. irq_mask = ixp2000_err_irq_mask ,
. irq_unmask = ixp2000_err_irq_unmask
2005-11-04 20:15:44 +03:00
} ;
2006-11-23 14:41:32 +03:00
static struct irq_chip ixp2000_pci_irq_chip = {
2010-11-29 12:32:55 +03:00
. irq_ack = ixp2000_pci_irq_mask ,
. irq_mask = ixp2000_pci_irq_mask ,
. irq_unmask = ixp2000_pci_irq_unmask
2005-04-17 02:20:36 +04:00
} ;
2010-11-29 12:32:55 +03:00
static void ixp2000_irq_mask ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 12:32:55 +03:00
ixp2000_reg_wrb ( IXP2000_IRQ_ENABLE_CLR , ( 1 < < d - > irq ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-29 12:32:55 +03:00
static void ixp2000_irq_unmask ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 12:32:55 +03:00
ixp2000_reg_write ( IXP2000_IRQ_ENABLE_SET , ( 1 < < d - > irq ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-23 14:41:32 +03:00
static struct irq_chip ixp2000_irq_chip = {
2010-11-29 12:32:55 +03:00
. irq_ack = ixp2000_irq_mask ,
. irq_mask = ixp2000_irq_mask ,
. irq_unmask = ixp2000_irq_unmask
2005-04-17 02:20:36 +04:00
} ;
void __init ixp2000_init_irq ( void )
{
int irq ;
/*
* Mask all sources
*/
ixp2000_reg_write ( IXP2000_IRQ_ENABLE_CLR , 0xffffffff ) ;
ixp2000_reg_write ( IXP2000_FIQ_ENABLE_CLR , 0xffffffff ) ;
/* clear all GPIO edge/level detects */
ixp2000_reg_write ( IXP2000_GPIO_REDR , 0 ) ;
ixp2000_reg_write ( IXP2000_GPIO_FEDR , 0 ) ;
ixp2000_reg_write ( IXP2000_GPIO_LSHR , 0 ) ;
ixp2000_reg_write ( IXP2000_GPIO_LSLR , 0 ) ;
ixp2000_reg_write ( IXP2000_GPIO_INCR , - 1 ) ;
/* clear PCI interrupt sources */
2005-11-01 22:44:26 +03:00
ixp2000_reg_wrb ( IXP2000_PCI_XSCALE_INT_ENABLE , 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* Certain bits in the IRQ status register of the
* IXP2000 are reserved . Instead of trying to map
* things non 1 : 1 from bit position to IRQ number ,
* we mark the reserved IRQs as invalid . This makes
* our mask / unmask code much simpler .
*/
for ( irq = IRQ_IXP2000_SOFT_INT ; irq < = IRQ_IXP2000_THDB3 ; irq + + ) {
2005-06-24 23:54:35 +04:00
if ( ( 1 < < irq ) & IXP2000_VALID_IRQ_MASK ) {
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irq , & ixp2000_irq_chip ,
handle_level_irq ) ;
2005-04-17 02:20:36 +04:00
set_irq_flags ( irq , IRQF_VALID ) ;
} else set_irq_flags ( irq , 0 ) ;
}
2005-06-24 23:54:35 +04:00
2005-11-04 20:15:44 +03:00
for ( irq = IRQ_IXP2000_DRAM0_MIN_ERR ; irq < = IRQ_IXP2000_SP_INT ; irq + + ) {
if ( ( 1 < < ( irq - IRQ_IXP2000_DRAM0_MIN_ERR ) ) &
IXP2000_VALID_ERR_IRQ_MASK ) {
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irq , & ixp2000_err_irq_chip ,
handle_level_irq ) ;
2005-11-04 20:15:44 +03:00
set_irq_flags ( irq , IRQF_VALID ) ;
}
else
set_irq_flags ( irq , 0 ) ;
}
2011-03-24 15:25:22 +03:00
irq_set_chained_handler ( IRQ_IXP2000_ERRSUM , ixp2000_err_irq_handler ) ;
2005-11-04 20:15:44 +03:00
2005-04-17 02:20:36 +04:00
for ( irq = IRQ_IXP2000_GPIO0 ; irq < = IRQ_IXP2000_GPIO7 ; irq + + ) {
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irq , & ixp2000_GPIO_irq_chip ,
handle_level_irq ) ;
2006-03-22 23:14:09 +03:00
set_irq_flags ( irq , IRQF_VALID ) ;
2005-04-17 02:20:36 +04:00
}
2011-03-24 15:25:22 +03:00
irq_set_chained_handler ( IRQ_IXP2000_GPIO , ixp2000_GPIO_irq_handler ) ;
2005-04-17 02:20:36 +04:00
/*
* Enable PCI irqs . The actual PCI [ AB ] decoding is done in
* entry - macro . S , so we don ' t need a chained handler for the
* PCI interrupt source .
*/
ixp2000_reg_write ( IXP2000_IRQ_ENABLE_SET , ( 1 < < IRQ_IXP2000_PCI ) ) ;
for ( irq = IRQ_IXP2000_PCIA ; irq < = IRQ_IXP2000_PCIB ; irq + + ) {
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irq , & ixp2000_pci_irq_chip ,
handle_level_irq ) ;
2005-04-17 02:20:36 +04:00
set_irq_flags ( irq , IRQF_VALID ) ;
}
}