* [PATCH v5 1/7] ARM: S3C24XX: move irq driver to drivers/irqchip
2013-03-25 21:26 [PATCH v5 0/7] move s3c24xx-irq to drivers/irqchip and add dt support Heiko Stübner
@ 2013-03-25 21:27 ` Heiko Stübner
2013-03-25 21:27 ` [PATCH v5 2/7] irqchip: s3c24xx: fix comments on some camera interrupts Heiko Stübner
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2013-03-25 21:27 UTC (permalink / raw)
To: Kukjin Kim
Cc: Grant Likely, Rob Herring, Thomas Abraham, Arnd Bergmann,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
This move is necessary to make use of the irqchip infrastructure
for the following devicetree support for s3c24xx architectures.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
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 be6e4d0..6f46ecf 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 98e3b87..4d65a21 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
--
1.7.10.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v5 2/7] irqchip: s3c24xx: fix comments on some camera interrupts
2013-03-25 21:26 [PATCH v5 0/7] move s3c24xx-irq to drivers/irqchip and add dt support Heiko Stübner
2013-03-25 21:27 ` [PATCH v5 1/7] ARM: S3C24XX: move irq driver to drivers/irqchip Heiko Stübner
@ 2013-03-25 21:27 ` Heiko Stübner
2013-03-25 21:28 ` [PATCH v5 3/7] irqchip: s3c24xx: fix irqlist of second s3c2416 controller Heiko Stübner
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2013-03-25 21:27 UTC (permalink / raw)
To: Kukjin Kim
Cc: Grant Likely, Rob Herring, Thomas Abraham, Arnd Bergmann,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
Might be confusing for people to read the code without having the
datasheet nearby.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
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 5c9f8b7..84afbc1 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)
--
1.7.10.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v5 3/7] irqchip: s3c24xx: fix irqlist of second s3c2416 controller
2013-03-25 21:26 [PATCH v5 0/7] move s3c24xx-irq to drivers/irqchip and add dt support Heiko Stübner
2013-03-25 21:27 ` [PATCH v5 1/7] ARM: S3C24XX: move irq driver to drivers/irqchip Heiko Stübner
2013-03-25 21:27 ` [PATCH v5 2/7] irqchip: s3c24xx: fix comments on some camera interrupts Heiko Stübner
@ 2013-03-25 21:28 ` Heiko Stübner
2013-03-25 21:29 ` [PATCH v5 4/7] irqchip: s3c24xx: add irq_set_type callback for basic interrupt types Heiko Stübner
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2013-03-25 21:28 UTC (permalink / raw)
To: Kukjin Kim
Cc: Grant Likely, Rob Herring, Thomas Abraham, Arnd Bergmann,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
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 <heiko@sntech.de>
---
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 84afbc1..a565eb8 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)
--
1.7.10.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v5 4/7] irqchip: s3c24xx: add irq_set_type callback for basic interrupt types
2013-03-25 21:26 [PATCH v5 0/7] move s3c24xx-irq to drivers/irqchip and add dt support Heiko Stübner
` (2 preceding siblings ...)
2013-03-25 21:28 ` [PATCH v5 3/7] irqchip: s3c24xx: fix irqlist of second s3c2416 controller Heiko Stübner
@ 2013-03-25 21:29 ` Heiko Stübner
2013-03-25 21:31 ` [PATCH v5 5/7] irqchip: s3c24xx: globally keep track of the created intc instances Heiko Stübner
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2013-03-25 21:29 UTC (permalink / raw)
To: Kukjin Kim
Cc: Grant Likely, Rob Herring, Thomas Abraham, Arnd Bergmann,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
Enables post-init setting of the desired typehandler for the interrupt.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
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 a565eb8..7cba4f0 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 = {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v5 5/7] irqchip: s3c24xx: globally keep track of the created intc instances
2013-03-25 21:26 [PATCH v5 0/7] move s3c24xx-irq to drivers/irqchip and add dt support Heiko Stübner
` (3 preceding siblings ...)
2013-03-25 21:29 ` [PATCH v5 4/7] irqchip: s3c24xx: add irq_set_type callback for basic interrupt types Heiko Stübner
@ 2013-03-25 21:31 ` Heiko Stübner
2013-03-25 21:32 ` [PATCH v5 6/7] irqchip: s3c24xx: make interrupt handling independent of irq_domain structure Heiko Stübner
2013-03-25 21:34 ` [PATCH v5 7/7] irqchip: s3c24xx: add devicetree support Heiko Stübner
6 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2013-03-25 21:31 UTC (permalink / raw)
To: Kukjin Kim
Cc: Grant Likely, Rob Herring, Thomas Abraham, Arnd Bergmann,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
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 <heiko@sntech.de>
---
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 7cba4f0..9914abd 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
--
1.7.10.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v5 6/7] irqchip: s3c24xx: make interrupt handling independent of irq_domain structure
2013-03-25 21:26 [PATCH v5 0/7] move s3c24xx-irq to drivers/irqchip and add dt support Heiko Stübner
` (4 preceding siblings ...)
2013-03-25 21:31 ` [PATCH v5 5/7] irqchip: s3c24xx: globally keep track of the created intc instances Heiko Stübner
@ 2013-03-25 21:32 ` Heiko Stübner
2013-03-25 21:34 ` [PATCH v5 7/7] irqchip: s3c24xx: add devicetree support Heiko Stübner
6 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2013-03-25 21:32 UTC (permalink / raw)
To: Kukjin Kim
Cc: Grant Likely, Rob Herring, Thomas Abraham, Arnd Bergmann,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
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 <heiko@sntech.de>
---
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 9914abd..02b82db 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) {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v5 7/7] irqchip: s3c24xx: add devicetree support
2013-03-25 21:26 [PATCH v5 0/7] move s3c24xx-irq to drivers/irqchip and add dt support Heiko Stübner
` (5 preceding siblings ...)
2013-03-25 21:32 ` [PATCH v5 6/7] irqchip: s3c24xx: make interrupt handling independent of irq_domain structure Heiko Stübner
@ 2013-03-25 21:34 ` Heiko Stübner
2013-03-25 22:00 ` Arnd Bergmann
6 siblings, 1 reply; 10+ messages in thread
From: Heiko Stübner @ 2013-03-25 21:34 UTC (permalink / raw)
To: Kukjin Kim
Cc: Grant Likely, Rob Herring, Thomas Abraham, Arnd Bergmann,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
Add the necessary code to initialize the interrupt controller
thru devicetree data using the irqchip infrastructure.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
.../interrupt-controller/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 0000000..40d9e66
--- /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,s3c24xx-irq"
+ for non s3c2416 machines and "samsung,s3c2416-irq" for s3c2416 machines
+
+- 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 ctrl_irq parent_irq type>
+
+ ctrl_num contains the controller to use:
+ - 0 ... main controller
+ - 1 ... sub controller
+ - 2 ... second main controller on s3c2416 and s3c2450
+ ctrl_irq contains the interrupt bit of the controller
+ parent_irq contains the parent bit in the main controller and will be
+ ignored in main controllers
+ type contains the trigger type to use
+
+Example:
+
+ interrupt-controller@4a000000 {
+ compatible = "samsung,s3c24xx-irq";
+ reg = <0x4a000000 0x100>;
+ interrupt-controller;
+ #interrupt-cells=<4>;
+ };
+
+ [...]
+
+ serial@50000000 {
+ compatible = "samsung,s3c2410-uart";
+ reg = <0x50000000 0x4000>;
+ interrupt-parent = <&subintc>;
+ interrupts = <1 0 28 4>, <1 1 28 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 02b82db..f39e1b7 100644
--- a/drivers/irqchip/irq-s3c24xx.c
+++ b/drivers/irqchip/irq-s3c24xx.c
@@ -25,6 +25,9 @@
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
#include <asm/exception.h>
#include <asm/mach/irq.h>
@@ -36,6 +39,8 @@
#include <plat/regs-irqtype.h>
#include <plat/pm.h>
+#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: <ctrl_num ctrl_irq parent_irq type>
+ */
+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[1];
+ *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
+
+ parent_intc = intc->parent;
+ if (parent_intc) {
+ irq_data = &intc->irqs[intspec[1]];
+ irq_data->parent_irq = intspec[2];
+ parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
+ parent_irq_data->sub_intc = intc;
+ parent_irq_data->sub_bits |= (1UL << intspec[1]);
+
+ /* parent_intc is always s3c_intc[0], so no offset */
+ irqno = irq_create_mapping(parent_intc->domain, intspec[2]);
+ 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 s3c24xx_ctrl[] = {
+ {
+ .name = "intc",
+ .offset = 0,
+ }, {
+ .name = "subintc",
+ .offset = 0x18,
+ .parent = &s3c_intc[0],
+ }
+};
+
+int __init s3c24xx_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,
+ s3c24xx_ctrl, ARRAY_SIZE(s3c24xx_ctrl));
+}
+IRQCHIP_DECLARE(s3c24xx_irq, "samsung,s3c24xx-irq", s3c24xx_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
--
1.7.10.4
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v5 7/7] irqchip: s3c24xx: add devicetree support
2013-03-25 21:34 ` [PATCH v5 7/7] irqchip: s3c24xx: add devicetree support Heiko Stübner
@ 2013-03-25 22:00 ` Arnd Bergmann
2013-03-26 7:38 ` Heiko Stübner
0 siblings, 1 reply; 10+ messages in thread
From: Arnd Bergmann @ 2013-03-25 22:00 UTC (permalink / raw)
To: Heiko Stübner
Cc: Kukjin Kim, Grant Likely, Rob Herring, Thomas Abraham,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
On Monday 25 March 2013, Heiko Stübner wrote:
> Add the necessary code to initialize the interrupt controller
> thru devicetree data using the irqchip infrastructure.
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
The binding looks fine now. I have a few detail comments but am happy
with the series otherwise.
> +Required properties:
> +- compatible: Compatible property value should be "samsung,s3c24xx-irq"
> + for non s3c2416 machines and "samsung,s3c2416-irq" for s3c2416 machines
We try to avoid wildcards in the "compatible" properties. Better use
the name of the first SoC that had this controller, and let the other
ones mark themselves as compatible with that one.
I guess "samsung,s3c2410-irq" would be the right identifier here.
> +- #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 ctrl_irq parent_irq type>
> +
> + ctrl_num contains the controller to use:
> + - 0 ... main controller
> + - 1 ... sub controller
> + - 2 ... second main controller on s3c2416 and s3c2450
> + ctrl_irq contains the interrupt bit of the controller
> + parent_irq contains the parent bit in the main controller and will be
> + ignored in main controllers
I expected the second and third cell to be in the opposite order, so
the meaning of the second cell is always the same.
> + /* 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;
Wouldn't it be easier to always use the same setup for the domains here?
Arnd
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v5 7/7] irqchip: s3c24xx: add devicetree support
2013-03-25 22:00 ` Arnd Bergmann
@ 2013-03-26 7:38 ` Heiko Stübner
0 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2013-03-26 7:38 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Kukjin Kim, Grant Likely, Rob Herring, Thomas Abraham,
devicetree-discuss, linux-arm-kernel, linux-samsung-soc
Hi Arnd,
thanks again for taking the time to look at the changes.
Am Montag, 25. März 2013, 23:00:46 schrieb Arnd Bergmann:
> On Monday 25 March 2013, Heiko Stübner wrote:
> > Add the necessary code to initialize the interrupt controller
> > thru devicetree data using the irqchip infrastructure.
> >
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
>
> The binding looks fine now. I have a few detail comments but am happy
> with the series otherwise.
>
> > +Required properties:
> > +- compatible: Compatible property value should be "samsung,s3c24xx-irq"
> > + for non s3c2416 machines and "samsung,s3c2416-irq" for s3c2416
> > machines
>
> We try to avoid wildcards in the "compatible" properties. Better use
> the name of the first SoC that had this controller, and let the other
> ones mark themselves as compatible with that one.
>
> I guess "samsung,s3c2410-irq" would be the right identifier here.
ok, sounds sensible
> > +- #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 ctrl_irq parent_irq type>
> > +
> > + ctrl_num contains the controller to use:
> > + - 0 ... main controller
> > + - 1 ... sub controller
> > + - 2 ... second main controller on s3c2416 and s3c2450
> > + ctrl_irq contains the interrupt bit of the controller
> > + parent_irq contains the parent bit in the main controller and will be
> > + ignored in main controllers
>
> I expected the second and third cell to be in the opposite order, so
> the meaning of the second cell is always the same.
ok, so we do <ctrl_num parent_irq ctrl_irq type>, right? ... As it only
involves exchanging the intspec values, that's easy :-)
> > + /* 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;
>
> Wouldn't it be easier to always use the same setup for the domains here?
nope ... the non-dt domains are not uniform (different lengths and start-irqs)
to recreate the static irq mappings that are still needed. For the non-dt case
it also implement the handling of the external interrupts that is not part of
the interrupt-controller itself but comes from the gpio-registers and will
move to pinctrl for dt machines.
My hope is still to move to dt in a "reasonable" time, so this stuff can go
away then altogether.
Heiko
^ permalink raw reply [flat|nested] 10+ messages in thread