On 03/17/2013 08:07 AM, Heiko Stübner wrote: > Add the necessary code to initialize the interrupt controller > thru devicetree data using the irqchip infrastructure. > > On dt machines the eint-type interrupts in the main interrupt controller > get mapped as regular edge-types, as their wakeup and interrupt type > properties will be handled by the upcoming pinctrl driver. > > Signed-off-by: Heiko Stuebner <heiko@xxxxxxxxx> > --- > .../interrupt-controller/samsung,s3c24xx-irq.txt | 54 +++++ > drivers/irqchip/irq-s3c24xx.c | 222 ++++++++++++++++++++ > 2 files changed, 276 insertions(+), 0 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..be5dead > --- /dev/null > +++ b/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt > @@ -0,0 +1,54 @@ > +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 one of "samsung,s3c2410-irq", > + "samsung,s3c2412-irq", "samsung,s3c2416-irq", "samsung,s3c2440-irq", > + "samsung,s3c2442-irq", "samsung,s3c2443-irq" depending on the SoC variant. > + > +- reg: Physical base address of the controller and length of memory mapped > + region. > + > +- interrupt-controller : Identifies the node as an interrupt controller > + > +Sub-controllers as child nodes: > + The interrupt controllers that should be referenced by device nodes are > + represented by child nodes. Valid names are intc, subintc and intc2. > + The interrupt values in device nodes are then mapped directly to the > + bit-numbers of the pending register of the named interrupt controller. > + > +Required properties: > +- 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 2. > + > +Example: > + > + interrupt-controller@4a000000 { > + compatible = "samsung,s3c2416-irq"; > + reg = <0x4a000000 0x100>; > + interrupt-controller; > + > + intc:intc { > + interrupt-controller; > + #interrupt-cells = <2>; > + }; > + > + subintc:subintc { > + interrupt-controller; > + #interrupt-cells = <2>; > + }; > + }; > + > + [...] > + > + serial@50000000 { > + compatible = "samsung,s3c2410-uart"; > + reg = <0x50000000 0x4000>; > + interrupt-parent = <&subintc>; > + interrupts = <0 0>, <1 0>; > + }; > diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c > index 1eba289..55cb363 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 > @@ -380,6 +385,10 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq, > > parent_intc = intc->parent; > > + /* on dt platforms the extints get handled by the pinctrl driver */ > + if (h->of_node && irq_data->type == S3C_IRQTYPE_EINT) > + irq_data->type = S3C_IRQTYPE_EDGE; > + > /* set handler and flags */ > switch (irq_data->type) { > case S3C_IRQTYPE_NONE: > @@ -1104,3 +1113,216 @@ void __init s3c2443_init_irq(void) > s3c24xx_init_intc(NULL, &init_s3c2443subint[0], main_intc, 0x4a000018); > } > #endif > + > +#ifdef CONFIG_OF > +struct s3c24xx_irq_of_ctrl { > + char *name; > + unsigned long offset; > + struct s3c_irq_data *irq_data; > + struct s3c_irq_intc **handle; > + struct s3c_irq_intc **parent; > +}; > + > +#define S3C24XX_IRQCTRL(n, o, d, h, p) \ > +{ \ > + .name = n, \ > + .offset = o, \ > + .irq_data = d, \ > + .handle = h, \ > + .parent = p, \ > +} > + > +struct s3c24xx_irq_of_data { > + struct s3c24xx_irq_of_ctrl *irq_ctrl; > + int num_ctrl; > +}; > + > +#ifdef CONFIG_CPU_S3C2410 > +static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { > + S3C24XX_IRQCTRL("intc", 0, init_s3c2410base, &main_intc, NULL), > + S3C24XX_IRQCTRL("subintc", 0x18, init_s3c2410subint, NULL, &main_intc), > +}; > + > +static struct s3c24xx_irq_of_data s3c2410_irq_data = { > + .irq_ctrl = s3c2410_ctrl, > + .num_ctrl = ARRAY_SIZE(s3c2410_ctrl), > +}; > +#endif > + > +#ifdef CONFIG_CPU_S3C2412 > +static struct s3c24xx_irq_of_ctrl s3c2412_ctrl[] = { > + S3C24XX_IRQCTRL("intc", 0, init_s3c2412base, &main_intc, NULL), > + S3C24XX_IRQCTRL("subintc", 0x18, init_s3c2412subint, NULL, &main_intc), > +}; > + > +static struct s3c24xx_irq_of_data s3c2412_irq_data = { > + .irq_ctrl = s3c2412_ctrl, > + .num_ctrl = ARRAY_SIZE(s3c2412_ctrl), > +}; > +#endif > + > +#ifdef CONFIG_CPU_S3C2416 > +static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = { > + S3C24XX_IRQCTRL("intc", 0, init_s3c2416base, &main_intc, NULL), > + S3C24XX_IRQCTRL("subintc", 0x18, init_s3c2416subint, NULL, &main_intc), > + S3C24XX_IRQCTRL("intc2", 0x40, init_s3c2416_second, &main_intc2, NULL), > +}; > + > +static struct s3c24xx_irq_of_data s3c2416_irq_data = { > + .irq_ctrl = s3c2416_ctrl, > + .num_ctrl = ARRAY_SIZE(s3c2416_ctrl), > +}; > +#endif > + > +#ifdef CONFIG_CPU_S3C2440 > +static struct s3c24xx_irq_of_ctrl s3c2440_ctrl[] = { > + S3C24XX_IRQCTRL("intc", 0, init_s3c2440base, &main_intc, NULL), > + S3C24XX_IRQCTRL("subintc", 0x18, init_s3c2440subint, NULL, &main_intc), > +}; > + > +static struct s3c24xx_irq_of_data s3c2440_irq_data = { > + .irq_ctrl = s3c2440_ctrl, > + .num_ctrl = ARRAY_SIZE(s3c2440_ctrl), > +}; > +#endif > + > +#ifdef CONFIG_CPU_S3C2442 > +static struct s3c24xx_irq_of_ctrl s3c2442_ctrl[] = { > + S3C24XX_IRQCTRL("intc", 0, init_s3c2442base, &main_intc, NULL), > + S3C24XX_IRQCTRL("subintc", 0x18, init_s3c2442subint, NULL, &main_intc), > +}; > + > +static struct s3c24xx_irq_of_data s3c2442_irq_data = { > + .irq_ctrl = s3c2442_ctrl, > + .num_ctrl = ARRAY_SIZE(s3c2442_ctrl), > +}; > +#endif > + > +#ifdef CONFIG_CPU_S3C2443 > +static struct s3c24xx_irq_of_ctrl s3c2443_ctrl[] = { > + S3C24XX_IRQCTRL("intc", 0, init_s3c2443base, &main_intc, NULL), > + S3C24XX_IRQCTRL("subintc", 0x18, init_s3c2443subint, NULL, &main_intc), > +}; > + > +static struct s3c24xx_irq_of_data s3c2443_irq_data = { > + .irq_ctrl = s3c2443_ctrl, > + .num_ctrl = ARRAY_SIZE(s3c2443_ctrl), > +}; > +#endif > + > +static const struct of_device_id intc_list[] = { > +#ifdef CONFIG_CPU_S3C2410 > + { .compatible = "samsung,s3c2410-irq", .data = &s3c2410_irq_data }, > +#endif > +#ifdef CONFIG_CPU_S3C2412 > + { .compatible = "samsung,s3c2412-irq", .data = &s3c2412_irq_data }, > +#endif > +#ifdef CONFIG_CPU_S3C2416 > + { .compatible = "samsung,s3c2416-irq", .data = &s3c2416_irq_data }, > +#endif > +#ifdef CONFIG_CPU_S3C2440 > + { .compatible = "samsung,s3c2440-irq", .data = &s3c2440_irq_data }, > +#endif > +#ifdef CONFIG_CPU_S3C2442 > + { .compatible = "samsung,s3c2442-irq", .data = &s3c2442_irq_data }, > +#endif > +#ifdef CONFIG_CPU_S3C2443 > + { .compatible = "samsung,s3c2443-irq", .data = &s3c2443_irq_data }, > +#endif > + {}, > +}; > + > +int __init s3c24xx_init_intc_of(struct device_node *np, > + struct device_node *interrupt_parent) > +{ > + const struct of_device_id *match; > + const struct s3c24xx_irq_of_data *ctrl_data; > + struct device_node *intc_node; > + struct s3c24xx_irq_of_ctrl *ctrl; > + struct s3c_irq_intc *intc; > + void __iomem *reg_base; > + int i; > + > + match = of_match_node(intc_list, np); Rather than matching twice, create a wrapper function that passes in the necessary data pointer. Something like this: s3c2410_init_intc_of(struct device_node *np, struct device_node *interrupt_parent) { return s3c24xx_init_intc_of(np, interrupt_parent, &s3c2410_irq_data); } > + if (!match) { > + pr_err("irq-s3c24xx: could not find matching irqdata\n"); > + return -EINVAL; > + } > + > + ctrl_data = match->data; > + > + reg_base = of_iomap(np, 0); > + if (!reg_base) { > + pr_err("irq-s3c24xx: could not map irq memory\n"); > + return -EINVAL; > + } > + > + for (i = 0; i < ctrl_data->num_ctrl; i++) { > + ctrl = &ctrl_data->irq_ctrl[i]; > + > + intc_node = of_find_node_by_name(np, ctrl->name); > + if (!intc_node) { > + pr_debug("irq: no device node for %s\n", > + ctrl->name); > + continue; > + } > + > + intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); > + if (!intc) { > + of_node_put(intc_node); > + return -ENOMEM; > + } > + > + pr_debug("irq: found controller %s\n", ctrl->name); > + > + intc->irqs = ctrl->irq_data; > + > + if (ctrl->parent) { > + if (*(ctrl->parent)) { > + intc->parent = *(ctrl->parent); > + } else { > + pr_warn("irq: parent of %s missing\n", > + ctrl->name); > + kfree(intc); > + of_node_put(intc_node); > + continue; > + } > + } > + > + if (!ctrl->parent) { > + intc->reg_pending = reg_base + ctrl->offset; > + intc->reg_mask = reg_base + ctrl->offset + 0x08; > + intc->reg_intpnd = reg_base + ctrl->offset + 0x10; > + } else { > + intc->reg_pending = reg_base + ctrl->offset; > + intc->reg_mask = reg_base + ctrl->offset + 0x4; > + } These if statements could be simplified some (move this else to the "if (ctrl->parent)" above). > + > + /* now that all the data is complete, init the irq-domain */ > + s3c24xx_clear_intc(intc); > + intc->domain = irq_domain_add_linear(intc_node, 32, > + &s3c24xx_irq_ops, intc); > + if (!intc->domain) { > + pr_err("irq: could not create irq-domain\n"); > + kfree(intc); > + of_node_put(intc_node); > + continue; > + } > + > + if (ctrl->handle) > + *(ctrl->handle) = intc; > + } > + > + set_handle_irq(s3c24xx_handle_irq); > + > + return 0; > +} > + > +IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c24xx_init_intc_of); > +IRQCHIP_DECLARE(s3c2412_irq, "samsung,s3c2412-irq", s3c24xx_init_intc_of); > +IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c24xx_init_intc_of); > +IRQCHIP_DECLARE(s3c2440_irq, "samsung,s3c2440-irq", s3c24xx_init_intc_of); > +IRQCHIP_DECLARE(s3c2442_irq, "samsung,s3c2442-irq", s3c24xx_init_intc_of); > +IRQCHIP_DECLARE(s3c2443_irq, "samsung,s3c2443-irq", s3c24xx_init_intc_of); > +IRQCHIP_DECLARE(s3c2450_irq, "samsung,s3c2450-irq", s3c24xx_init_intc_of); > +#endif > -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html