2005-04-17 02:20:36 +04:00
/*
2015-05-19 18:16:14 +03:00
* Copyright ( C ) 2015 Dmitry Eremin - Solenikov
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1999 - 2001 Nicolas Pitre
*
2015-05-19 18:16:14 +03:00
* Generic IRQ handling for the SA11x0 .
2005-04-17 02:20:36 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/module.h>
2006-07-02 01:32:38 +04:00
# include <linux/interrupt.h>
2012-06-06 14:42:36 +04:00
# include <linux/io.h>
2006-07-02 01:32:38 +04:00
# include <linux/irq.h>
2014-11-28 17:56:54 +03:00
# include <linux/irqdomain.h>
2011-04-23 00:03:03 +04:00
# include <linux/syscore_ops.h>
2015-05-19 18:16:14 +03:00
# include <linux/irqchip/irq-sa11x0.h>
2005-04-17 02:20:36 +04:00
2015-05-18 18:01:19 +03:00
# include <soc/sa1100/pwer.h>
2005-04-17 02:20:36 +04:00
2014-11-28 17:55:16 +03:00
# include <asm/exception.h>
2005-04-17 02:20:36 +04:00
2015-05-18 18:02:47 +03:00
# define ICIP 0x00 /* IC IRQ Pending reg. */
# define ICMR 0x04 /* IC Mask Reg. */
# define ICLR 0x08 /* IC Level Reg. */
# define ICCR 0x0C /* IC Control Reg. */
# define ICFP 0x10 /* IC FIQ Pending reg. */
# define ICPR 0x20 /* IC Pending Reg. */
2005-04-17 02:20:36 +04:00
2015-05-18 18:02:47 +03:00
static void __iomem * iobase ;
2005-04-17 02:20:36 +04:00
2014-11-28 17:57:52 +03:00
/*
* We don ' t need to ACK IRQs on the SA1100 unless they ' re GPIOs
* this is for internal IRQs i . e . from IRQ LCD to RTCAlrm .
*/
static void sa1100_mask_irq ( struct irq_data * d )
{
2015-05-18 18:02:47 +03:00
u32 reg ;
reg = readl_relaxed ( iobase + ICMR ) ;
reg & = ~ BIT ( d - > hwirq ) ;
writel_relaxed ( reg , iobase + ICMR ) ;
2014-11-28 17:57:52 +03:00
}
static void sa1100_unmask_irq ( struct irq_data * d )
{
2015-05-18 18:02:47 +03:00
u32 reg ;
reg = readl_relaxed ( iobase + ICMR ) ;
reg | = BIT ( d - > hwirq ) ;
writel_relaxed ( reg , iobase + ICMR ) ;
2014-11-28 17:57:52 +03:00
}
static int sa1100_set_wake ( struct irq_data * d , unsigned int on )
{
2015-05-18 18:01:19 +03:00
return sa11x0_sc_set_wake ( d - > hwirq , on ) ;
2014-11-28 17:57:52 +03:00
}
static struct irq_chip sa1100_normal_chip = {
. name = " SC " ,
. irq_ack = sa1100_mask_irq ,
. irq_mask = sa1100_mask_irq ,
. irq_unmask = sa1100_unmask_irq ,
. irq_set_wake = sa1100_set_wake ,
} ;
static int sa1100_normal_irqdomain_map ( struct irq_domain * d ,
unsigned int irq , irq_hw_number_t hwirq )
{
irq_set_chip_and_handler ( irq , & sa1100_normal_chip ,
handle_level_irq ) ;
return 0 ;
}
2015-04-27 16:55:12 +03:00
static const struct irq_domain_ops sa1100_normal_irqdomain_ops = {
2014-11-28 17:57:52 +03:00
. map = sa1100_normal_irqdomain_map ,
. xlate = irq_domain_xlate_onetwocell ,
} ;
static struct irq_domain * sa1100_normal_irqdomain ;
2005-04-17 02:20:36 +04:00
static struct sa1100irq_state {
unsigned int saved ;
unsigned int icmr ;
unsigned int iclr ;
unsigned int iccr ;
} sa1100irq_state ;
2011-04-23 00:03:03 +04:00
static int sa1100irq_suspend ( void )
2005-04-17 02:20:36 +04:00
{
struct sa1100irq_state * st = & sa1100irq_state ;
st - > saved = 1 ;
2015-05-18 18:02:47 +03:00
st - > icmr = readl_relaxed ( iobase + ICMR ) ;
st - > iclr = readl_relaxed ( iobase + ICLR ) ;
st - > iccr = readl_relaxed ( iobase + ICCR ) ;
2005-04-17 02:20:36 +04:00
/*
* Disable all GPIO - based interrupts .
*/
2015-05-18 18:02:47 +03:00
writel_relaxed ( st - > icmr & 0xfffff000 , iobase + ICMR ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2011-04-23 00:03:03 +04:00
static void sa1100irq_resume ( void )
2005-04-17 02:20:36 +04:00
{
struct sa1100irq_state * st = & sa1100irq_state ;
if ( st - > saved ) {
2015-05-18 18:02:47 +03:00
writel_relaxed ( st - > iccr , iobase + ICCR ) ;
writel_relaxed ( st - > iclr , iobase + ICLR ) ;
2005-04-17 02:20:36 +04:00
2015-05-18 18:02:47 +03:00
writel_relaxed ( st - > icmr , iobase + ICMR ) ;
2005-04-17 02:20:36 +04:00
}
}
2011-04-23 00:03:03 +04:00
static struct syscore_ops sa1100irq_syscore_ops = {
2005-04-17 02:20:36 +04:00
. suspend = sa1100irq_suspend ,
. resume = sa1100irq_resume ,
} ;
static int __init sa1100irq_init_devicefs ( void )
{
2011-04-23 00:03:03 +04:00
register_syscore_ops ( & sa1100irq_syscore_ops ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
device_initcall ( sa1100irq_init_devicefs ) ;
2014-11-28 17:55:16 +03:00
static asmlinkage void __exception_irq_entry
sa1100_handle_irq ( struct pt_regs * regs )
{
uint32_t icip , icmr , mask ;
do {
2015-05-18 18:02:47 +03:00
icip = readl_relaxed ( iobase + ICIP ) ;
icmr = readl_relaxed ( iobase + ICMR ) ;
2014-11-28 17:55:16 +03:00
mask = icip & icmr ;
if ( mask = = 0 )
break ;
2015-01-15 04:33:23 +03:00
handle_domain_irq ( sa1100_normal_irqdomain ,
ffs ( mask ) - 1 , regs ) ;
2014-11-28 17:55:16 +03:00
} while ( 1 ) ;
}
2015-05-19 18:16:14 +03:00
void __init sa11x0_init_irq_nodt ( int irq_start , resource_size_t io_start )
2005-04-17 02:20:36 +04:00
{
2015-05-19 18:16:14 +03:00
iobase = ioremap ( io_start , SZ_64K ) ;
2015-05-18 18:02:47 +03:00
if ( WARN_ON ( ! iobase ) )
return ;
2005-04-17 02:20:36 +04:00
/* disable all IRQs */
2015-05-18 18:02:47 +03:00
writel_relaxed ( 0 , iobase + ICMR ) ;
2005-04-17 02:20:36 +04:00
/* all IRQs are IRQ, not FIQ */
2015-05-18 18:02:47 +03:00
writel_relaxed ( 0 , iobase + ICLR ) ;
2005-04-17 02:20:36 +04:00
/*
* Whatever the doc says , this has to be set for the wait - on - irq
* instruction to work . . . on a SA1100 rev 9 at least .
*/
2015-05-18 18:02:47 +03:00
writel_relaxed ( 1 , iobase + ICCR ) ;
2005-04-17 02:20:36 +04:00
2015-01-15 04:31:48 +03:00
sa1100_normal_irqdomain = irq_domain_add_simple ( NULL ,
2015-05-19 18:16:14 +03:00
32 , irq_start ,
2015-01-15 04:29:16 +03:00
& sa1100_normal_irqdomain_ops , NULL ) ;
2014-11-28 17:55:16 +03:00
set_handle_irq ( sa1100_handle_irq ) ;
2005-04-17 02:20:36 +04:00
}