This adds devicetree parsing of the controller-data for the
interrupt controllers on S3C24XX architectures.
As the interrupts and their parent differ on all s3c24xx SoCs the
interrupt-list and parent-relationship is read from a list in the
devicetree data.
Signed-off-by: Heiko Stuebner <heiko@...>
.../interrupt-controller/samsung,s3c24xx-irq.txt | 53 ++++++++
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-s3c24xx.c | 128 ++++++++++++++++++++
3 files changed, 182 insertions(+), 1 deletions(-)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt
@@ -0,0 +1,53 @@
+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 number
+
+ 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
+ - 2 .. edge irq
+ - 3 .. level irq
+
+Optional properties:
+- interrupt_parent : The parent interrupt controller
+
+Example:
+
+ intc2:interrupt-controller@4a000040 {
+ compatible = "samsung,s3c24xx-irq";
+ reg = <0x4a000040 0x18>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ s3c24xx,irqlist = <2 0 /* 2D */
+ 2 0 /* IIC1 */
+ 0 0 /* reserved */
+ 0 0 /* reserved */
+ 2 0 /* PCM0 */
+ 2 0 /* PCM1 */
+ 2 0 /* I2S0 */
+ 2 0>; /* I2S1 */
+ };
@@ -4,7 +4,7 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
-obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.c
+obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
obj-$(CONFIG_ARM_GIC) += irq-gic.o
@@ -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/mach/irq.h>
@@ -35,6 +38,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
@@ -1066,3 +1071,126 @@ void __init s3c2443_init_irq(void)
s3c24xx_init_intc(NULL, &init_s3c2443subint[0], main_intc, 0x4a000018);
}
#endif
+
+#ifdef CONFIG_OF
+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;
+ const __be32 *p;
+ unsigned long address;
+ int ret;
+ int i;
+ int cnt;
+ u32 val;
+
+ p = of_get_address(np, 0, NULL, NULL);
+ if (!p) {
+ pr_err("irq: register address missing\n");
+ return -EINVAL;
+ }
+
+ address = of_translate_address(np, p);
+
+ intc_prop = of_find_property(np, "s3c24xx,irqlist", NULL);
+ if (!intc_prop) {
+ pr_err("irq: irqlist not found\n");
+ return -EINVAL;
+ }
+
+ irq_data = kzalloc(sizeof(struct s3c_irq_data) * 32, GFP_KERNEL);
+ if (!irq_data)
+ return -ENOMEM;
+
+ /* build the irq_data list */
+ p = NULL;
+ cnt = 0;
+ 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 interrupt 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;
+ }
+
+ 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");
+ ret = -EINVAL;
+ goto err;
+ }
+ } else {
+ parent = NULL;
+ }
+
+ intc = s3c24xx_init_intc(np, irq_data, parent, address);
+ if (IS_ERR(intc)) {
+ ret = PTR_ERR(intc);
+ goto err;
+ }
+
+ /* put the intc as property into the dt, so we can access it
+ * as the interrupt_parent later
+ */
+ intc_prop = kzalloc(sizeof(struct property), GFP_KERNEL);
+ if (!intc_prop) {
+ pr_err("irq: could not allocate memory for dt property\n");
+
+ /* the interrupt controller was already added, so don't
+ * remove the created structures.
+ */
+ return -ENOMEM;
+ }
+
+ intc_prop->name = kstrdup("s3c-irq-intc", GFP_KERNEL);
+ intc_prop->value = intc;
+ intc_prop->length = sizeof(struct s3c_irq_intc);
+
+ ret = of_add_property(np, intc_prop);
+ if (ret) {
+ pr_err("irq: failed to add dt property\n");
+ kfree(intc_prop);
+ return ret;
+ }
+
+ return 0;
+
+err:
+ kfree(irq_data);
+
+ return ret;
+}
+IRQCHIP_DECLARE(s3c24xx_irq, "samsung,s3c24xx-irq", s3c24xx_init_intc_of);
+#endif