From 502a29890cb10292f464beb802f7184c0d567ab8 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Mar 2013 12:38:13 +0900 Subject: [PATCH 01/12] ARM: S3C24XX: fix redundant checks in the irq mapping function The check during the parent handling itself was wrong, as it should have checked for parent_irq_data. The interrupt controller structs always contain an irq_data array with 32 entries and the only possible error could be a parent_irq assignment of >31. As this would point to outside the irq_data array this could contain anything including non-NULL values. Therefore correct this to check the parent_irq value to be in the right range. With the same explanation of a valid interrupt controller always having a full irq_data array, the topmost irq_data check in s3c24xx_irq_map can also go away. Finally the mapping function is only called thru the irq_domain ops, in which case the intc struct is already successfully created, so there is no need to check for it again. Reported-by: Julia Lawall Signed-off-by: Heiko Stuebner Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c24xx/irq.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-s3c24xx/irq.c b/arch/arm/mach-s3c24xx/irq.c index 3f3de7492094..5257c9fd9e04 100644 --- a/arch/arm/mach-s3c24xx/irq.c +++ b/arch/arm/mach-s3c24xx/irq.c @@ -324,16 +324,6 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, struct s3c_irq_data *parent_irq_data; unsigned int irqno; - if (!intc) { - pr_err("irq-s3c24xx: no controller found for hwirq %lu\n", hw); - return -EINVAL; - } - - if (!irq_data) { - pr_err("irq-s3c24xx: no irq data found for hwirq %lu\n", hw); - return -EINVAL; - } - /* attach controller pointer to irq_data */ irq_data->intc = intc; @@ -383,13 +373,13 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, goto err; } - parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; - if (!irq_data) { - pr_err("irq-s3c24xx: no irq data found for hwirq %lu\n", - hw); + if (irq_data->parent_irq > 31) { + pr_err("irq-s3c24xx: parent irq %lu is out of range\n", + irq_data->parent_irq); goto err; } + parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; parent_irq_data->sub_intc = intc; parent_irq_data->sub_bits |= (1UL << hw); From 0fe3cb1ea5bf382cec946328ee25b233cab45c4d Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Mar 2013 12:38:16 +0900 Subject: [PATCH 02/12] ARM: S3C24XX: fix irq parent check The current parent_irq check checks for a value != 0. This does of course not work when the parent irq sits in the bit 0 of the parent register. This only affects the eint0 interrupt of the s3c2412. To fix this behaviour, check for the presence of a parent_intc in the structure. In an s3c24xx interrupt controller either all interrupts have parent interrupts or none have, so if a parent controller is available the parent_irq value always points to a parent_irq. Signed-off-by: Heiko Stuebner Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c24xx/irq.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-s3c24xx/irq.c b/arch/arm/mach-s3c24xx/irq.c index 5257c9fd9e04..a6a7554d946e 100644 --- a/arch/arm/mach-s3c24xx/irq.c +++ b/arch/arm/mach-s3c24xx/irq.c @@ -81,7 +81,7 @@ static void s3c_irq_mask(struct irq_data *data) mask |= (1UL << data->hwirq); __raw_writel(mask, intc->reg_mask); - if (parent_intc && irq_data->parent_irq) { + if (parent_intc) { parent_data = &parent_intc->irqs[irq_data->parent_irq]; /* check to see if we need to mask the parent IRQ */ @@ -105,7 +105,7 @@ static void s3c_irq_unmask(struct irq_data *data) mask &= ~(1UL << data->hwirq); __raw_writel(mask, intc->reg_mask); - if (parent_intc && irq_data->parent_irq) { + if (parent_intc) { irqno = irq_find_mapping(parent_intc->domain, irq_data->parent_irq); s3c_irq_unmask(irq_get_irq_data(irqno)); @@ -327,6 +327,8 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, /* attach controller pointer to irq_data */ irq_data->intc = intc; + parent_intc = intc->parent; + /* set handler and flags */ switch (irq_data->type) { case S3C_IRQTYPE_NONE: @@ -335,7 +337,7 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, /* On the S3C2412, the EINT0to3 have a parent irq * but need the s3c_irq_eint0t4 chip */ - if (irq_data->parent_irq && (!soc_is_s3c2412() || hw >= 4)) + if (parent_intc && (!soc_is_s3c2412() || hw >= 4)) irq_set_chip_and_handler(virq, &s3c_irqext_chip, handle_edge_irq); else @@ -343,8 +345,7 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, handle_edge_irq); break; case S3C_IRQTYPE_EDGE: - if (irq_data->parent_irq || - intc->reg_pending == S3C2416_SRCPND2) + if (parent_intc || intc->reg_pending == S3C2416_SRCPND2) irq_set_chip_and_handler(virq, &s3c_irq_level_chip, handle_edge_irq); else @@ -352,7 +353,7 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, handle_edge_irq); break; case S3C_IRQTYPE_LEVEL: - if (irq_data->parent_irq) + if (parent_intc) irq_set_chip_and_handler(virq, &s3c_irq_level_chip, handle_level_irq); else @@ -365,14 +366,7 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, } set_irq_flags(virq, IRQF_VALID); - if (irq_data->parent_irq) { - parent_intc = intc->parent; - if (!parent_intc) { - pr_err("irq-s3c24xx: no parent controller found for hwirq %lu\n", - hw); - goto err; - } - + if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) { if (irq_data->parent_irq > 31) { pr_err("irq-s3c24xx: parent irq %lu is out of range\n", irq_data->parent_irq); From f182aa1dfa6283a1193308c4917aef4a7a982b8c Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Mar 2013 12:38:19 +0900 Subject: [PATCH 03/12] ARM: S3C24XX: move s3c24xx_init_irq to s3c2410_init_irq The s3c24xx_init_irq function that was the base for all irq inits is now only used to initialize the real s3c2410 irqs. Therefore rename it and also move its declaration from plat/cpu.h to common.h The eint declaration is used by the vast majority of the SoCs and gets therefore placed outside any ifdefs. Signed-off-by: Heiko Stuebner Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c24xx/common.h | 1 + arch/arm/mach-s3c24xx/irq.c | 69 ++++++++++++------------ arch/arm/mach-s3c24xx/mach-amlm5900.c | 2 +- arch/arm/mach-s3c24xx/mach-bast.c | 2 +- arch/arm/mach-s3c24xx/mach-h1940.c | 7 +-- arch/arm/mach-s3c24xx/mach-n30.c | 4 +- arch/arm/mach-s3c24xx/mach-otom.c | 2 +- arch/arm/mach-s3c24xx/mach-qt2410.c | 2 +- arch/arm/mach-s3c24xx/mach-smdk2410.c | 2 +- arch/arm/mach-s3c24xx/mach-tct_hammer.c | 2 +- arch/arm/mach-s3c24xx/mach-vr1000.c | 2 +- arch/arm/plat-samsung/include/plat/cpu.h | 1 - 12 files changed, 44 insertions(+), 52 deletions(-) diff --git a/arch/arm/mach-s3c24xx/common.h b/arch/arm/mach-s3c24xx/common.h index abefeb38bba4..307c3714be55 100644 --- a/arch/arm/mach-s3c24xx/common.h +++ b/arch/arm/mach-s3c24xx/common.h @@ -21,6 +21,7 @@ extern void s3c2410_map_io(void); extern void s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no); extern void s3c2410_init_clocks(int xtal); extern void s3c2410_restart(char mode, const char *cmd); +extern void s3c2410_init_irq(void); #else #define s3c2410_init_clocks NULL #define s3c2410_init_uarts NULL diff --git a/arch/arm/mach-s3c24xx/irq.c b/arch/arm/mach-s3c24xx/irq.c index a6a7554d946e..ee4765403ab0 100644 --- a/arch/arm/mach-s3c24xx/irq.c +++ b/arch/arm/mach-s3c24xx/irq.c @@ -509,12 +509,35 @@ struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, return ERR_PTR(ret); } -/* s3c24xx_init_irq - * - * Initialise S3C2410 IRQ system -*/ +static struct s3c_irq_data init_eint[32] = { + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_NONE, }, /* reserved */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ + { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ +}; -static struct s3c_irq_data init_base[32] = { +#ifdef CONFIG_CPU_S3C2410 +static struct s3c_irq_data init_s3c2410base[32] = { { .type = S3C_IRQTYPE_EINT, }, /* EINT0 */ { .type = S3C_IRQTYPE_EINT, }, /* EINT1 */ { .type = S3C_IRQTYPE_EINT, }, /* EINT2 */ @@ -549,34 +572,7 @@ static struct s3c_irq_data init_base[32] = { { .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */ }; -static struct s3c_irq_data init_eint[32] = { - { .type = S3C_IRQTYPE_NONE, }, /* reserved */ - { .type = S3C_IRQTYPE_NONE, }, /* reserved */ - { .type = S3C_IRQTYPE_NONE, }, /* reserved */ - { .type = S3C_IRQTYPE_NONE, }, /* reserved */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */ - { .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */ -}; - -static struct s3c_irq_data init_subint[32] = { +static struct s3c_irq_data init_s3c2410subint[32] = { { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */ { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */ { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */ @@ -590,7 +586,7 @@ static struct s3c_irq_data init_subint[32] = { { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ }; -void __init s3c24xx_init_irq(void) +void __init s3c2410_init_irq(void) { struct s3c_irq_intc *main_intc; @@ -598,15 +594,16 @@ void __init s3c24xx_init_irq(void) init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_base[0], NULL, 0x4a000000); + main_intc = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, 0x4a000000); if (IS_ERR(main_intc)) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_s3c2410subint[0], main_intc, 0x4a000018); s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); } +#endif #ifdef CONFIG_CPU_S3C2412 static struct s3c_irq_data init_s3c2412base[32] = { diff --git a/arch/arm/mach-s3c24xx/mach-amlm5900.c b/arch/arm/mach-s3c24xx/mach-amlm5900.c index 432144cb54ae..e27b5c91b3db 100644 --- a/arch/arm/mach-s3c24xx/mach-amlm5900.c +++ b/arch/arm/mach-s3c24xx/mach-amlm5900.c @@ -238,7 +238,7 @@ static void __init amlm5900_init(void) MACHINE_START(AML_M5900, "AML_M5900") .atag_offset = 0x100, .map_io = amlm5900_map_io, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .init_machine = amlm5900_init, .init_time = samsung_timer_init, .restart = s3c2410_restart, diff --git a/arch/arm/mach-s3c24xx/mach-bast.c b/arch/arm/mach-s3c24xx/mach-bast.c index eabe2db42ef6..22d6ae926d91 100644 --- a/arch/arm/mach-s3c24xx/mach-bast.c +++ b/arch/arm/mach-s3c24xx/mach-bast.c @@ -605,7 +605,7 @@ MACHINE_START(BAST, "Simtec-BAST") /* Maintainer: Ben Dooks */ .atag_offset = 0x100, .map_io = bast_map_io, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .init_machine = bast_init, .init_time = samsung_timer_init, .restart = s3c2410_restart, diff --git a/arch/arm/mach-s3c24xx/mach-h1940.c b/arch/arm/mach-s3c24xx/mach-h1940.c index 8dd660102846..af4334d6b4d5 100644 --- a/arch/arm/mach-s3c24xx/mach-h1940.c +++ b/arch/arm/mach-s3c24xx/mach-h1940.c @@ -667,11 +667,6 @@ static void __init h1940_reserve(void) memblock_reserve(0x30081000, 0x1000); } -static void __init h1940_init_irq(void) -{ - s3c24xx_init_irq(); -} - static void __init h1940_init(void) { u32 tmp; @@ -740,7 +735,7 @@ MACHINE_START(H1940, "IPAQ-H1940") .atag_offset = 0x100, .map_io = h1940_map_io, .reserve = h1940_reserve, - .init_irq = h1940_init_irq, + .init_irq = s3c2410_init_irq, .init_machine = h1940_init, .init_time = samsung_timer_init, .restart = s3c2410_restart, diff --git a/arch/arm/mach-s3c24xx/mach-n30.c b/arch/arm/mach-s3c24xx/mach-n30.c index 73a690f431e6..2cb46c37c920 100644 --- a/arch/arm/mach-s3c24xx/mach-n30.c +++ b/arch/arm/mach-s3c24xx/mach-n30.c @@ -592,7 +592,7 @@ MACHINE_START(N30, "Acer-N30") .atag_offset = 0x100, .init_time = samsung_timer_init, .init_machine = n30_init, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .map_io = n30_map_io, .restart = s3c2410_restart, MACHINE_END @@ -603,7 +603,7 @@ MACHINE_START(N35, "Acer-N35") .atag_offset = 0x100, .init_time = samsung_timer_init, .init_machine = n30_init, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .map_io = n30_map_io, .restart = s3c2410_restart, MACHINE_END diff --git a/arch/arm/mach-s3c24xx/mach-otom.c b/arch/arm/mach-s3c24xx/mach-otom.c index 7b8670746b6a..7e16b0740ec1 100644 --- a/arch/arm/mach-s3c24xx/mach-otom.c +++ b/arch/arm/mach-s3c24xx/mach-otom.c @@ -116,7 +116,7 @@ MACHINE_START(OTOM, "Nex Vision - Otom 1.1") .atag_offset = 0x100, .map_io = otom11_map_io, .init_machine = otom11_init, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .init_time = samsung_timer_init, .restart = s3c2410_restart, MACHINE_END diff --git a/arch/arm/mach-s3c24xx/mach-qt2410.c b/arch/arm/mach-s3c24xx/mach-qt2410.c index 71cf29b12d1f..f8feaeadb55a 100644 --- a/arch/arm/mach-s3c24xx/mach-qt2410.c +++ b/arch/arm/mach-s3c24xx/mach-qt2410.c @@ -343,7 +343,7 @@ static void __init qt2410_machine_init(void) MACHINE_START(QT2410, "QT2410") .atag_offset = 0x100, .map_io = qt2410_map_io, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .init_machine = qt2410_machine_init, .init_time = samsung_timer_init, .restart = s3c2410_restart, diff --git a/arch/arm/mach-s3c24xx/mach-smdk2410.c b/arch/arm/mach-s3c24xx/mach-smdk2410.c index fd96f7fc330c..a773789e4f38 100644 --- a/arch/arm/mach-s3c24xx/mach-smdk2410.c +++ b/arch/arm/mach-s3c24xx/mach-smdk2410.c @@ -116,7 +116,7 @@ MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switc /* Maintainer: Jonas Dietsche */ .atag_offset = 0x100, .map_io = smdk2410_map_io, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .init_machine = smdk2410_init, .init_time = samsung_timer_init, .restart = s3c2410_restart, diff --git a/arch/arm/mach-s3c24xx/mach-tct_hammer.c b/arch/arm/mach-s3c24xx/mach-tct_hammer.c index 31dfe589e349..7fad8f055cab 100644 --- a/arch/arm/mach-s3c24xx/mach-tct_hammer.c +++ b/arch/arm/mach-s3c24xx/mach-tct_hammer.c @@ -149,7 +149,7 @@ static void __init tct_hammer_init(void) MACHINE_START(TCT_HAMMER, "TCT_HAMMER") .atag_offset = 0x100, .map_io = tct_hammer_map_io, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .init_machine = tct_hammer_init, .init_time = samsung_timer_init, .restart = s3c2410_restart, diff --git a/arch/arm/mach-s3c24xx/mach-vr1000.c b/arch/arm/mach-s3c24xx/mach-vr1000.c index deeb8a0a4034..42e7187fed60 100644 --- a/arch/arm/mach-s3c24xx/mach-vr1000.c +++ b/arch/arm/mach-s3c24xx/mach-vr1000.c @@ -355,7 +355,7 @@ MACHINE_START(VR1000, "Thorcom-VR1000") .atag_offset = 0x100, .map_io = vr1000_map_io, .init_machine = vr1000_init, - .init_irq = s3c24xx_init_irq, + .init_irq = s3c2410_init_irq, .init_time = samsung_timer_init, .restart = s3c2410_restart, MACHINE_END diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h index 0f6c47a6475b..989fefe18be6 100644 --- a/arch/arm/plat-samsung/include/plat/cpu.h +++ b/arch/arm/plat-samsung/include/plat/cpu.h @@ -183,7 +183,6 @@ extern void s3c_init_cpu(unsigned long idcode, /* core initialisation functions */ -extern void s3c24xx_init_irq(void); extern void s5p_init_irq(u32 *vic, u32 num_vic); extern void s3c24xx_init_io(struct map_desc *mach_desc, int size); From 3d3eb5a476a55e9ef1aeaa6a3c2b00587ed32521 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Mar 2013 12:38:22 +0900 Subject: [PATCH 04/12] ARM: S3C24XX: make s3c24xx_init_intc static It's not used anywhere else. Signed-off-by: Heiko Stuebner Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c24xx/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-s3c24xx/irq.c b/arch/arm/mach-s3c24xx/irq.c index ee4765403ab0..8bc29313c341 100644 --- a/arch/arm/mach-s3c24xx/irq.c +++ b/arch/arm/mach-s3c24xx/irq.c @@ -428,7 +428,7 @@ static void s3c24xx_clear_intc(struct s3c_irq_intc *intc) } } -struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, +static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, struct s3c_irq_data *irq_data, struct s3c_irq_intc *parent, unsigned long address) From 17453dd2e7df20612770ebbf1ab5d506a432210c Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 7 Mar 2013 12:38:25 +0900 Subject: [PATCH 05/12] ARM: S3C24XX: add handle_irq function This removes the dependency on static irq mappings for basic irq handling and makes the s3c24xx entry-macro.S obsolete. Also the interrupts of the second full interrupt controller on the s3c2416 are really handled now, which was forgotten when adding them. The handling itself does the same as the previous assembler-code in that it tries to get the interrupt offset from the offset register first and if that produces wrong results manually searches for the interrupt bit in the pending register value. It also saves the historic comment which explains the reason behind this. Signed-off-by: Heiko Stuebner Signed-off-by: Kukjin Kim --- arch/arm/Kconfig | 1 + .../mach-s3c24xx/include/mach/entry-macro.S | 70 ------------------- arch/arm/mach-s3c24xx/irq.c | 58 +++++++++++++++ 3 files changed, 59 insertions(+), 70 deletions(-) delete mode 100644 arch/arm/mach-s3c24xx/include/mach/entry-macro.S diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 46fcfa8805f8..6820ffde359f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -778,6 +778,7 @@ config ARCH_S3C24XX select HAVE_S3C2410_I2C if I2C select HAVE_S3C2410_WATCHDOG if WATCHDOG select HAVE_S3C_RTC if RTC_CLASS + select MULTI_IRQ_HANDLER select NEED_MACH_GPIO_H select NEED_MACH_IO_H help diff --git a/arch/arm/mach-s3c24xx/include/mach/entry-macro.S b/arch/arm/mach-s3c24xx/include/mach/entry-macro.S deleted file mode 100644 index 6a21beeba1da..000000000000 --- a/arch/arm/mach-s3c24xx/include/mach/entry-macro.S +++ /dev/null @@ -1,70 +0,0 @@ -/* - * arch/arm/mach-s3c2410/include/mach/entry-macro.S - * - * Low-level IRQ helper macros for S3C2410-based platforms - * - * 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. -*/ - -/* We have a problem that the INTOFFSET register does not always - * show one interrupt. Occasionally we get two interrupts through - * the prioritiser, and this causes the INTOFFSET register to show - * what looks like the logical-or of the two interrupt numbers. - * - * Thanks to Klaus, Shannon, et al for helping to debug this problem -*/ - -#define INTPND (0x10) -#define INTOFFSET (0x14) - -#include -#include - - .macro get_irqnr_preamble, base, tmp - .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - - mov \base, #S3C24XX_VA_IRQ - - @@ try the interrupt offset register, since it is there - - ldr \irqstat, [\base, #INTPND ] - teq \irqstat, #0 - beq 1002f - ldr \irqnr, [\base, #INTOFFSET ] - mov \tmp, #1 - tst \irqstat, \tmp, lsl \irqnr - bne 1001f - - @@ the number specified is not a valid irq, so try - @@ and work it out for ourselves - - mov \irqnr, #0 @@ start here - - @@ work out which irq (if any) we got - - movs \tmp, \irqstat, lsl#16 - addeq \irqnr, \irqnr, #16 - moveq \irqstat, \irqstat, lsr#16 - tst \irqstat, #0xff - addeq \irqnr, \irqnr, #8 - moveq \irqstat, \irqstat, lsr#8 - tst \irqstat, #0xf - addeq \irqnr, \irqnr, #4 - moveq \irqstat, \irqstat, lsr#4 - tst \irqstat, #0x3 - addeq \irqnr, \irqnr, #2 - moveq \irqstat, \irqstat, lsr#2 - tst \irqstat, #0x1 - addeq \irqnr, \irqnr, #1 - - @@ we have the value -1001: - adds \irqnr, \irqnr, #IRQ_EINT0 -1002: - @@ exit here, Z flag unset if IRQ - - .endm diff --git a/arch/arm/mach-s3c24xx/irq.c b/arch/arm/mach-s3c24xx/irq.c index 8bc29313c341..5c9f8b7a1fd6 100644 --- a/arch/arm/mach-s3c24xx/irq.c +++ b/arch/arm/mach-s3c24xx/irq.c @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -282,6 +283,56 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } +static struct s3c_irq_intc *main_intc; +static struct s3c_irq_intc *main_intc2; + +static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, + struct pt_regs *regs) +{ + int pnd; + int offset; + int irq; + + pnd = __raw_readl(intc->reg_intpnd); + if (!pnd) + return false; + + /* We have a problem that the INTOFFSET register does not always + * show one interrupt. Occasionally we get two interrupts through + * the prioritiser, and this causes the INTOFFSET register to show + * what looks like the logical-or of the two interrupt numbers. + * + * Thanks to Klaus, Shannon, et al for helping to debug this problem + */ + offset = __raw_readl(intc->reg_intpnd + 4); + + /* Find the bit manually, when the offset is wrong. + * The pending register only ever contains the one bit of the next + * interrupt to handle. + */ + if (!(pnd & (1 << offset))) + offset = __ffs(pnd); + + irq = irq_find_mapping(intc->domain, offset); + handle_IRQ(irq, regs); + return true; +} + +asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) +{ + do { + if (likely(main_intc)) + if (s3c24xx_handle_intc(main_intc, regs)) + continue; + + if (main_intc2) + if (s3c24xx_handle_intc(main_intc2, regs)) + continue; + + break; + } while (1); +} + #ifdef CONFIG_FIQ /** * s3c24xx_set_fiq - set the FIQ routing @@ -502,6 +553,13 @@ static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, goto err; } + if (address == 0x4a000000) + main_intc = intc; + else if (address == 0x4a000040) + main_intc2 = intc; + + set_handle_irq(s3c24xx_handle_irq); + return intc; err: From 8a407835bef6d47dcef9594d8c85900f994fbedf Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:33 +0900 Subject: [PATCH 06/12] ARM: S3C24XX: move irq driver to drivers/irqchip This move is necessary to make use of the irqchip infrastructure for the following devicetree support for s3c24xx architectures. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c24xx/Makefile | 2 +- drivers/irqchip/Makefile | 1 + arch/arm/mach-s3c24xx/irq.c => drivers/irqchip/irq-s3c24xx.c | 0 3 files changed, 2 insertions(+), 1 deletion(-) rename arch/arm/mach-s3c24xx/irq.c => drivers/irqchip/irq-s3c24xx.c (100%) diff --git a/arch/arm/mach-s3c24xx/Makefile b/arch/arm/mach-s3c24xx/Makefile index be6e4d0e6f1a..6f46ecfc8396 100644 --- a/arch/arm/mach-s3c24xx/Makefile +++ b/arch/arm/mach-s3c24xx/Makefile @@ -14,7 +14,7 @@ obj- := # core -obj-y += common.o irq.o +obj-y += common.o obj-$(CONFIG_CPU_S3C2410) += s3c2410.o obj-$(CONFIG_S3C2410_CPUFREQ) += cpufreq-s3c2410.o diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87bdf1b..4d65a21eb9b8 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o +obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o diff --git a/arch/arm/mach-s3c24xx/irq.c b/drivers/irqchip/irq-s3c24xx.c similarity index 100% rename from arch/arm/mach-s3c24xx/irq.c rename to drivers/irqchip/irq-s3c24xx.c From e2714f79edb8ed4f9afd09ca48bf2abaa994b1c1 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:37 +0900 Subject: [PATCH 07/12] irqchip: s3c24xx: fix comments on some camera interrupts Might be confusing for people to read the code without having the datasheet nearby. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 5c9f8b7a1fd6..84afbc16fcc3 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -916,8 +916,8 @@ static struct s3c_irq_data init_s3c2440subint[32] = { { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* TC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */ { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */ }; @@ -991,8 +991,8 @@ static struct s3c_irq_data init_s3c2442subint[32] = { { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */ { .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* TC */ - { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* ADC */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */ + { .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */ }; void __init s3c2442_init_irq(void) From 1ebc7e83b1cc58edae3f371ac9cf1614e0d3b345 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:41 +0900 Subject: [PATCH 08/12] irqchip: s3c24xx: fix irqlist of second s3c2416 controller The list in used was from the s3c2450, a close cousin of the s3c2416. As it's not possible to distinguish between the s3c2416 and s3c2450 the additional interrupts of the s3c2450 will only be available thru devicetree later. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 84afbc16fcc3..a565eb8def30 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -835,13 +835,12 @@ static struct s3c_irq_data init_s3c2416subint[32] = { static struct s3c_irq_data init_s3c2416_second[32] = { { .type = S3C_IRQTYPE_EDGE }, /* 2D */ - { .type = S3C_IRQTYPE_EDGE }, /* IIC1 */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_EDGE }, /* PCM0 */ - { .type = S3C_IRQTYPE_EDGE }, /* PCM1 */ + { .type = S3C_IRQTYPE_NONE }, /* reserved */ { .type = S3C_IRQTYPE_EDGE }, /* I2S0 */ - { .type = S3C_IRQTYPE_EDGE }, /* I2S1 */ }; void __init s3c2416_init_irq(void) From bd7c0da2c1590ea292052b873f93d2c1dbbf7d28 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:45 +0900 Subject: [PATCH 09/12] irqchip: s3c24xx: add irq_set_type callback for basic interrupt types Enables post-init setting of the desired typehandler for the interrupt. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index a565eb8def30..7cba4f0090f2 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -123,6 +123,28 @@ static inline void s3c_irq_ack(struct irq_data *data) __raw_writel(bitval, intc->reg_intpnd); } +static int s3c_irq_type(struct irq_data *data, unsigned int type) +{ + switch (type) { + case IRQ_TYPE_NONE: + break; + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + irq_set_handler(data->irq, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + case IRQ_TYPE_LEVEL_HIGH: + irq_set_handler(data->irq, handle_level_irq); + break; + default: + pr_err("No such irq type %d", type); + return -EINVAL; + } + + return 0; +} + static int s3c_irqext_type_set(void __iomem *gpcon_reg, void __iomem *extint_reg, unsigned long gpcon_offset, @@ -228,6 +250,7 @@ static struct irq_chip s3c_irq_chip = { .irq_ack = s3c_irq_ack, .irq_mask = s3c_irq_mask, .irq_unmask = s3c_irq_unmask, + .irq_set_type = s3c_irq_type, .irq_set_wake = s3c_irq_wake }; @@ -236,6 +259,7 @@ static struct irq_chip s3c_irq_level_chip = { .irq_mask = s3c_irq_mask, .irq_unmask = s3c_irq_unmask, .irq_ack = s3c_irq_ack, + .irq_set_type = s3c_irq_type, }; static struct irq_chip s3c_irqext_chip = { From 658dc8fb9cbc7712b1518ce79211ffadcabd1c9e Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:49 +0900 Subject: [PATCH 10/12] irqchip: s3c24xx: globally keep track of the created intc instances For dt-enabled machines we want to use a big irq_domain over all controllers and therefore need to access not only the main controllers but the sub-controller as well. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 99 ++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 7cba4f0090f2..9914abdb8214 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -69,6 +69,14 @@ struct s3c_irq_intc { struct s3c_irq_data *irqs; }; +/* + * Array holding pointers to the global controller structs + * [0] ... main_intc + * [1] ... sub_intc + * [2] ... main_intc2 on s3c2416 + */ +static struct s3c_irq_intc *s3c_intc[3]; + static void s3c_irq_mask(struct irq_data *data) { struct s3c_irq_intc *intc = data->domain->host_data; @@ -307,9 +315,6 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } -static struct s3c_irq_intc *main_intc; -static struct s3c_irq_intc *main_intc2; - static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, struct pt_regs *regs) { @@ -345,12 +350,12 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) { do { - if (likely(main_intc)) - if (s3c24xx_handle_intc(main_intc, regs)) + if (likely(s3c_intc[0])) + if (s3c24xx_handle_intc(s3c_intc[0], regs)) continue; - if (main_intc2) - if (s3c24xx_handle_intc(main_intc2, regs)) + if (s3c_intc[2]) + if (s3c24xx_handle_intc(s3c_intc[2], regs)) continue; break; @@ -577,11 +582,6 @@ static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np, goto err; } - if (address == 0x4a000000) - main_intc = intc; - else if (address == 0x4a000040) - main_intc2 = intc; - set_handle_irq(s3c24xx_handle_irq); return intc; @@ -670,20 +670,20 @@ static struct s3c_irq_data init_s3c2410subint[32] = { void __init s3c2410_init_irq(void) { - struct s3c_irq_intc *main_intc; - #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_s3c2410subint[0], main_intc, 0x4a000018); - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2410subint[0], + s3c_intc[0], 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); } #endif @@ -770,22 +770,22 @@ static struct s3c_irq_data init_s3c2412subint[32] = { void s3c2412_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2412: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_s3c2412eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2412subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_s3c2412eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2412subint[0], + s3c_intc[0], 0x4a000018); } #endif @@ -869,24 +869,25 @@ static struct s3c_irq_data init_s3c2416_second[32] = { void __init s3c2416_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2416: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2416subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2416subint[0], + s3c_intc[0], 0x4a000018); - s3c24xx_init_intc(NULL, &init_s3c2416_second[0], NULL, 0x4a000040); + s3c_intc[2] = s3c24xx_init_intc(NULL, &init_s3c2416_second[0], + NULL, 0x4a000040); } #endif @@ -947,22 +948,22 @@ static struct s3c_irq_data init_s3c2440subint[32] = { void __init s3c2440_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2440: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2440subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0], + s3c_intc[0], 0x4a000018); } #endif @@ -1020,22 +1021,22 @@ static struct s3c_irq_data init_s3c2442subint[32] = { void __init s3c2442_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2442: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2442subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2442subint[0], + s3c_intc[0], 0x4a000018); } #endif @@ -1110,21 +1111,21 @@ static struct s3c_irq_data init_s3c2443subint[32] = { void __init s3c2443_init_irq(void) { - struct s3c_irq_intc *main_intc; - pr_info("S3C2443: IRQ Support\n"); #ifdef CONFIG_FIQ init_FIQ(FIQ_START); #endif - main_intc = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL, 0x4a000000); - if (IS_ERR(main_intc)) { + s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL, + 0x4a000000); + if (IS_ERR(s3c_intc[0])) { pr_err("irq: could not create main interrupt controller\n"); return; } - s3c24xx_init_intc(NULL, &init_eint[0], main_intc, 0x560000a4); - s3c24xx_init_intc(NULL, &init_s3c2443subint[0], main_intc, 0x4a000018); + s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4); + s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2443subint[0], + s3c_intc[0], 0x4a000018); } #endif From f5a25524508e68ac670b28db9112d3962dca4703 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:53:52 +0900 Subject: [PATCH 11/12] irqchip: s3c24xx: make interrupt handling independent of irq_domain structure Keep a pointer to the corresponding s3c_irq_data struct as irq_chip_data. This removes the need to fetch the intc struct from the irq_domains host_data, thus making it independent of the underlying irq_domain structure. Also keep the real register offset of the interrupt in the s3c_irq_data struct to make it independent of the hwirq structure in the irq_domain Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Signed-off-by: Kukjin Kim --- drivers/irqchip/irq-s3c24xx.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 9914abdb8214..02b82db086d8 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -43,6 +43,7 @@ struct s3c_irq_data { unsigned int type; + unsigned long offset; unsigned long parent_irq; /* data gets filled during init */ @@ -79,15 +80,15 @@ static struct s3c_irq_intc *s3c_intc[3]; static void s3c_irq_mask(struct irq_data *data) { - struct s3c_irq_intc *intc = data->domain->host_data; + struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); + struct s3c_irq_intc *intc = irq_data->intc; struct s3c_irq_intc *parent_intc = intc->parent; - struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq]; struct s3c_irq_data *parent_data; unsigned long mask; unsigned int irqno; mask = __raw_readl(intc->reg_mask); - mask |= (1UL << data->hwirq); + mask |= (1UL << irq_data->offset); __raw_writel(mask, intc->reg_mask); if (parent_intc) { @@ -104,14 +105,14 @@ static void s3c_irq_mask(struct irq_data *data) static void s3c_irq_unmask(struct irq_data *data) { - struct s3c_irq_intc *intc = data->domain->host_data; + struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); + struct s3c_irq_intc *intc = irq_data->intc; struct s3c_irq_intc *parent_intc = intc->parent; - struct s3c_irq_data *irq_data = &intc->irqs[data->hwirq]; unsigned long mask; unsigned int irqno; mask = __raw_readl(intc->reg_mask); - mask &= ~(1UL << data->hwirq); + mask &= ~(1UL << irq_data->offset); __raw_writel(mask, intc->reg_mask); if (parent_intc) { @@ -123,8 +124,9 @@ static void s3c_irq_unmask(struct irq_data *data) static inline void s3c_irq_ack(struct irq_data *data) { - struct s3c_irq_intc *intc = data->domain->host_data; - unsigned long bitval = 1UL << data->hwirq; + struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data); + struct s3c_irq_intc *intc = irq_data->intc; + unsigned long bitval = 1UL << irq_data->offset; __raw_writel(bitval, intc->reg_pending); if (intc->reg_intpnd) @@ -291,8 +293,7 @@ static struct irq_chip s3c_irq_eint0t4 = { static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); - struct s3c_irq_intc *intc = desc->irq_data.domain->host_data; - struct s3c_irq_data *irq_data = &intc->irqs[desc->irq_data.hwirq]; + struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); struct s3c_irq_intc *sub_intc = irq_data->sub_intc; unsigned long src; unsigned long msk; @@ -406,6 +407,7 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, /* attach controller pointer to irq_data */ irq_data->intc = intc; + irq_data->offset = hw; parent_intc = intc->parent; @@ -444,6 +446,9 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, pr_err("irq-s3c24xx: unsupported irqtype %d\n", irq_data->type); return -EINVAL; } + + irq_set_chip_data(virq, irq_data); + set_irq_flags(virq, IRQF_VALID); if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) { From f0774d41da0e607b70e54ecc50aeb6684f54c2b1 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 4 Apr 2013 14:55:10 +0900 Subject: [PATCH 12/12] irqchip: s3c24xx: add devicetree support Add the necessary code to initialize the interrupt controller thru devicetree data using the irqchip infrastructure. Signed-off-by: Heiko Stuebner Acked-by: Arnd Bergmann Acked-by: Rob Herring Signed-off-by: Kukjin Kim --- .../samsung,s3c24xx-irq.txt | 53 ++++ drivers/irqchip/irq-s3c24xx.c | 231 +++++++++++++++++- 2 files changed, 278 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt diff --git a/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt new file mode 100644 index 000000000000..c54c5a9a2a90 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt @@ -0,0 +1,53 @@ +Samsung S3C24XX Interrupt Controllers + +The S3C24XX SoCs contain a custom set of interrupt controllers providing a +varying number of interrupt sources. The set consists of a main- and sub- +controller and on newer SoCs even a second main controller. + +Required properties: +- compatible: Compatible property value should be "samsung,s3c2410-irq" + for machines before s3c2416 and "samsung,s3c2416-irq" for s3c2416 and later. + +- reg: Physical base address of the controller and length of memory mapped + region. + +- interrupt-controller : Identifies the node as an interrupt controller + +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 4 and interrupt descriptor shall + have the following format: + + + ctrl_num contains the controller to use: + - 0 ... main controller + - 1 ... sub controller + - 2 ... second main controller on s3c2416 and s3c2450 + parent_irq contains the parent bit in the main controller and will be + ignored in main controllers + ctrl_irq contains the interrupt bit of the controller + type contains the trigger type to use + +Example: + + interrupt-controller@4a000000 { + compatible = "samsung,s3c2410-irq"; + reg = <0x4a000000 0x100>; + interrupt-controller; + #interrupt-cells=<4>; + }; + + [...] + + serial@50000000 { + compatible = "samsung,s3c2410-uart"; + reg = <0x50000000 0x4000>; + interrupt-parent = <&subintc>; + interrupts = <1 28 0 4>, <1 28 1 4>; + }; + + rtc@57000000 { + compatible = "samsung,s3c2410-rtc"; + reg = <0x57000000 0x100>; + interrupt-parent = <&intc>; + interrupts = <0 30 0 3>, <0 8 0 3>; + }; diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 02b82db086d8..5e40b3424df8 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include @@ -36,6 +39,8 @@ #include #include +#include "irqchip.h" + #define S3C_IRQTYPE_NONE 0 #define S3C_IRQTYPE_EINT 1 #define S3C_IRQTYPE_EDGE 2 @@ -94,7 +99,10 @@ static void s3c_irq_mask(struct irq_data *data) if (parent_intc) { parent_data = &parent_intc->irqs[irq_data->parent_irq]; - /* check to see if we need to mask the parent IRQ */ + /* check to see if we need to mask the parent IRQ + * The parent_irq is always in main_intc, so the hwirq + * for find_mapping does not need an offset in any case. + */ if ((mask & parent_data->sub_bits) == parent_data->sub_bits) { irqno = irq_find_mapping(parent_intc->domain, irq_data->parent_irq); @@ -294,10 +302,18 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); + struct s3c_irq_intc *intc = irq_data->intc; struct s3c_irq_intc *sub_intc = irq_data->sub_intc; unsigned long src; unsigned long msk; unsigned int n; + unsigned int offset; + + /* we're using individual domains for the non-dt case + * and one big domain for the dt case where the subintc + * starts at hwirq number 32. + */ + offset = (intc->domain->of_node) ? 32 : 0; chained_irq_enter(chip, desc); @@ -310,14 +326,15 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc) while (src) { n = __ffs(src); src &= ~(1 << n); - generic_handle_irq(irq_find_mapping(sub_intc->domain, n)); + irq = irq_find_mapping(sub_intc->domain, offset + n); + generic_handle_irq(irq); } chained_irq_exit(chip, desc); } static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, - struct pt_regs *regs) + struct pt_regs *regs, int intc_offset) { int pnd; int offset; @@ -327,6 +344,10 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, if (!pnd) return false; + /* non-dt machines use individual domains */ + if (!intc->domain->of_node) + intc_offset = 0; + /* We have a problem that the INTOFFSET register does not always * show one interrupt. Occasionally we get two interrupts through * the prioritiser, and this causes the INTOFFSET register to show @@ -343,7 +364,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, if (!(pnd & (1 << offset))) offset = __ffs(pnd); - irq = irq_find_mapping(intc->domain, offset); + irq = irq_find_mapping(intc->domain, intc_offset + offset); handle_IRQ(irq, regs); return true; } @@ -352,11 +373,11 @@ asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs) { do { if (likely(s3c_intc[0])) - if (s3c24xx_handle_intc(s3c_intc[0], regs)) + if (s3c24xx_handle_intc(s3c_intc[0], regs, 0)) continue; if (s3c_intc[2]) - if (s3c24xx_handle_intc(s3c_intc[2], regs)) + if (s3c24xx_handle_intc(s3c_intc[2], regs, 64)) continue; break; @@ -1134,3 +1155,201 @@ void __init s3c2443_init_irq(void) s3c_intc[0], 0x4a000018); } #endif + +#ifdef CONFIG_OF +static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + unsigned int ctrl_num = hw / 32; + unsigned int intc_hw = hw % 32; + struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; + struct s3c_irq_intc *parent_intc = intc->parent; + struct s3c_irq_data *irq_data = &intc->irqs[intc_hw]; + + /* attach controller pointer to irq_data */ + irq_data->intc = intc; + irq_data->offset = intc_hw; + + if (!parent_intc) + irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq); + else + irq_set_chip_and_handler(virq, &s3c_irq_level_chip, + handle_edge_irq); + + irq_set_chip_data(virq, irq_data); + + set_irq_flags(virq, IRQF_VALID); + + return 0; +} + +/* Translate our of irq notation + * format: + */ +static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) +{ + struct s3c_irq_intc *intc; + struct s3c_irq_intc *parent_intc; + struct s3c_irq_data *irq_data; + struct s3c_irq_data *parent_irq_data; + int irqno; + + if (WARN_ON(intsize < 4)) + return -EINVAL; + + if (intspec[0] > 2 || !s3c_intc[intspec[0]]) { + pr_err("controller number %d invalid\n", intspec[0]); + return -EINVAL; + } + intc = s3c_intc[intspec[0]]; + + *out_hwirq = intspec[0] * 32 + intspec[2]; + *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; + + parent_intc = intc->parent; + if (parent_intc) { + irq_data = &intc->irqs[intspec[2]]; + irq_data->parent_irq = intspec[1]; + parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; + parent_irq_data->sub_intc = intc; + parent_irq_data->sub_bits |= (1UL << intspec[2]); + + /* parent_intc is always s3c_intc[0], so no offset */ + irqno = irq_create_mapping(parent_intc->domain, intspec[1]); + if (irqno < 0) { + pr_err("irq: could not map parent interrupt\n"); + return irqno; + } + + irq_set_chained_handler(irqno, s3c_irq_demux); + } + + return 0; +} + +static struct irq_domain_ops s3c24xx_irq_ops_of = { + .map = s3c24xx_irq_map_of, + .xlate = s3c24xx_irq_xlate_of, +}; + +struct s3c24xx_irq_of_ctrl { + char *name; + unsigned long offset; + struct s3c_irq_intc **handle; + struct s3c_irq_intc **parent; + struct irq_domain_ops *ops; +}; + +static int __init s3c_init_intc_of(struct device_node *np, + struct device_node *interrupt_parent, + struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl) +{ + struct s3c_irq_intc *intc; + struct s3c24xx_irq_of_ctrl *ctrl; + struct irq_domain *domain; + void __iomem *reg_base; + int i; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_err("irq-s3c24xx: could not map irq registers\n"); + return -EINVAL; + } + + domain = irq_domain_add_linear(np, num_ctrl * 32, + &s3c24xx_irq_ops_of, NULL); + if (!domain) { + pr_err("irq: could not create irq-domain\n"); + return -EINVAL; + } + + for (i = 0; i < num_ctrl; i++) { + ctrl = &s3c_ctrl[i]; + + pr_debug("irq: found controller %s\n", ctrl->name); + + intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); + if (!intc) + return -ENOMEM; + + intc->domain = domain; + intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32, + GFP_KERNEL); + if (!intc->irqs) { + kfree(intc); + return -ENOMEM; + } + + if (ctrl->parent) { + intc->reg_pending = reg_base + ctrl->offset; + intc->reg_mask = reg_base + ctrl->offset + 0x4; + + if (*(ctrl->parent)) { + intc->parent = *(ctrl->parent); + } else { + pr_warn("irq: parent of %s missing\n", + ctrl->name); + kfree(intc->irqs); + kfree(intc); + continue; + } + } else { + intc->reg_pending = reg_base + ctrl->offset; + intc->reg_mask = reg_base + ctrl->offset + 0x08; + intc->reg_intpnd = reg_base + ctrl->offset + 0x10; + } + + s3c24xx_clear_intc(intc); + s3c_intc[i] = intc; + } + + set_handle_irq(s3c24xx_handle_irq); + + return 0; +} + +static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { + { + .name = "intc", + .offset = 0, + }, { + .name = "subintc", + .offset = 0x18, + .parent = &s3c_intc[0], + } +}; + +int __init s3c2410_init_intc_of(struct device_node *np, + struct device_node *interrupt_parent, + struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) +{ + return s3c_init_intc_of(np, interrupt_parent, + s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl)); +} +IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of); + +static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = { + { + .name = "intc", + .offset = 0, + }, { + .name = "subintc", + .offset = 0x18, + .parent = &s3c_intc[0], + }, { + .name = "intc2", + .offset = 0x40, + } +}; + +int __init s3c2416_init_intc_of(struct device_node *np, + struct device_node *interrupt_parent, + struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) +{ + return s3c_init_intc_of(np, interrupt_parent, + s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl)); +} +IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of); +#endif