2010-04-28 13:09:01 +04:00
/* linux/arch/arm/mach-s3c2416/irq.c
*
* Copyright ( c ) 2009 Yauhen Kharuzhy < jekhor @ gmail . com > ,
* as part of OpenInkpot project
* Copyright ( c ) 2009 Promwad Innovation Company
* Yauhen Kharuzhy < yauhen . kharuzhy @ promwad . com >
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
2011-12-22 04:01:38 +04:00
# include <linux/device.h>
2010-04-28 13:09:01 +04:00
# include <linux/io.h>
2012-05-19 20:01:35 +04:00
# include <linux/syscore_ops.h>
2010-04-28 13:09:01 +04:00
# include <mach/hardware.h>
# include <asm/irq.h>
# include <asm/mach/irq.h>
# include <mach/regs-irq.h>
# include <mach/regs-gpio.h>
# include <plat/cpu.h>
# include <plat/pm.h>
# include <plat/irq.h>
# define INTMSK(start, end) ((1 << ((end) + 1 - (start))) - 1)
static inline void s3c2416_irq_demux ( unsigned int irq , unsigned int len )
{
unsigned int subsrc , submsk ;
unsigned int end ;
/* read the current pending interrupts, and the mask
* for what it is available */
subsrc = __raw_readl ( S3C2410_SUBSRCPND ) ;
submsk = __raw_readl ( S3C2410_INTSUBMSK ) ;
subsrc & = ~ submsk ;
subsrc > > = ( irq - S3C2410_IRQSUB ( 0 ) ) ;
subsrc & = ( 1 < < len ) - 1 ;
end = len + irq ;
for ( ; irq < end & & subsrc ; irq + + ) {
if ( subsrc & 1 )
generic_handle_irq ( irq ) ;
subsrc > > = 1 ;
}
}
/* WDT/AC97 sub interrupts */
static void s3c2416_irq_demux_wdtac97 ( unsigned int irq , struct irq_desc * desc )
{
s3c2416_irq_demux ( IRQ_S3C2443_WDT , 4 ) ;
}
# define INTMSK_WDTAC97 (1UL << (IRQ_WDT - IRQ_EINT0))
# define SUBMSK_WDTAC97 INTMSK(IRQ_S3C2443_WDT, IRQ_S3C2443_AC97)
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_wdtac97_mask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_mask ( data - > irq , INTMSK_WDTAC97 , SUBMSK_WDTAC97 ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_wdtac97_unmask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_unmask ( data - > irq , INTMSK_WDTAC97 ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_wdtac97_ack ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_maskack ( data - > irq , INTMSK_WDTAC97 , SUBMSK_WDTAC97 ) ;
2010-04-28 13:09:01 +04:00
}
static struct irq_chip s3c2416_irq_wdtac97 = {
2011-01-03 13:15:54 +03:00
. irq_mask = s3c2416_irq_wdtac97_mask ,
. irq_unmask = s3c2416_irq_wdtac97_unmask ,
. irq_ack = s3c2416_irq_wdtac97_ack ,
2010-04-28 13:09:01 +04:00
} ;
/* LCD sub interrupts */
static void s3c2416_irq_demux_lcd ( unsigned int irq , struct irq_desc * desc )
{
s3c2416_irq_demux ( IRQ_S3C2443_LCD1 , 4 ) ;
}
# define INTMSK_LCD (1UL << (IRQ_LCD - IRQ_EINT0))
# define SUBMSK_LCD INTMSK(IRQ_S3C2443_LCD1, IRQ_S3C2443_LCD4)
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_lcd_mask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_mask ( data - > irq , INTMSK_LCD , SUBMSK_LCD ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_lcd_unmask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_unmask ( data - > irq , INTMSK_LCD ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_lcd_ack ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_maskack ( data - > irq , INTMSK_LCD , SUBMSK_LCD ) ;
2010-04-28 13:09:01 +04:00
}
static struct irq_chip s3c2416_irq_lcd = {
2011-01-03 13:15:54 +03:00
. irq_mask = s3c2416_irq_lcd_mask ,
. irq_unmask = s3c2416_irq_lcd_unmask ,
. irq_ack = s3c2416_irq_lcd_ack ,
2010-04-28 13:09:01 +04:00
} ;
/* DMA sub interrupts */
static void s3c2416_irq_demux_dma ( unsigned int irq , struct irq_desc * desc )
{
s3c2416_irq_demux ( IRQ_S3C2443_DMA0 , 6 ) ;
}
# define INTMSK_DMA (1UL << (IRQ_S3C2443_DMA - IRQ_EINT0))
# define SUBMSK_DMA INTMSK(IRQ_S3C2443_DMA0, IRQ_S3C2443_DMA5)
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_dma_mask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_mask ( data - > irq , INTMSK_DMA , SUBMSK_DMA ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_dma_unmask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_unmask ( data - > irq , INTMSK_DMA ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_dma_ack ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_maskack ( data - > irq , INTMSK_DMA , SUBMSK_DMA ) ;
2010-04-28 13:09:01 +04:00
}
static struct irq_chip s3c2416_irq_dma = {
2011-01-03 13:15:54 +03:00
. irq_mask = s3c2416_irq_dma_mask ,
. irq_unmask = s3c2416_irq_dma_unmask ,
. irq_ack = s3c2416_irq_dma_ack ,
2010-04-28 13:09:01 +04:00
} ;
/* UART3 sub interrupts */
static void s3c2416_irq_demux_uart3 ( unsigned int irq , struct irq_desc * desc )
{
2010-10-21 05:15:48 +04:00
s3c2416_irq_demux ( IRQ_S3C2443_RX3 , 3 ) ;
2010-04-28 13:09:01 +04:00
}
# define INTMSK_UART3 (1UL << (IRQ_S3C2443_UART3 - IRQ_EINT0))
2010-10-21 05:15:48 +04:00
# define SUBMSK_UART3 (0x7 << (IRQ_S3C2443_RX3 - S3C2410_IRQSUB(0)))
2010-04-28 13:09:01 +04:00
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_uart3_mask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_mask ( data - > irq , INTMSK_UART3 , SUBMSK_UART3 ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_uart3_unmask ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_unmask ( data - > irq , INTMSK_UART3 ) ;
2010-04-28 13:09:01 +04:00
}
2011-01-03 13:15:54 +03:00
static void s3c2416_irq_uart3_ack ( struct irq_data * data )
2010-04-28 13:09:01 +04:00
{
2011-01-03 13:15:54 +03:00
s3c_irqsub_maskack ( data - > irq , INTMSK_UART3 , SUBMSK_UART3 ) ;
2010-04-28 13:09:01 +04:00
}
static struct irq_chip s3c2416_irq_uart3 = {
2011-01-03 13:15:54 +03:00
. irq_mask = s3c2416_irq_uart3_mask ,
. irq_unmask = s3c2416_irq_uart3_unmask ,
. irq_ack = s3c2416_irq_uart3_ack ,
2010-04-28 13:09:01 +04:00
} ;
2012-05-19 20:01:35 +04:00
/* second interrupt register */
static inline void s3c2416_irq_ack_second ( struct irq_data * data )
{
unsigned long bitval = 1UL < < ( data - > irq - IRQ_S3C2416_2D ) ;
__raw_writel ( bitval , S3C2416_SRCPND2 ) ;
__raw_writel ( bitval , S3C2416_INTPND2 ) ;
}
static void s3c2416_irq_mask_second ( struct irq_data * data )
{
unsigned long bitval = 1UL < < ( data - > irq - IRQ_S3C2416_2D ) ;
unsigned long mask ;
mask = __raw_readl ( S3C2416_INTMSK2 ) ;
mask | = bitval ;
__raw_writel ( mask , S3C2416_INTMSK2 ) ;
}
static void s3c2416_irq_unmask_second ( struct irq_data * data )
{
unsigned long bitval = 1UL < < ( data - > irq - IRQ_S3C2416_2D ) ;
unsigned long mask ;
mask = __raw_readl ( S3C2416_INTMSK2 ) ;
mask & = ~ bitval ;
__raw_writel ( mask , S3C2416_INTMSK2 ) ;
}
struct irq_chip s3c2416_irq_second = {
. irq_ack = s3c2416_irq_ack_second ,
. irq_mask = s3c2416_irq_mask_second ,
. irq_unmask = s3c2416_irq_unmask_second ,
} ;
2010-04-28 13:09:01 +04:00
/* IRQ initialisation code */
ARM: s3c24xx: fix multiple section mismatch warnings
The *_irq_add function should not be marked __init because the driver
subsystem thinks they might be called at a later stage.
The usb_simtec_init function accesses initdata and should be marked
init. This is safe because the only caller is also an init function.
Without this patch, building s3c2410_defconfig results in:
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0x1030): Section mismatch in reference from the variable s3c2416_irq_interface to the function .init.text:s3c2416_irq_add()
The variable s3c2416_irq_interface references
the function __init s3c2416_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0x1b08): Section mismatch in reference from the variable s3c2443_irq_interface to the function .init.text:s3c2443_irq_add()
The variable s3c2443_irq_interface references
the function __init s3c2443_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0xf44): Section mismatch in reference from the variable s3c2416_irq_interface to the function .init.text:s3c2416_irq_add()
The variable s3c2416_irq_interface references
the function __init s3c2416_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.text+0x3f7c): Section mismatch in reference from the function usb_simtec_init() to the (unknown reference) .init.data:(unknown)
The function usb_simtec_init() references
the (unknown reference) __initdata (unknown).
This is often because usb_simtec_init lacks a __initdata
annotation or the annotation of (unknown) is wrong.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kukjin Kim <kgene.kim@samsung.com>
2012-05-27 06:42:12 +04:00
static int s3c2416_add_sub ( unsigned int base ,
2010-04-28 13:09:01 +04:00
void ( * demux ) ( unsigned int ,
struct irq_desc * ) ,
struct irq_chip * chip ,
unsigned int start , unsigned int end )
{
unsigned int irqno ;
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( base , & s3c_irq_level_chip , handle_level_irq ) ;
2011-03-24 15:25:22 +03:00
irq_set_chained_handler ( base , demux ) ;
2010-04-28 13:09:01 +04:00
for ( irqno = start ; irqno < = end ; irqno + + ) {
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irqno , chip , handle_level_irq ) ;
2010-04-28 13:09:01 +04:00
set_irq_flags ( irqno , IRQF_VALID ) ;
}
return 0 ;
}
ARM: s3c24xx: fix multiple section mismatch warnings
The *_irq_add function should not be marked __init because the driver
subsystem thinks they might be called at a later stage.
The usb_simtec_init function accesses initdata and should be marked
init. This is safe because the only caller is also an init function.
Without this patch, building s3c2410_defconfig results in:
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0x1030): Section mismatch in reference from the variable s3c2416_irq_interface to the function .init.text:s3c2416_irq_add()
The variable s3c2416_irq_interface references
the function __init s3c2416_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0x1b08): Section mismatch in reference from the variable s3c2443_irq_interface to the function .init.text:s3c2443_irq_add()
The variable s3c2443_irq_interface references
the function __init s3c2443_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0xf44): Section mismatch in reference from the variable s3c2416_irq_interface to the function .init.text:s3c2416_irq_add()
The variable s3c2416_irq_interface references
the function __init s3c2416_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.text+0x3f7c): Section mismatch in reference from the function usb_simtec_init() to the (unknown reference) .init.data:(unknown)
The function usb_simtec_init() references
the (unknown reference) __initdata (unknown).
This is often because usb_simtec_init lacks a __initdata
annotation or the annotation of (unknown) is wrong.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kukjin Kim <kgene.kim@samsung.com>
2012-05-27 06:42:12 +04:00
static void s3c2416_irq_add_second ( void )
2012-05-19 20:01:35 +04:00
{
unsigned long pend ;
unsigned long last ;
int irqno ;
int i ;
/* first, clear all interrupts pending... */
last = 0 ;
for ( i = 0 ; i < 4 ; i + + ) {
pend = __raw_readl ( S3C2416_INTPND2 ) ;
if ( pend = = 0 | | pend = = last )
break ;
__raw_writel ( pend , S3C2416_SRCPND2 ) ;
__raw_writel ( pend , S3C2416_INTPND2 ) ;
printk ( KERN_INFO " irq: clearing pending status %08x \n " ,
( int ) pend ) ;
last = pend ;
}
for ( irqno = IRQ_S3C2416_2D ; irqno < = IRQ_S3C2416_I2S1 ; irqno + + ) {
switch ( irqno ) {
case IRQ_S3C2416_RESERVED2 :
case IRQ_S3C2416_RESERVED3 :
/* no IRQ here */
break ;
default :
irq_set_chip_and_handler ( irqno , & s3c2416_irq_second ,
handle_edge_irq ) ;
set_irq_flags ( irqno , IRQF_VALID ) ;
}
}
}
ARM: s3c24xx: fix multiple section mismatch warnings
The *_irq_add function should not be marked __init because the driver
subsystem thinks they might be called at a later stage.
The usb_simtec_init function accesses initdata and should be marked
init. This is safe because the only caller is also an init function.
Without this patch, building s3c2410_defconfig results in:
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0x1030): Section mismatch in reference from the variable s3c2416_irq_interface to the function .init.text:s3c2416_irq_add()
The variable s3c2416_irq_interface references
the function __init s3c2416_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0x1b08): Section mismatch in reference from the variable s3c2443_irq_interface to the function .init.text:s3c2443_irq_add()
The variable s3c2443_irq_interface references
the function __init s3c2443_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.data+0xf44): Section mismatch in reference from the variable s3c2416_irq_interface to the function .init.text:s3c2416_irq_add()
The variable s3c2416_irq_interface references
the function __init s3c2416_irq_add()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the variable:
*_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console
WARNING: arch/arm/mach-s3c24xx/built-in.o(.text+0x3f7c): Section mismatch in reference from the function usb_simtec_init() to the (unknown reference) .init.data:(unknown)
The function usb_simtec_init() references
the (unknown reference) __initdata (unknown).
This is often because usb_simtec_init lacks a __initdata
annotation or the annotation of (unknown) is wrong.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Kukjin Kim <kgene.kim@samsung.com>
2012-05-27 06:42:12 +04:00
static int s3c2416_irq_add ( struct device * dev ,
2012-01-27 10:35:25 +04:00
struct subsys_interface * sif )
2010-04-28 13:09:01 +04:00
{
printk ( KERN_INFO " S3C2416: IRQ Support \n " ) ;
s3c2416_add_sub ( IRQ_LCD , s3c2416_irq_demux_lcd , & s3c2416_irq_lcd ,
IRQ_S3C2443_LCD2 , IRQ_S3C2443_LCD4 ) ;
s3c2416_add_sub ( IRQ_S3C2443_DMA , s3c2416_irq_demux_dma ,
& s3c2416_irq_dma , IRQ_S3C2443_DMA0 , IRQ_S3C2443_DMA5 ) ;
s3c2416_add_sub ( IRQ_S3C2443_UART3 , s3c2416_irq_demux_uart3 ,
& s3c2416_irq_uart3 ,
IRQ_S3C2443_RX3 , IRQ_S3C2443_ERR3 ) ;
s3c2416_add_sub ( IRQ_WDT , s3c2416_irq_demux_wdtac97 ,
& s3c2416_irq_wdtac97 ,
IRQ_S3C2443_WDT , IRQ_S3C2443_AC97 ) ;
2012-05-19 20:01:35 +04:00
s3c2416_irq_add_second ( ) ;
2010-04-28 13:09:01 +04:00
return 0 ;
}
2011-12-22 04:01:38 +04:00
static struct subsys_interface s3c2416_irq_interface = {
. name = " s3c2416_irq " ,
. subsys = & s3c2416_subsys ,
. add_dev = s3c2416_irq_add ,
2010-04-28 13:09:01 +04:00
} ;
static int __init s3c2416_irq_init ( void )
{
2011-12-22 04:01:38 +04:00
return subsys_interface_register ( & s3c2416_irq_interface ) ;
2010-04-28 13:09:01 +04:00
}
arch_initcall ( s3c2416_irq_init ) ;
2012-05-19 20:01:35 +04:00
# ifdef CONFIG_PM
static struct sleep_save irq_save [ ] = {
SAVE_ITEM ( S3C2416_INTMSK2 ) ,
} ;
int s3c2416_irq_suspend ( void )
{
s3c_pm_do_save ( irq_save , ARRAY_SIZE ( irq_save ) ) ;
return 0 ;
}
void s3c2416_irq_resume ( void )
{
s3c_pm_do_restore ( irq_save , ARRAY_SIZE ( irq_save ) ) ;
}
struct syscore_ops s3c2416_irq_syscore_ops = {
. suspend = s3c2416_irq_suspend ,
. resume = s3c2416_irq_resume ,
} ;
# endif