[RFC 1/2] ARM: S3C24XX: add devicetree support for interrupts
Thomas Abraham
thomas.abraham at linaro.org
Mon Nov 26 06:03:22 EST 2012
Hi Heiko,
On 25 November 2012 06:17, Heiko Stübner <heiko at sntech.de> wrote:
> This adds devicetree parsing of the controller-data for the
> interrupt controllers on S3C24XX architectures.
>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> ---
> .../interrupt-controller/samsung,s3c24xx-irq.txt | 57 ++++++
> arch/arm/mach-s3c24xx/common.h | 1 +
> arch/arm/plat-s3c24xx/irq.c | 197 ++++++++++++++++++++
> 3 files changed, 255 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..c637637
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt
> @@ -0,0 +1,57 @@
> +Samsung S3C24XX Interrupt Controllers
> +
> +The S3C24XX SoCs contain custom set of interrupt controllers providing a
> +varying number of interrupt sources.
> +
> +The set consists of a main- and a sub-controller as well as a controller
> +for the external interrupts and on newer SoCs even a second main controller.
> +
> +The bit-to-interrupt and parent mapping of the controllers is not fixed
> +over all SoCs and therefore must be defined in the controller description.
> +
> +Required properties:
> +- compatible: Compatible property value should be "samsung,s3c24xx-irq".
> +
> +- 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 2.
> +
> +- s3c24xx,irqlist : List of irqtypes found on this controller as
> + two-value pairs consisting of irqtype and parent-irq
> +
> + parent-irq is always the list position of the irq in the irqlist
> + of the parent controller (0..31)
> +
> + irqtypes are:
> + - 0 .. none
> + - 1 .. external interrupts in the main register (GPF0 .. GPF3)
> + - 2 .. edge irq in the main register
> + - 3 .. for parent-irqs, that have sub-irqs in child controllers
> + - 4 .. level irqs in the sub-register
> + - 5 .. edge irqs in the sub-register
> + - 6 .. external irqs in the external irq register (starting with GPF4)
> + - 7 .. irq in the second base irq controller of S3C2416/S3C2450 SoCs
Instead of defining the type of interrupt controller as above, is it
possible to create multiple device nodes, with each node representing
a type of interrupt controller with a unique compatible string and
corresponding properties. There will be a init function for each type
of interrupt controller. There should be a irq-domain for each of
these different types of interrupt controller. And then, in the device
tree source file, a proper tree like hierarchy of interrupt
controllers can defined (using the interrupt-parent property for each
controller node). The client nodes that generate the interrupt can
specify the parent node and the interrupt number within the parent to
which they generate the interrupt.
Thanks,
Thomas.
> +
> +Optional properties:
> +- interrupt_parent : The parent interrupt controller
> +
> +Example:
> +
> + intc2:interrupt-controller at 4a000040 {
> + compatible = "samsung,s3c24xx-irq";
> + reg = <0x4a000040 0x18>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> +
> + s3c24xx,irqlist = <7 0 /* 2D */
> + 7 0 /* IIC1 */
> + 0 0 /* reserved */
> + 0 0 /* reserved */
> + 7 0 /* PCM0 */
> + 7 0 /* PCM1 */
> + 7 0 /* I2S0 */
> + 7 0>; /* I2S1 */
> + };
> diff --git a/arch/arm/mach-s3c24xx/common.h b/arch/arm/mach-s3c24xx/common.h
> index ed6276f..7a7b770 100644
> --- a/arch/arm/mach-s3c24xx/common.h
> +++ b/arch/arm/mach-s3c24xx/common.h
> @@ -16,5 +16,6 @@ void s3c2410_restart(char mode, const char *cmd);
> void s3c244x_restart(char mode, const char *cmd);
>
> extern struct syscore_ops s3c24xx_irq_syscore_ops;
> +extern void s3c24xx_init_irq_of(void);
>
> #endif /* __ARCH_ARM_MACH_S3C24XX_COMMON_H */
> diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c
> index 0510627..4f8fe4a 100644
> --- a/arch/arm/plat-s3c24xx/irq.c
> +++ b/arch/arm/plat-s3c24xx/irq.c
> @@ -25,6 +25,9 @@
> #include <linux/slab.h>
>
> #include <linux/irqdomain.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
>
> #include <asm/irq.h>
> #include <asm/mach/irq.h>
> @@ -956,3 +959,197 @@ void __init s3c2443_init_irq(void)
> s3c24xx_init_irq();
> }
> #endif
> +
> +#ifdef CONFIG_OF
> +static int __init s3c24xx_init_intc_of(struct device_node *np,
> + struct device_node *interrupt_parent)
> +{
> + struct s3c_irq_intc *intc;
> + struct s3c_irq_intc *parent;
> + struct s3c_irq_data *irq_data;
> + struct property *intc_prop;
> + int irq_num = 0, irq_start = 0, irq_offset = 0;
> + int ret, i, cnt;
> + const __be32 *p;
> + u32 val;
> +
> + intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
> + if (!intc)
> + return -ENOMEM;
> +
> + p = of_get_address(np, 0, NULL, NULL);
> + if (!p) {
> + pr_err("irq: register address missing\n");
> + ret = -EINVAL;
> + goto err_addr;
> + }
> +
> + /* select the correct data for the controller.
> + * Need to hard code the irq num start and offset
> + * to preserve the static mapping for now
> + */
> + switch (of_translate_address(np, p)) {
> + case 0x4a000000:
> + pr_debug("irq: found main intc\n");
> + intc->reg_pending = S3C2410_SRCPND;
> + intc->reg_intpnd = S3C2410_INTPND;
> + intc->reg_mask = S3C2410_INTMSK;
> + irq_num = 32;
> + irq_start = S3C2410_IRQ(0);
> + irq_offset = 0;
> + break;
> + case 0x560000a4:
> + pr_debug("irq: found extintc\n");
> + intc->reg_pending = S3C2410_EINTPEND;
> + intc->reg_mask = S3C2410_EINTMASK;
> + irq_num = 20;
> + irq_start = S3C2410_IRQ(32);
> + irq_offset = 4;
> + break;
> + case 0x4a000018:
> + pr_debug("irq: found subintc\n");
> + intc->reg_pending = S3C2410_SUBSRCPND;
> + intc->reg_mask = S3C2410_INTSUBMSK;
> + irq_num = 29;
> + irq_start = S3C2410_IRQSUB(0);
> + irq_offset = 0;
> + break;
> + case 0x4a000040:
> + pr_debug("irq: found intc2\n");
> + intc->reg_pending = S3C2416_SRCPND2;
> + intc->reg_intpnd = S3C2416_INTPND2;
> + intc->reg_mask = S3C2416_INTMSK2;
> + irq_num = 8;
> + irq_start = S3C2416_IRQ(0);
> + irq_offset = 0;
> + break;
> + case 0:
> + pr_err("irq: couldn't translate address\n");
> + ret = -EINVAL;
> + goto err_addr;
> + default:
> + pr_err("irq: unsupported controller address\n");
> + ret = -EINVAL;
> + goto err_addr;
> + }
> +
> + irq_data = kzalloc(sizeof(struct s3c_irq_data) * 32, GFP_KERNEL);
> + if (!irq_data) {
> + ret = -ENOMEM;
> + goto err_addr;
> + }
> +
> + cnt = 0;
> + intc_prop = of_find_property(np, "s3c24xx,irqlist", NULL);
> + if (!intc_prop) {
> + pr_err("irq: irqlist not found\n");
> + ret = -EINVAL;
> + goto err_data;
> + }
> +
> + /* build the irq_data list */
> + p = NULL;
> + for (i = 0; i < 32; i++) {
> + p = of_prop_next_u32(intc_prop, p, &val);
> +
> + /* when we hit the first non-valid element, assume it's
> + * the end of the list. The rest of the fields are
> + * already of type S3C_IRQTYPE_NONE (value 0)
> + */
> + if (!p)
> + break;
> +
> + irq_data[i].type = val;
> +
> + p = of_prop_next_u32(intc_prop, p, &val);
> + if (!p) {
> + pr_warn("irq: uneven number of elements in irqlist, last value will be dropped\n");
> + irq_data[i].type = 0;
> + break;
> + }
> +
> + irq_data[i].parent_irq = val;
> +
> + pr_debug("irq: found hwirq %d with type %d and parent %lu\n",
> + i, irq_data[i].type, irq_data[i].parent_irq);
> + cnt++;
> + }
> +
> + /* if we haven't found any irq definition at all,
> + * something is very wrong.
> + */
> + if (!cnt) {
> + pr_err("irq: empty irq definition\n");
> + ret = -EINVAL;
> + goto err_data;
> + }
> +
> + intc->irqs = irq_data;
> +
> + /* put the intc into the dt as property, so we can access it from
> + * as the interrupt_parent later
> + */
> +
> + intc_prop = kzalloc(sizeof(struct property), GFP_KERNEL);
> + if (!intc_prop) {
> + ret = -ENOMEM;
> + goto err_data;
> + }
> +
> + intc_prop->name = kstrdup("s3c-irq-intc", GFP_KERNEL);
> + intc_prop->value = intc;
> + intc_prop->length = sizeof(struct s3c_irq_intc);
> +
> + ret = prom_add_property(np, intc_prop);
> + if (ret) {
> + pr_err("irq: failed to add dt property\n");
> + goto err_prop;
> + }
> +
> + /* set the parent relationship */
> + if (interrupt_parent) {
> + parent = (struct s3c_irq_intc *)of_get_property(
> + interrupt_parent, "s3c-irq-intc", NULL);
> + if (!parent) {
> + pr_err("irq: no parent for non-root controller found\n");
> + goto err_domain;
> + }
> +
> + intc->parent = parent;
> + }
> +
> + /* now that all the data is complete, init the irq-domain */
> + s3c24xx_clear_intc(intc);
> + intc->domain = irq_domain_add_legacy(np, irq_num, irq_start,
> + irq_offset, &s3c24xx_irq_ops,
> + intc);
> + if (!intc->domain) {
> + pr_err("irq: could not create irq-domain\n");
> + ret = -EINVAL;
> + goto err_domain;
> + }
> +
> + return 0;
> +
> +err_domain:
> + prom_remove_property(np, intc_prop);
> +err_prop:
> + kfree(intc_prop);
> +err_data:
> + kfree(irq_data);
> +err_addr:
> + kfree(intc);
> +
> + return ret;
> +}
> +
> +static const struct of_device_id s3c24xx_irq_match[] __initconst = {
> + { .compatible = "samsung,s3c24xx-irq", .data = s3c24xx_init_intc_of, },
> + { /* sentinel */ }
> +};
> +
> +void __init s3c24xx_init_irq_of(void)
> +{
> + of_irq_init(s3c24xx_irq_match);
> +}
> +#endif
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
More information about the linux-arm-kernel
mailing list