diff options
author | Byungho Min <bhminjames@gmail.com> | 2009-06-23 21:40:03 +0900 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-08-16 23:25:00 +0100 |
commit | c9b870e7e796eea515a261a314917317ebb1cb4a (patch) | |
tree | 09e360ea4c3f85153307009ee890e73992079f61 | |
parent | 0164cbf4390fbcd7125fc8d476a451a3efa14c5d (diff) |
ARM: S5PC100: IRQ and timer
S5PC100 has 3 VICs(Vectored Interrupt Controller). The VICs come from S3C64xx
series, so the driver source code can be shared with S3C families. The S5PC100
has 3 VICs while S3C64xx has only 2.
Signed-off-by: Byungho Min <bhmin@samsung.com>
[ben-linux@fluff.org: subject fixup]
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
-rw-r--r-- | arch/arm/mach-s5pc100/include/mach/irqs.h | 14 | ||||
-rw-r--r-- | arch/arm/mach-s5pc100/include/mach/pwm-clock.h | 56 | ||||
-rw-r--r-- | arch/arm/mach-s5pc100/include/mach/regs-irq.h | 24 | ||||
-rw-r--r-- | arch/arm/mach-s5pc100/include/mach/tick.h | 29 | ||||
-rw-r--r-- | arch/arm/plat-s5pc1xx/include/plat/irqs.h | 182 | ||||
-rw-r--r-- | arch/arm/plat-s5pc1xx/irq.c | 259 |
6 files changed, 564 insertions, 0 deletions
diff --git a/arch/arm/mach-s5pc100/include/mach/irqs.h b/arch/arm/mach-s5pc100/include/mach/irqs.h new file mode 100644 index 00000000000..622720dba28 --- /dev/null +++ b/arch/arm/mach-s5pc100/include/mach/irqs.h @@ -0,0 +1,14 @@ +/* linux/arch/arm/mach-s5pc100/include/mach/irqs.h + * + * Copyright 2009 Samsung Electronics Co. + * Byungho Min <bhmin@samsung.com> + * + * S5PC100 - IRQ definitions + */ + +#ifndef __ASM_ARCH_IRQS_H +#define __ASM_ARCH_IRQS_H __FILE__ + +#include <plat/irqs.h> + +#endif /* __ASM_ARCH_IRQ_H */ diff --git a/arch/arm/mach-s5pc100/include/mach/pwm-clock.h b/arch/arm/mach-s5pc100/include/mach/pwm-clock.h new file mode 100644 index 00000000000..b34d2f7aae5 --- /dev/null +++ b/arch/arm/mach-s5pc100/include/mach/pwm-clock.h @@ -0,0 +1,56 @@ +/* linux/arch/arm/mach-s5pc100/include/mach/pwm-clock.h + * + * Copyright 2009 Samsung Electronics Co. + * Byungho Min <bhmin@samsung.com> + * + * S5PC100 - pwm clock and timer support + * + * Based on mach-s3c6400/include/mach/pwm-clock.h + */ + +/** + * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk + * @tcfg: The timer TCFG1 register bits shifted down to 0. + * + * Return true if the given configuration from TCFG1 is a TCLK instead + * any of the TDIV clocks. + */ +static inline int pwm_cfg_src_is_tclk(unsigned long tcfg) +{ + return tcfg >= S3C64XX_TCFG1_MUX_TCLK; +} + +/** + * tcfg_to_divisor() - convert tcfg1 setting to a divisor + * @tcfg1: The tcfg1 setting, shifted down. + * + * Get the divisor value for the given tcfg1 setting. We assume the + * caller has already checked to see if this is not a TCLK source. + */ +static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) +{ + return 1 << tcfg1; +} + +/** + * pwm_tdiv_has_div1() - does the tdiv setting have a /1 + * + * Return true if we have a /1 in the tdiv setting. + */ +static inline unsigned int pwm_tdiv_has_div1(void) +{ + return 1; +} + +/** + * pwm_tdiv_div_bits() - calculate TCFG1 divisor value. + * @div: The divisor to calculate the bit information for. + * + * Turn a divisor into the necessary bit field for TCFG1. + */ +static inline unsigned long pwm_tdiv_div_bits(unsigned int div) +{ + return ilog2(div); +} + +#define S3C_TCFG1_MUX_TCLK S3C64XX_TCFG1_MUX_TCLK diff --git a/arch/arm/mach-s5pc100/include/mach/regs-irq.h b/arch/arm/mach-s5pc100/include/mach/regs-irq.h new file mode 100644 index 00000000000..751ac15438c --- /dev/null +++ b/arch/arm/mach-s5pc100/include/mach/regs-irq.h @@ -0,0 +1,24 @@ +/* linux/arch/arm/mach-s5pc100/include/mach/regs-irq.h + * + * Copyright 2009 Samsung Electronics Co. + * Byungho Min <bhmin@samsung.com> + * + * S5PC1XX - IRQ register definitions + * + * 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. +*/ + +#ifndef __ASM_ARCH_REGS_IRQ_H +#define __ASM_ARCH_REGS_IRQ_H __FILE__ + +#include <mach/map.h> +#include <asm/hardware/vic.h> + +/* interrupt controller */ +#define S5PC1XX_VIC0REG(x) ((x) + S5PC1XX_VA_VIC(0)) +#define S5PC1XX_VIC1REG(x) ((x) + S5PC1XX_VA_VIC(1)) +#define S5PC1XX_VIC2REG(x) ((x) + S5PC1XX_VA_VIC(2)) + +#endif /* __ASM_ARCH_REGS_IRQ_H */ diff --git a/arch/arm/mach-s5pc100/include/mach/tick.h b/arch/arm/mach-s5pc100/include/mach/tick.h new file mode 100644 index 00000000000..d3de0f3591a --- /dev/null +++ b/arch/arm/mach-s5pc100/include/mach/tick.h @@ -0,0 +1,29 @@ +/* linux/arch/arm/mach-s5pc100/include/mach/tick.h + * + * Copyright 2009 Samsung Electronics Co. + * Byungho Min <bhmin@samsung.com> + * + * S3C64XX - Timer tick support definitions + * + * Based on mach-s3c6400/include/mach/tick.h + * + * 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. +*/ + +#ifndef __ASM_ARCH_TICK_H +#define __ASM_ARCH_TICK_H __FILE__ + +/* note, the timer interrutps turn up in 2 places, the vic and then + * the timer block. We take the VIC as the base at the moment. + */ +static inline u32 s3c24xx_ostimer_pending(void) +{ + u32 pend = __raw_readl(S3C_VA_VIC0 + VIC_RAW_STATUS); + return pend & 1 << (IRQ_TIMER4 - S5PC1XX_IRQ_VIC0(0)); +} + +#define TICK_MAX (0xffffffff) + +#endif /* __ASM_ARCH_TICK_H */ diff --git a/arch/arm/plat-s5pc1xx/include/plat/irqs.h b/arch/arm/plat-s5pc1xx/include/plat/irqs.h new file mode 100644 index 00000000000..f07d8c3b25d --- /dev/null +++ b/arch/arm/plat-s5pc1xx/include/plat/irqs.h @@ -0,0 +1,182 @@ +/* linux/arch/arm/plat-s5pc1xx/include/plat/irqs.h + * + * Copyright 2009 Samsung Electronics Co. + * Byungho Min <bhmin@samsung.com> + * + * S5PC1XX - Common IRQ support + * + * Based on plat-s3c64xx/include/plat/irqs.h + */ + +#ifndef __ASM_PLAT_S5PC1XX_IRQS_H +#define __ASM_PLAT_S5PC1XX_IRQS_H __FILE__ + +/* we keep the first set of CPU IRQs out of the range of + * the ISA space, so that the PC104 has them to itself + * and we don't end up having to do horrible things to the + * standard ISA drivers.... + * + * note, since we're using the VICs, our start must be a + * mulitple of 32 to allow the common code to work + */ + +#define S3C_IRQ_OFFSET (32) + +#define S3C_IRQ(x) ((x) + S3C_IRQ_OFFSET) + +#define S3C_VIC0_BASE S3C_IRQ(0) +#define S3C_VIC1_BASE S3C_IRQ(32) +#define S3C_VIC2_BASE S3C_IRQ(64) + +/* UART interrupts, each UART has 4 intterupts per channel so + * use the space between the ISA and S3C main interrupts. Note, these + * are not in the same order as the S3C24XX series! */ + +#define IRQ_S3CUART_BASE0 (16) +#define IRQ_S3CUART_BASE1 (20) +#define IRQ_S3CUART_BASE2 (24) +#define IRQ_S3CUART_BASE3 (28) + +#define UART_IRQ_RXD (0) +#define UART_IRQ_ERR (1) +#define UART_IRQ_TXD (2) +#define UART_IRQ_MODEM (3) + +#define IRQ_S3CUART_RX0 (IRQ_S3CUART_BASE0 + UART_IRQ_RXD) +#define IRQ_S3CUART_TX0 (IRQ_S3CUART_BASE0 + UART_IRQ_TXD) +#define IRQ_S3CUART_ERR0 (IRQ_S3CUART_BASE0 + UART_IRQ_ERR) + +#define IRQ_S3CUART_RX1 (IRQ_S3CUART_BASE1 + UART_IRQ_RXD) +#define IRQ_S3CUART_TX1 (IRQ_S3CUART_BASE1 + UART_IRQ_TXD) +#define IRQ_S3CUART_ERR1 (IRQ_S3CUART_BASE1 + UART_IRQ_ERR) + +#define IRQ_S3CUART_RX2 (IRQ_S3CUART_BASE2 + UART_IRQ_RXD) +#define IRQ_S3CUART_TX2 (IRQ_S3CUART_BASE2 + UART_IRQ_TXD) +#define IRQ_S3CUART_ERR2 (IRQ_S3CUART_BASE2 + UART_IRQ_ERR) + +#define IRQ_S3CUART_RX3 (IRQ_S3CUART_BASE3 + UART_IRQ_RXD) +#define IRQ_S3CUART_TX3 (IRQ_S3CUART_BASE3 + UART_IRQ_TXD) +#define IRQ_S3CUART_ERR3 (IRQ_S3CUART_BASE3 + UART_IRQ_ERR) + +/* VIC based IRQs */ + +#define S5PC1XX_IRQ_VIC0(x) (S3C_VIC0_BASE + (x)) +#define S5PC1XX_IRQ_VIC1(x) (S3C_VIC1_BASE + (x)) +#define S5PC1XX_IRQ_VIC2(x) (S3C_VIC2_BASE + (x)) + +/* + * VIC0: system, DMA, timer + */ +#define IRQ_EINT0 S5PC1XX_IRQ_VIC0(0) +#define IRQ_EINT1 S5PC1XX_IRQ_VIC0(1) +#define IRQ_EINT2 S5PC1XX_IRQ_VIC0(2) +#define IRQ_EINT3 S5PC1XX_IRQ_VIC0(3) +#define IRQ_EINT4 S5PC1XX_IRQ_VIC0(4) +#define IRQ_EINT5 S5PC1XX_IRQ_VIC0(5) +#define IRQ_EINT6 S5PC1XX_IRQ_VIC0(6) +#define IRQ_EINT7 S5PC1XX_IRQ_VIC0(7) +#define IRQ_EINT8 S5PC1XX_IRQ_VIC0(8) +#define IRQ_EINT9 S5PC1XX_IRQ_VIC0(9) +#define IRQ_EINT10 S5PC1XX_IRQ_VIC0(10) +#define IRQ_EINT11 S5PC1XX_IRQ_VIC0(11) +#define IRQ_EINT12 S5PC1XX_IRQ_VIC0(12) +#define IRQ_EINT13 S5PC1XX_IRQ_VIC0(13) +#define IRQ_EINT14 S5PC1XX_IRQ_VIC0(14) +#define IRQ_EINT15 S5PC1XX_IRQ_VIC0(15) +#define IRQ_EINT16_31 S5PC1XX_IRQ_VIC0(16) +#define IRQ_BATF S5PC1XX_IRQ_VIC0(17) +#define IRQ_MDMA S5PC1XX_IRQ_VIC0(18) +#define IRQ_PDMA0 S5PC1XX_IRQ_VIC0(19) +#define IRQ_PDMA1 S5PC1XX_IRQ_VIC0(20) +#define IRQ_TIMER0 S5PC1XX_IRQ_VIC0(21) +#define IRQ_TIMER1 S5PC1XX_IRQ_VIC0(22) +#define IRQ_TIMER2 S5PC1XX_IRQ_VIC0(23) +#define IRQ_TIMER3 S5PC1XX_IRQ_VIC0(24) +#define IRQ_TIMER4 S5PC1XX_IRQ_VIC0(25) +#define IRQ_SYSTIMER S5PC1XX_IRQ_VIC0(26) +#define IRQ_WDT S5PC1XX_IRQ_VIC0(27) +#define IRQ_RTC_ALARM S5PC1XX_IRQ_VIC0(28) +#define IRQ_RTC_TIC S5PC1XX_IRQ_VIC0(29) +#define IRQ_GPIOINT S5PC1XX_IRQ_VIC0(30) + +/* + * VIC1: ARM, power, memory, connectivity + */ +#define IRQ_CORTEX0 S5PC1XX_IRQ_VIC1(0) +#define IRQ_CORTEX1 S5PC1XX_IRQ_VIC1(1) +#define IRQ_CORTEX2 S5PC1XX_IRQ_VIC1(2) +#define IRQ_CORTEX3 S5PC1XX_IRQ_VIC1(3) +#define IRQ_CORTEX4 S5PC1XX_IRQ_VIC1(4) +#define IRQ_IEMAPC S5PC1XX_IRQ_VIC1(5) +#define IRQ_IEMIEC S5PC1XX_IRQ_VIC1(6) +#define IRQ_ONENAND S5PC1XX_IRQ_VIC1(7) +#define IRQ_NFC S5PC1XX_IRQ_VIC1(8) +#define IRQ_CFC S5PC1XX_IRQ_VIC1(9) +#define IRQ_UART0 S5PC1XX_IRQ_VIC1(10) +#define IRQ_UART1 S5PC1XX_IRQ_VIC1(11) +#define IRQ_UART2 S5PC1XX_IRQ_VIC1(12) +#define IRQ_UART3 S5PC1XX_IRQ_VIC1(13) +#define IRQ_IIC S5PC1XX_IRQ_VIC1(14) +#define IRQ_SPI0 S5PC1XX_IRQ_VIC1(15) +#define IRQ_SPI1 S5PC1XX_IRQ_VIC1(16) +#define IRQ_SPI2 S5PC1XX_IRQ_VIC1(17) +#define IRQ_IRDA S5PC1XX_IRQ_VIC1(18) +#define IRQ_CAN0 S5PC1XX_IRQ_VIC1(19) +#define IRQ_CAN1 S5PC1XX_IRQ_VIC1(20) +#define IRQ_HSIRX S5PC1XX_IRQ_VIC1(21) +#define IRQ_HSITX S5PC1XX_IRQ_VIC1(22) +#define IRQ_UHOST S5PC1XX_IRQ_VIC1(23) +#define IRQ_OTG S5PC1XX_IRQ_VIC1(24) +#define IRQ_MSM S5PC1XX_IRQ_VIC1(25) +#define IRQ_HSMMC0 S5PC1XX_IRQ_VIC1(26) +#define IRQ_HSMMC1 S5PC1XX_IRQ_VIC1(27) +#define IRQ_HSMMC2 S5PC1XX_IRQ_VIC1(28) +#define IRQ_MIPICSI S5PC1XX_IRQ_VIC1(29) +#define IRQ_MIPIDSI S5PC1XX_IRQ_VIC1(30) + +/* + * VIC2: multimedia, audio, security + */ +#define IRQ_LCD0 S5PC1XX_IRQ_VIC2(0) +#define IRQ_LCD1 S5PC1XX_IRQ_VIC2(1) +#define IRQ_LCD2 S5PC1XX_IRQ_VIC2(2) +#define IRQ_LCD3 S5PC1XX_IRQ_VIC2(3) +#define IRQ_ROTATOR S5PC1XX_IRQ_VIC2(4) +#define IRQ_FIMC0 S5PC1XX_IRQ_VIC2(5) +#define IRQ_FIMC1 S5PC1XX_IRQ_VIC2(6) +#define IRQ_FIMC2 S5PC1XX_IRQ_VIC2(7) +#define IRQ_JPEG S5PC1XX_IRQ_VIC2(8) +#define IRQ_2D S5PC1XX_IRQ_VIC2(9) +#define IRQ_3D S5PC1XX_IRQ_VIC2(10) +#define IRQ_MIXER S5PC1XX_IRQ_VIC2(11) +#define IRQ_HDMI S5PC1XX_IRQ_VIC2(12) +#define IRQ_IIC1 S5PC1XX_IRQ_VIC2(13) +#define IRQ_MFC S5PC1XX_IRQ_VIC2(14) +#define IRQ_TVENC S5PC1XX_IRQ_VIC2(15) +#define IRQ_I2S0 S5PC1XX_IRQ_VIC2(16) +#define IRQ_I2S1 S5PC1XX_IRQ_VIC2(17) +#define IRQ_I2S2 S5PC1XX_IRQ_VIC2(18) +#define IRQ_AC97 S5PC1XX_IRQ_VIC2(19) +#define IRQ_PCM0 S5PC1XX_IRQ_VIC2(20) +#define IRQ_PCM1 S5PC1XX_IRQ_VIC2(21) +#define IRQ_SPDIF S5PC1XX_IRQ_VIC2(22) +#define IRQ_ADC S5PC1XX_IRQ_VIC2(23) +#define IRQ_PENDN S5PC1XX_IRQ_VIC2(24) +#define IRQ_TC IRQ_PENDN +#define IRQ_KEYPAD S5PC1XX_IRQ_VIC2(25) +#define IRQ_CG S5PC1XX_IRQ_VIC2(26) +#define IRQ_SEC S5PC1XX_IRQ_VIC2(27) +#define IRQ_SECRX S5PC1XX_IRQ_VIC2(28) +#define IRQ_SECTX S5PC1XX_IRQ_VIC2(29) +#define IRQ_SDMIRQ S5PC1XX_IRQ_VIC2(30) +#define IRQ_SDMFIQ S5PC1XX_IRQ_VIC2(31) + +#define S3C_IRQ_EINT_BASE (IRQ_SDMFIQ + 1) + +#define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE) +#define IRQ_EINT(x) S3C_EINT(x) + +#define NR_IRQS (IRQ_EINT(31)+1) + +#endif /* __ASM_PLAT_S5PC1XX_IRQS_H */ + diff --git a/arch/arm/plat-s5pc1xx/irq.c b/arch/arm/plat-s5pc1xx/irq.c new file mode 100644 index 00000000000..80d6dd942cb --- /dev/null +++ b/arch/arm/plat-s5pc1xx/irq.c @@ -0,0 +1,259 @@ +/* arch/arm/plat-s5pc1xx/irq.c + * + * Copyright 2009 Samsung Electronics Co. + * Byungho Min <bhmin@samsung.com> + * + * S5PC1XX - Interrupt handling + * + * Based on plat-s3c64xx/irq.c + * + * 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/kernel.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <asm/hardware/vic.h> + +#include <mach/map.h> +#include <plat/regs-timer.h> +#include <plat/cpu.h> + +/* Timer interrupt handling */ + +static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq) +{ + generic_handle_irq(sub_irq); +} + +static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc) +{ + s3c_irq_demux_timer(irq, IRQ_TIMER0); +} + +static void s3c_irq_demux_timer1(unsigned int irq, struct irq_desc *desc) +{ + s3c_irq_demux_timer(irq, IRQ_TIMER1); +} + +static void s3c_irq_demux_timer2(unsigned int irq, struct irq_desc *desc) +{ + s3c_irq_demux_timer(irq, IRQ_TIMER2); +} + +static void s3c_irq_demux_timer3(unsigned int irq, struct irq_desc *desc) +{ + s3c_irq_demux_timer(irq, IRQ_TIMER3); +} + +static void s3c_irq_demux_timer4(unsigned int irq, struct irq_desc *desc) +{ + s3c_irq_demux_timer(irq, IRQ_TIMER4); +} + +/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */ + +static void s3c_irq_timer_mask(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; /* mask out pending interrupts */ + reg &= ~(1 << (irq - IRQ_TIMER0)); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static void s3c_irq_timer_unmask(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; /* mask out pending interrupts */ + reg |= 1 << (irq - IRQ_TIMER0); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static void s3c_irq_timer_ack(unsigned int irq) +{ + u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); + + reg &= 0x1f; + reg |= (1 << 5) << (irq - IRQ_TIMER0); + __raw_writel(reg, S3C64XX_TINT_CSTAT); +} + +static struct irq_chip s3c_irq_timer = { + .name = "s3c-timer", + .mask = s3c_irq_timer_mask, + .unmask = s3c_irq_timer_unmask, + .ack = s3c_irq_timer_ack, +}; + +struct uart_irq { + void __iomem *regs; + unsigned int base_irq; + unsigned int parent_irq; +}; + +/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] + * are consecutive when looking up the interrupt in the demux routines. + */ +static struct uart_irq uart_irqs[] = { + [0] = { + .regs = (void *)S3C_VA_UART0, + .base_irq = IRQ_S3CUART_BASE0, + .parent_irq = IRQ_UART0, + }, + [1] = { + .regs = (void *)S3C_VA_UART1, + .base_irq = IRQ_S3CUART_BASE1, + .parent_irq = IRQ_UART1, + }, + [2] = { + .regs = (void *)S3C_VA_UART2, + .base_irq = IRQ_S3CUART_BASE2, + .parent_irq = IRQ_UART2, + }, + [3] = { + .regs = (void *)S3C_VA_UART3, + .base_irq = IRQ_S3CUART_BASE3, + .parent_irq = IRQ_UART3, + }, +}; + +static inline void __iomem *s3c_irq_uart_base(unsigned int irq) +{ + struct uart_irq *uirq = get_irq_chip_data(irq); + return uirq->regs; +} + +static inline unsigned int s3c_irq_uart_bit(unsigned int irq) +{ + return irq & 3; +} + +/* UART interrupt registers, not worth adding to seperate include header */ +#define S3C64XX_UINTP 0x30 +#define S3C64XX_UINTSP 0x34 +#define S3C64XX_UINTM 0x38 + +static void s3c_irq_uart_mask(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg |= (1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); +} + +static void s3c_irq_uart_maskack(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg |= (1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); + __raw_writel(1 << bit, regs + S3C64XX_UINTP); +} + +static void s3c_irq_uart_unmask(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + u32 reg; + + reg = __raw_readl(regs + S3C64XX_UINTM); + reg &= ~(1 << bit); + __raw_writel(reg, regs + S3C64XX_UINTM); +} + +static void s3c_irq_uart_ack(unsigned int irq) +{ + void __iomem *regs = s3c_irq_uart_base(irq); + unsigned int bit = s3c_irq_uart_bit(irq); + + __raw_writel(1 << bit, regs + S3C64XX_UINTP); +} + +static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc) +{ + struct uart_irq *uirq = &uart_irqs[irq - IRQ_UART0]; + u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP); + int base = uirq->base_irq; + + if (pend & (1 << 0)) + generic_handle_irq(base); + if (pend & (1 << 1)) + generic_handle_irq(base + 1); + if (pend & (1 << 2)) + generic_handle_irq(base + 2); + if (pend & (1 << 3)) + generic_handle_irq(base + 3); +} + +static struct irq_chip s3c_irq_uart = { + .name = "s3c-uart", + .mask = s3c_irq_uart_mask, + .unmask = s3c_irq_uart_unmask, + .mask_ack = s3c_irq_uart_maskack, + .ack = s3c_irq_uart_ack, +}; + +static void __init s5pc1xx_uart_irq(struct uart_irq *uirq) +{ + void __iomem *reg_base = uirq->regs; + unsigned int irq; + int offs; + + /* mask all interrupts at the start. */ + __raw_writel(0xf, reg_base + S3C64XX_UINTM); + + for (offs = 0; offs < 3; offs++) { + irq = uirq->base_irq + offs; + + set_irq_chip(irq, &s3c_irq_uart); + set_irq_chip_data(irq, uirq); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + } + + set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart); +} + +void __init s5pc1xx_init_irq(u32 *vic_valid, int num) +{ + int i; + int uart, irq; + + printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); + + /* initialise the pair of VICs */ + for (i = 0; i < num; i++) + vic_init((void *)S5PC1XX_VA_VIC(i), S3C_IRQ(i * S3C_IRQ_OFFSET), + vic_valid[i], 0); + + /* add the timer sub-irqs */ + + set_irq_chained_handler(IRQ_TIMER0, s3c_irq_demux_timer0); + set_irq_chained_handler(IRQ_TIMER1, s3c_irq_demux_timer1); + set_irq_chained_handler(IRQ_TIMER2, s3c_irq_demux_timer2); + set_irq_chained_handler(IRQ_TIMER3, s3c_irq_demux_timer3); + set_irq_chained_handler(IRQ_TIMER4, s3c_irq_demux_timer4); + + for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) { + set_irq_chip(irq, &s3c_irq_timer); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + } + + for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++) + s5pc1xx_uart_irq(&uart_irqs[uart]); +} + + |