diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-04-11 15:54:21 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-04-11 15:54:21 -0700 |
commit | 65311d813461295c85f338d99de7c7b8fcccbb9b (patch) | |
tree | 9419ad38e9730faa1dbe2794e4ac0bb35512bf3b | |
parent | 9ec7dfaf2cc0e5000dec931bde41f17c7eb203db (diff) | |
download | linux-3.10-65311d813461295c85f338d99de7c7b8fcccbb9b.tar.gz linux-3.10-65311d813461295c85f338d99de7c7b8fcccbb9b.tar.bz2 linux-3.10-65311d813461295c85f338d99de7c7b8fcccbb9b.zip |
irqchip patches added
16 files changed, 1951 insertions, 0 deletions
diff --git a/patches.misc/0001-irqchip-add-basic-infrastructure.patch b/patches.misc/0001-irqchip-add-basic-infrastructure.patch new file mode 100644 index 00000000000..e2743479ecf --- /dev/null +++ b/patches.misc/0001-irqchip-add-basic-infrastructure.patch @@ -0,0 +1,207 @@ +From ec84d795c3d26160bba23ec9f37e1d6e484afc8a Mon Sep 17 00:00:00 2001 +From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> +Date: Tue, 20 Nov 2012 23:00:52 +0100 +Subject: irqchip: add basic infrastructure + +With the recent creation of the drivers/irqchip/ directory, it is +desirable to move irq controller drivers here. At the moment, the only +driver here is irq-bcm2835, the driver for the irq controller found in +the ARM BCM2835 SoC, present in Rasberry Pi systems. This irq +controller driver was exporting its initialization function and its +irq handling function through a header file in +<linux/irqchip/bcm2835.h>. + +When proposing to also move another irq controller driver in +drivers/irqchip, Rob Herring raised the very valid point that moving +things to drivers/irqchip was good in order to remove more stuff from +arch/arm, but if it means adding gazillions of headers files in +include/linux/irqchip/, it would not be very nice. + +So, upon the suggestion of Rob Herring and Arnd Bergmann, this commit +introduces a small infrastructure that defines a central +irqchip_init() function in drivers/irqchip/irqchip.c, which is meant +to be called as the ->init_irq() callback of ARM platforms. This +function calls of_irq_init() with an array of match strings and init +functions generated from a special linker section. + +Note that the irq controller driver initialization function is +responsible for setting the global handle_arch_irq() variable, so that +ARM platforms no longer have to define the ->handle_irq field in their +DT_MACHINE structure. + +A global header, <linux/irqchip.h> is also added to expose the single +irqchip_init() function to the reset of the kernel. + +A further commit moves the BCM2835 irq controller driver to this new +small infrastructure, therefore removing the include/linux/irqchip/ +directory. + +Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> +Reviewed-by: Stephen Warren <swarren@wwwdotorg.org> +Reviewed-by: Rob Herring <rob.herring@calxeda.com> +Acked-by: Arnd Bergmann <arnd@arndb.de> +[rob.herring: reword commit message to reflect use of linker sections.] +Signed-off-by: Rob Herring <rob.herring@calxeda.com> +(cherry picked from commit f6e916b82022cba67bdd0ec7df84e2bce2ef3f73) + +Conflicts: + drivers/Kconfig + drivers/Makefile + drivers/irqchip/Kconfig + drivers/irqchip/Makefile + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/Kconfig | 1 + + drivers/Makefile | 2 ++ + drivers/irqchip/Kconfig | 3 +++ + drivers/irqchip/Makefile | 1 + + drivers/irqchip/irqchip.c | 30 ++++++++++++++++++++++++++++++ + drivers/irqchip/irqchip.h | 29 +++++++++++++++++++++++++++++ + include/asm-generic/vmlinux.lds.h | 12 +++++++++++- + include/linux/irqchip.h | 16 ++++++++++++++++ + 8 files changed, 93 insertions(+), 1 deletion(-) + create mode 100644 drivers/irqchip/Kconfig + create mode 100644 drivers/irqchip/Makefile + create mode 100644 drivers/irqchip/irqchip.c + create mode 100644 drivers/irqchip/irqchip.h + create mode 100644 include/linux/irqchip.h + +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -140,4 +140,5 @@ source "drivers/virt/Kconfig" + + source "drivers/devfreq/Kconfig" + ++source "drivers/irqchip/Kconfig" + endmenu +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -134,3 +134,5 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/ + obj-$(CONFIG_HYPERV) += hv/ + + obj-$(CONFIG_PM_DEVFREQ) += devfreq/ ++ ++obj-y += irqchip/ +--- /dev/null ++++ b/drivers/irqchip/Kconfig +@@ -0,0 +1,3 @@ ++config IRQCHIP ++ def_bool y ++ depends on OF_IRQ +--- /dev/null ++++ b/drivers/irqchip/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_IRQCHIP) += irqchip.o +--- /dev/null ++++ b/drivers/irqchip/irqchip.c +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (C) 2012 Thomas Petazzoni ++ * ++ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ++ * ++ * 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. ++ */ ++ ++#include <linux/init.h> ++#include <linux/of_irq.h> ++ ++#include "irqchip.h" ++ ++/* ++ * This special of_device_id is the sentinel at the end of the ++ * of_device_id[] array of all irqchips. It is automatically placed at ++ * the end of the array by the linker, thanks to being part of a ++ * special section. ++ */ ++static const struct of_device_id ++irqchip_of_match_end __used __section(__irqchip_of_end); ++ ++extern struct of_device_id __irqchip_begin[]; ++ ++void __init irqchip_init(void) ++{ ++ of_irq_init(__irqchip_begin); ++} +--- /dev/null ++++ b/drivers/irqchip/irqchip.h +@@ -0,0 +1,29 @@ ++/* ++ * Copyright (C) 2012 Thomas Petazzoni ++ * ++ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ++ * ++ * 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. ++ */ ++ ++#ifndef _IRQCHIP_H ++#define _IRQCHIP_H ++ ++/* ++ * This macro must be used by the different irqchip drivers to declare ++ * the association between their DT compatible string and their ++ * initialization function. ++ * ++ * @name: name that must be unique accross all IRQCHIP_DECLARE of the ++ * same file. ++ * @compstr: compatible string of the irqchip driver ++ * @fn: initialization function ++ */ ++#define IRQCHIP_DECLARE(name,compstr,fn) \ ++ static const struct of_device_id irqchip_of_match_##name \ ++ __used __section(__irqchip_of_table) \ ++ = { .compatible = compstr, .data = fn } ++ ++#endif +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -149,6 +149,15 @@ + #define TRACE_SYSCALLS() + #endif + ++#ifdef CONFIG_IRQCHIP ++#define IRQCHIP_OF_MATCH_TABLE() \ ++ . = ALIGN(8); \ ++ VMLINUX_SYMBOL(__irqchip_begin) = .; \ ++ *(__irqchip_of_table) \ ++ *(__irqchip_of_end) ++#else ++#define IRQCHIP_OF_MATCH_TABLE() ++#endif + + #define KERNEL_DTB() \ + STRUCT_ALIGN(); \ +@@ -493,7 +502,8 @@ + DEV_DISCARD(init.rodata) \ + CPU_DISCARD(init.rodata) \ + MEM_DISCARD(init.rodata) \ +- KERNEL_DTB() ++ KERNEL_DTB() \ ++ IRQCHIP_OF_MATCH_TABLE() + + #define INIT_TEXT \ + *(.init.text) \ +--- /dev/null ++++ b/include/linux/irqchip.h +@@ -0,0 +1,16 @@ ++/* ++ * Copyright (C) 2012 Thomas Petazzoni ++ * ++ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> ++ * ++ * 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. ++ */ ++ ++#ifndef _LINUX_IRQCHIP_H ++#define _LINUX_IRQCHIP_H ++ ++void irqchip_init(void); ++ ++#endif diff --git a/patches.misc/0002-irqdomain-augment-add_simple-to-allocate-descs.patch b/patches.misc/0002-irqdomain-augment-add_simple-to-allocate-descs.patch new file mode 100644 index 00000000000..bdfe77e4655 --- /dev/null +++ b/patches.misc/0002-irqdomain-augment-add_simple-to-allocate-descs.patch @@ -0,0 +1,83 @@ +From 6d044f57896d098e437879dae9993aefe7ad60e0 Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Thu, 27 Sep 2012 14:59:39 +0200 +Subject: irqdomain: augment add_simple() to allocate descs + +Currently we rely on all IRQ chip instances to dynamically +allocate their IRQ descriptors unless they use the linear +IRQ domain. So for irqdomain_add_legacy() and +irqdomain_add_simple() the caller need to make sure that +descriptors are allocated. + +Let's slightly augment the yet unused irqdomain_add_simple() +to also allocate descriptors as a means to simplify usage +and avoid code duplication throughout the kernel. + +We warn if descriptors cannot be allocated, e.g. if a +platform has the bad habit of hogging descriptors at boot +time. + +Cc: Thomas Gleixner <tglx@linutronix.de> +Cc: Grant Likely <grant.likely@secretlab.ca> +Cc: Paul Mundt <lethal@linux-sh.org> +Cc: Russell King <linux@arm.linux.org.uk> +Cc: Lee Jones <lee.jones@linaro.org> +Reviewed-by: Rob Herring <rob.herring@calxeda.com> +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +(cherry picked from commit 2854d167cc545d0642277bf8b77f972a91146fc6) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + kernel/irq/irqdomain.c | 33 ++++++++++++++++++++++++++++----- + 1 file changed, 28 insertions(+), 5 deletions(-) + +--- a/kernel/irq/irqdomain.c ++++ b/kernel/irq/irqdomain.c +@@ -148,7 +148,8 @@ static unsigned int irq_domain_legacy_re + * @host_data: Controller private data pointer + * + * Allocates a legacy irq_domain if irq_base is positive or a linear +- * domain otherwise. ++ * domain otherwise. For the legacy domain, IRQ descriptors will also ++ * be allocated. + * + * This is intended to implement the expected behaviour for most + * interrupt controllers which is that a linear mapping should +@@ -162,11 +163,33 @@ struct irq_domain *irq_domain_add_simple + const struct irq_domain_ops *ops, + void *host_data) + { +- if (first_irq > 0) +- return irq_domain_add_legacy(of_node, size, first_irq, 0, ++ if (first_irq > 0) { ++ int irq_base; ++ ++ if (IS_ENABLED(CONFIG_SPARSE_IRQ)) { ++ /* ++ * Set the descriptor allocator to search for a ++ * 1-to-1 mapping, such as irq_alloc_desc_at(). ++ * Use of_node_to_nid() which is defined to ++ * numa_node_id() on platforms that have no custom ++ * implementation. ++ */ ++ irq_base = irq_alloc_descs(first_irq, first_irq, size, ++ of_node_to_nid(of_node)); ++ if (irq_base < 0) { ++ WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", ++ first_irq); ++ irq_base = first_irq; ++ } ++ } else ++ irq_base = first_irq; ++ ++ return irq_domain_add_legacy(of_node, size, irq_base, 0, + ops, host_data); +- else +- return irq_domain_add_linear(of_node, size, ops, host_data); ++ } ++ ++ /* A linear domain is the default */ ++ return irq_domain_add_linear(of_node, size, ops, host_data); + } + + /** diff --git a/patches.misc/0003-irqdomain-stop-screaming-about-preallocated-irqdescs.patch b/patches.misc/0003-irqdomain-stop-screaming-about-preallocated-irqdescs.patch new file mode 100644 index 00000000000..f9444d9ef35 --- /dev/null +++ b/patches.misc/0003-irqdomain-stop-screaming-about-preallocated-irqdescs.patch @@ -0,0 +1,30 @@ +From cfb1c1582aada77bfe7294b7ec4f60c6bf74ceac Mon Sep 17 00:00:00 2001 +From: Linus Walleij <linus.walleij@linaro.org> +Date: Tue, 27 Nov 2012 01:20:32 +0100 +Subject: irqdomain: stop screaming about preallocated irqdescs + +In the simple irqdomain: don't shout warnings to the user, +there is no point. An informational print is sufficient. + +Signed-off-by: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: Grant Likely <grant.likely@secretlab.ca> +(cherry picked from commit d202b7b970b2a21278505c2467919fd441a7b6c8) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + kernel/irq/irqdomain.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/kernel/irq/irqdomain.c ++++ b/kernel/irq/irqdomain.c +@@ -177,8 +177,8 @@ struct irq_domain *irq_domain_add_simple + irq_base = irq_alloc_descs(first_irq, first_irq, size, + of_node_to_nid(of_node)); + if (irq_base < 0) { +- WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", +- first_irq); ++ pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", ++ first_irq); + irq_base = first_irq; + } + } else diff --git a/patches.misc/0004-irqchip-Renesas-INTC-External-IRQ-pin-driver.patch b/patches.misc/0004-irqchip-Renesas-INTC-External-IRQ-pin-driver.patch new file mode 100644 index 00000000000..c64f7378e0c --- /dev/null +++ b/patches.misc/0004-irqchip-Renesas-INTC-External-IRQ-pin-driver.patch @@ -0,0 +1,548 @@ +From a19094353a6fc670c1ff007a56698a088bcb2cbd Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Mon, 18 Feb 2013 23:28:34 +0900 +Subject: irqchip: Renesas INTC External IRQ pin driver + +This patch adds a driver for external IRQ pins connected +to the INTC block on recent SoCs from Renesas. + +The INTC hardware block usually contains a rather wide +range of features ranging from external IRQ pin handling +to legacy interrupt controller support. On older SoCs +the INTC is used as a general purpose interrupt controller +both for external IRQ pins and on-chip devices. + +On more recent ARM based SoCs with Cortex-A9 the main +interrupt controller is the GIC, but IRQ trigger setup +still need to happen in the INTC hardware block. + +This driver implements the glue code needed to configure +IRQ trigger and also handle mask/unmask and demux of +external IRQ pins hooked up from the INTC to the GIC. + +Tested on sh73a0 and r8a7779. The hardware varies quite +a bit with SoC model, for instance register width and +bitfield widths vary wildly. The driver requires one GIC +SPI per external IRQ pin to operate. Each driver instance +will handle up to 8 external IRQ pins. + +The SoCs using this driver are currently mainly used +together with regular platform devices so this driver +allows configuration via platform data to support things +like static interrupt base address. DT support will +be added incrementally in the not so distant future. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit 0ed30c2820e229e0334f20d93ec75ca196a69162) + +Conflicts: + drivers/irqchip/Kconfig + drivers/irqchip/Makefile + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 2 + drivers/irqchip/irq-renesas-intc-irqpin.c | 464 ++++++++++++++++++ + include/linux/platform_data/irq-renesas-intc-irqpin.h | 10 + 4 files changed, 480 insertions(+) + create mode 100644 drivers/irqchip/irq-renesas-intc-irqpin.c + create mode 100644 include/linux/platform_data/irq-renesas-intc-irqpin.h + +--- a/drivers/irqchip/Kconfig ++++ b/drivers/irqchip/Kconfig +@@ -1,3 +1,7 @@ + config IRQCHIP + def_bool y + depends on OF_IRQ ++ ++config RENESAS_INTC_IRQPIN ++ bool ++ select IRQ_DOMAIN +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -1 +1,3 @@ + obj-$(CONFIG_IRQCHIP) += irqchip.o ++ ++obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o +--- /dev/null ++++ b/drivers/irqchip/irq-renesas-intc-irqpin.c +@@ -0,0 +1,464 @@ ++/* ++ * Renesas INTC External IRQ Pin Driver ++ * ++ * Copyright (C) 2013 Magnus Damm ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/irqdomain.h> ++#include <linux/err.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/platform_data/irq-renesas-intc-irqpin.h> ++ ++#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ ++ ++#define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ ++#define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ ++#define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ ++#define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ ++#define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ ++#define INTC_IRQPIN_REG_NR 5 ++ ++/* INTC external IRQ PIN hardware register access: ++ * ++ * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) ++ * PRIO is read-write 32-bit with 4-bits per IRQ (**) ++ * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) ++ * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) ++ * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) ++ * ++ * (*) May be accessed by more than one driver instance - lock needed ++ * (**) Read-modify-write access by one driver instance - lock needed ++ * (***) Accessed by one driver instance only - no locking needed ++ */ ++ ++struct intc_irqpin_iomem { ++ void __iomem *iomem; ++ unsigned long (*read)(void __iomem *iomem); ++ void (*write)(void __iomem *iomem, unsigned long data); ++ int width; ++}; ++ ++struct intc_irqpin_irq { ++ int hw_irq; ++ int irq; ++ struct intc_irqpin_priv *p; ++}; ++ ++struct intc_irqpin_priv { ++ struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; ++ struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; ++ struct renesas_intc_irqpin_config config; ++ unsigned int number_of_irqs; ++ struct platform_device *pdev; ++ struct irq_chip irq_chip; ++ struct irq_domain *irq_domain; ++}; ++ ++static unsigned long intc_irqpin_read32(void __iomem *iomem) ++{ ++ return ioread32(iomem); ++} ++ ++static unsigned long intc_irqpin_read8(void __iomem *iomem) ++{ ++ return ioread8(iomem); ++} ++ ++static void intc_irqpin_write32(void __iomem *iomem, unsigned long data) ++{ ++ iowrite32(data, iomem); ++} ++ ++static void intc_irqpin_write8(void __iomem *iomem, unsigned long data) ++{ ++ iowrite8(data, iomem); ++} ++ ++static inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, ++ int reg) ++{ ++ struct intc_irqpin_iomem *i = &p->iomem[reg]; ++ return i->read(i->iomem); ++} ++ ++static inline void intc_irqpin_write(struct intc_irqpin_priv *p, ++ int reg, unsigned long data) ++{ ++ struct intc_irqpin_iomem *i = &p->iomem[reg]; ++ i->write(i->iomem, data); ++} ++ ++static inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, ++ int reg, int hw_irq) ++{ ++ return BIT((p->iomem[reg].width - 1) - hw_irq); ++} ++ ++static inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, ++ int reg, int hw_irq) ++{ ++ intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); ++} ++ ++static DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ ++ ++static void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, ++ int reg, int shift, ++ int width, int value) ++{ ++ unsigned long flags; ++ unsigned long tmp; ++ ++ raw_spin_lock_irqsave(&intc_irqpin_lock, flags); ++ ++ tmp = intc_irqpin_read(p, reg); ++ tmp &= ~(((1 << width) - 1) << shift); ++ tmp |= value << shift; ++ intc_irqpin_write(p, reg, tmp); ++ ++ raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); ++} ++ ++static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, ++ int irq, int do_mask) ++{ ++ int bitfield_width = 4; /* PRIO assumed to have fixed bitfield width */ ++ int shift = (7 - irq) * bitfield_width; /* PRIO assumed to be 32-bit */ ++ ++ intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, ++ shift, bitfield_width, ++ do_mask ? 0 : (1 << bitfield_width) - 1); ++} ++ ++static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) ++{ ++ int bitfield_width = p->config.sense_bitfield_width; ++ int shift = (7 - irq) * bitfield_width; /* SENSE assumed to be 32-bit */ ++ ++ dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); ++ ++ if (value >= (1 << bitfield_width)) ++ return -EINVAL; ++ ++ intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, ++ bitfield_width, value); ++ return 0; ++} ++ ++static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) ++{ ++ dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", ++ str, i->irq, i->hw_irq, ++ irq_find_mapping(i->p->irq_domain, i->hw_irq)); ++} ++ ++static void intc_irqpin_irq_enable(struct irq_data *d) ++{ ++ struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); ++ int hw_irq = irqd_to_hwirq(d); ++ ++ intc_irqpin_dbg(&p->irq[hw_irq], "enable"); ++ intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); ++} ++ ++static void intc_irqpin_irq_disable(struct irq_data *d) ++{ ++ struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); ++ int hw_irq = irqd_to_hwirq(d); ++ ++ intc_irqpin_dbg(&p->irq[hw_irq], "disable"); ++ intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); ++} ++ ++static void intc_irqpin_irq_enable_force(struct irq_data *d) ++{ ++ struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); ++ int irq = p->irq[irqd_to_hwirq(d)].irq; ++ ++ intc_irqpin_irq_enable(d); ++ irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); ++} ++ ++static void intc_irqpin_irq_disable_force(struct irq_data *d) ++{ ++ struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); ++ int irq = p->irq[irqd_to_hwirq(d)].irq; ++ ++ irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); ++ intc_irqpin_irq_disable(d); ++} ++ ++#define INTC_IRQ_SENSE_VALID 0x10 ++#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) ++ ++static unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { ++ [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), ++ [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), ++ [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), ++ [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), ++ [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), ++}; ++ ++static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) ++{ ++ unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; ++ struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); ++ ++ if (!(value & INTC_IRQ_SENSE_VALID)) ++ return -EINVAL; ++ ++ return intc_irqpin_set_sense(p, irqd_to_hwirq(d), ++ value ^ INTC_IRQ_SENSE_VALID); ++} ++ ++static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) ++{ ++ struct intc_irqpin_irq *i = dev_id; ++ struct intc_irqpin_priv *p = i->p; ++ unsigned long bit; ++ ++ intc_irqpin_dbg(i, "demux1"); ++ bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); ++ ++ if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { ++ intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); ++ intc_irqpin_dbg(i, "demux2"); ++ generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq)); ++ return IRQ_HANDLED; ++ } ++ return IRQ_NONE; ++} ++ ++static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, ++ irq_hw_number_t hw) ++{ ++ struct intc_irqpin_priv *p = h->host_data; ++ ++ intc_irqpin_dbg(&p->irq[hw], "map"); ++ irq_set_chip_data(virq, h->host_data); ++ irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); ++ set_irq_flags(virq, IRQF_VALID); /* kill me now */ ++ return 0; ++} ++ ++static struct irq_domain_ops intc_irqpin_irq_domain_ops = { ++ .map = intc_irqpin_irq_domain_map, ++}; ++ ++static int intc_irqpin_probe(struct platform_device *pdev) ++{ ++ struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; ++ struct intc_irqpin_priv *p; ++ struct intc_irqpin_iomem *i; ++ struct resource *io[INTC_IRQPIN_REG_NR]; ++ struct resource *irq; ++ struct irq_chip *irq_chip; ++ void (*enable_fn)(struct irq_data *d); ++ void (*disable_fn)(struct irq_data *d); ++ const char *name = dev_name(&pdev->dev); ++ int ret; ++ int k; ++ ++ p = kzalloc(sizeof(*p), GFP_KERNEL); ++ if (!p) { ++ dev_err(&pdev->dev, "failed to allocate driver data\n"); ++ ret = -ENOMEM; ++ goto err0; ++ } ++ ++ /* deal with driver instance configuration */ ++ if (pdata) ++ memcpy(&p->config, pdata, sizeof(*pdata)); ++ if (!p->config.sense_bitfield_width) ++ p->config.sense_bitfield_width = 4; /* default to 4 bits */ ++ ++ p->pdev = pdev; ++ platform_set_drvdata(pdev, p); ++ ++ /* get hold of manadatory IOMEM */ ++ for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { ++ io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); ++ if (!io[k]) { ++ dev_err(&pdev->dev, "not enough IOMEM resources\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ } ++ ++ /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ ++ for (k = 0; k < INTC_IRQPIN_MAX; k++) { ++ irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); ++ if (!irq) ++ break; ++ ++ p->irq[k].hw_irq = k; ++ p->irq[k].p = p; ++ p->irq[k].irq = irq->start; ++ } ++ ++ p->number_of_irqs = k; ++ if (p->number_of_irqs < 1) { ++ dev_err(&pdev->dev, "not enough IRQ resources\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ ++ /* ioremap IOMEM and setup read/write callbacks */ ++ for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { ++ i = &p->iomem[k]; ++ ++ switch (resource_size(io[k])) { ++ case 1: ++ i->width = 8; ++ i->read = intc_irqpin_read8; ++ i->write = intc_irqpin_write8; ++ break; ++ case 4: ++ i->width = 32; ++ i->read = intc_irqpin_read32; ++ i->write = intc_irqpin_write32; ++ break; ++ default: ++ dev_err(&pdev->dev, "IOMEM size mismatch\n"); ++ ret = -EINVAL; ++ goto err2; ++ } ++ ++ i->iomem = ioremap_nocache(io[k]->start, resource_size(io[k])); ++ if (!i->iomem) { ++ dev_err(&pdev->dev, "failed to remap IOMEM\n"); ++ ret = -ENXIO; ++ goto err2; ++ } ++ } ++ ++ /* mask all interrupts using priority */ ++ for (k = 0; k < p->number_of_irqs; k++) ++ intc_irqpin_mask_unmask_prio(p, k, 1); ++ ++ /* use more severe masking method if requested */ ++ if (p->config.control_parent) { ++ enable_fn = intc_irqpin_irq_enable_force; ++ disable_fn = intc_irqpin_irq_disable_force; ++ } else { ++ enable_fn = intc_irqpin_irq_enable; ++ disable_fn = intc_irqpin_irq_disable; ++ } ++ ++ irq_chip = &p->irq_chip; ++ irq_chip->name = name; ++ irq_chip->irq_mask = disable_fn; ++ irq_chip->irq_unmask = enable_fn; ++ irq_chip->irq_enable = enable_fn; ++ irq_chip->irq_disable = disable_fn; ++ irq_chip->irq_set_type = intc_irqpin_irq_set_type; ++ irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; ++ ++ p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, ++ p->number_of_irqs, ++ p->config.irq_base, ++ &intc_irqpin_irq_domain_ops, p); ++ if (!p->irq_domain) { ++ ret = -ENXIO; ++ dev_err(&pdev->dev, "cannot initialize irq domain\n"); ++ goto err2; ++ } ++ ++ /* request and set priority on interrupts one by one */ ++ for (k = 0; k < p->number_of_irqs; k++) { ++ if (request_irq(p->irq[k].irq, intc_irqpin_irq_handler, ++ 0, name, &p->irq[k])) { ++ dev_err(&pdev->dev, "failed to request low IRQ\n"); ++ ret = -ENOENT; ++ goto err3; ++ } ++ intc_irqpin_mask_unmask_prio(p, k, 0); ++ } ++ ++ dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); ++ ++ /* warn in case of mismatch if irq base is specified */ ++ if (p->config.irq_base) { ++ k = irq_find_mapping(p->irq_domain, 0); ++ if (p->config.irq_base != k) ++ dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", ++ p->config.irq_base, k); ++ } ++ ++ return 0; ++ ++err3: ++ for (; k >= 0; k--) ++ free_irq(p->irq[k - 1].irq, &p->irq[k - 1]); ++ ++ irq_domain_remove(p->irq_domain); ++err2: ++ for (k = 0; k < INTC_IRQPIN_REG_NR; k++) ++ iounmap(p->iomem[k].iomem); ++err1: ++ kfree(p); ++err0: ++ return ret; ++} ++ ++static int intc_irqpin_remove(struct platform_device *pdev) ++{ ++ struct intc_irqpin_priv *p = platform_get_drvdata(pdev); ++ int k; ++ ++ for (k = 0; k < p->number_of_irqs; k++) ++ free_irq(p->irq[k].irq, &p->irq[k]); ++ ++ irq_domain_remove(p->irq_domain); ++ ++ for (k = 0; k < INTC_IRQPIN_REG_NR; k++) ++ iounmap(p->iomem[k].iomem); ++ ++ kfree(p); ++ return 0; ++} ++ ++static struct platform_driver intc_irqpin_device_driver = { ++ .probe = intc_irqpin_probe, ++ .remove = intc_irqpin_remove, ++ .driver = { ++ .name = "renesas_intc_irqpin", ++ } ++}; ++ ++static int __init intc_irqpin_init(void) ++{ ++ return platform_driver_register(&intc_irqpin_device_driver); ++} ++postcore_initcall(intc_irqpin_init); ++ ++static void __exit intc_irqpin_exit(void) ++{ ++ platform_driver_unregister(&intc_irqpin_device_driver); ++} ++module_exit(intc_irqpin_exit); ++ ++MODULE_AUTHOR("Magnus Damm"); ++MODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/platform_data/irq-renesas-intc-irqpin.h +@@ -0,0 +1,10 @@ ++#ifndef __IRQ_RENESAS_INTC_IRQPIN_H__ ++#define __IRQ_RENESAS_INTC_IRQPIN_H__ ++ ++struct renesas_intc_irqpin_config { ++ unsigned int sense_bitfield_width; ++ unsigned int irq_base; ++ bool control_parent; ++}; ++ ++#endif /* __IRQ_RENESAS_INTC_IRQPIN_H__ */ diff --git a/patches.misc/0005-irqchip-intc-irqpin-Cache-mapped-IRQ.patch b/patches.misc/0005-irqchip-intc-irqpin-Cache-mapped-IRQ.patch new file mode 100644 index 00000000000..43e41c66aca --- /dev/null +++ b/patches.misc/0005-irqchip-intc-irqpin-Cache-mapped-IRQ.patch @@ -0,0 +1,128 @@ +From 12395af649b20eb8a23d1e69b2ba8aa6ba6ae8f9 Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Tue, 26 Feb 2013 20:58:54 +0900 +Subject: irqchip: intc-irqpin: Cache mapped IRQ + +Cache IRQ in domain_irq variable instead of +making use of irq_find_mapping(). While at it +rename the irq variable to requested_irq. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit 325a9c2426dd7144fc3d56397286ce1e69713c12) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/irq-renesas-intc-irqpin.c | 30 ++++++++++++++++-------------- + 1 file changed, 16 insertions(+), 14 deletions(-) + +--- a/drivers/irqchip/irq-renesas-intc-irqpin.c ++++ b/drivers/irqchip/irq-renesas-intc-irqpin.c +@@ -61,7 +61,8 @@ struct intc_irqpin_iomem { + + struct intc_irqpin_irq { + int hw_irq; +- int irq; ++ int requested_irq; ++ int domain_irq; + struct intc_irqpin_priv *p; + }; + +@@ -169,8 +170,7 @@ static int intc_irqpin_set_sense(struct + static void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) + { + dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", +- str, i->irq, i->hw_irq, +- irq_find_mapping(i->p->irq_domain, i->hw_irq)); ++ str, i->requested_irq, i->hw_irq, i->domain_irq); + } + + static void intc_irqpin_irq_enable(struct irq_data *d) +@@ -194,7 +194,7 @@ static void intc_irqpin_irq_disable(stru + static void intc_irqpin_irq_enable_force(struct irq_data *d) + { + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +- int irq = p->irq[irqd_to_hwirq(d)].irq; ++ int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + + intc_irqpin_irq_enable(d); + irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); +@@ -203,7 +203,7 @@ static void intc_irqpin_irq_enable_force + static void intc_irqpin_irq_disable_force(struct irq_data *d) + { + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); +- int irq = p->irq[irqd_to_hwirq(d)].irq; ++ int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + + irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); + intc_irqpin_irq_disable(d); +@@ -244,7 +244,7 @@ static irqreturn_t intc_irqpin_irq_handl + if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { + intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); + intc_irqpin_dbg(i, "demux2"); +- generic_handle_irq(irq_find_mapping(p->irq_domain, i->hw_irq)); ++ generic_handle_irq(i->domain_irq); + return IRQ_HANDLED; + } + return IRQ_NONE; +@@ -255,6 +255,9 @@ static int intc_irqpin_irq_domain_map(st + { + struct intc_irqpin_priv *p = h->host_data; + ++ p->irq[hw].domain_irq = virq; ++ p->irq[hw].hw_irq = hw; ++ + intc_irqpin_dbg(&p->irq[hw], "map"); + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); +@@ -312,9 +315,8 @@ static int intc_irqpin_probe(struct plat + if (!irq) + break; + +- p->irq[k].hw_irq = k; + p->irq[k].p = p; +- p->irq[k].irq = irq->start; ++ p->irq[k].requested_irq = irq->start; + } + + p->number_of_irqs = k; +@@ -387,7 +389,8 @@ static int intc_irqpin_probe(struct plat + + /* request and set priority on interrupts one by one */ + for (k = 0; k < p->number_of_irqs; k++) { +- if (request_irq(p->irq[k].irq, intc_irqpin_irq_handler, ++ if (request_irq(p->irq[k].requested_irq, ++ intc_irqpin_irq_handler, + 0, name, &p->irq[k])) { + dev_err(&pdev->dev, "failed to request low IRQ\n"); + ret = -ENOENT; +@@ -400,17 +403,16 @@ static int intc_irqpin_probe(struct plat + + /* warn in case of mismatch if irq base is specified */ + if (p->config.irq_base) { +- k = irq_find_mapping(p->irq_domain, 0); +- if (p->config.irq_base != k) ++ if (p->config.irq_base != p->irq[0].domain_irq) + dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", +- p->config.irq_base, k); ++ p->config.irq_base, p->irq[0].domain_irq); + } + + return 0; + + err3: + for (; k >= 0; k--) +- free_irq(p->irq[k - 1].irq, &p->irq[k - 1]); ++ free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); + + irq_domain_remove(p->irq_domain); + err2: +@@ -428,7 +430,7 @@ static int intc_irqpin_remove(struct pla + int k; + + for (k = 0; k < p->number_of_irqs; k++) +- free_irq(p->irq[k].irq, &p->irq[k]); ++ free_irq(p->irq[k].requested_irq, &p->irq[k]); + + irq_domain_remove(p->irq_domain); + diff --git a/patches.misc/0006-ARM-shmobile-irq_pin-for-static-IRQ-pin-assignment.patch b/patches.misc/0006-ARM-shmobile-irq_pin-for-static-IRQ-pin-assignment.patch new file mode 100644 index 00000000000..d009d3067b5 --- /dev/null +++ b/patches.misc/0006-ARM-shmobile-irq_pin-for-static-IRQ-pin-assignment.patch @@ -0,0 +1,28 @@ +From d0f1eb80c491e8e0d13f6c413cf16f1f6cc609a0 Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Tue, 26 Feb 2013 12:00:59 +0900 +Subject: ARM: shmobile: irq_pin() for static IRQ pin assignment + +Add the macro irq_pin() to let board-specific code using +platform devices tie in external IRQn pins in a common way. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit a877feb63cb49b7fa4722f9a0e61949bd46d4577) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + arch/arm/mach-shmobile/include/mach/irqs.h | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/arm/mach-shmobile/include/mach/irqs.h ++++ b/arch/arm/mach-shmobile/include/mach/irqs.h +@@ -11,4 +11,8 @@ + #define INTCS_VECT(n, vect) INTC_VECT((n), INTCS_VECT_BASE + (vect)) + #define intcs_evt2irq(evt) evt2irq(INTCS_VECT_BASE + (evt)) + ++/* External IRQ pins */ ++#define IRQPIN_BASE 2000 ++#define irq_pin(nr) ((nr) + IRQPIN_BASE) ++ + #endif /* __ASM_MACH_IRQS_H */ diff --git a/patches.misc/0007-ARM-shmobile-INTC-External-IRQ-pin-driver-on-r8a7779.patch b/patches.misc/0007-ARM-shmobile-INTC-External-IRQ-pin-driver-on-r8a7779.patch new file mode 100644 index 00000000000..a2e29213539 --- /dev/null +++ b/patches.misc/0007-ARM-shmobile-INTC-External-IRQ-pin-driver-on-r8a7779.patch @@ -0,0 +1,155 @@ +From 25598603d52c830b2c317ab1b94155525b11c09c Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Tue, 26 Feb 2013 12:01:18 +0900 +Subject: ARM: shmobile: INTC External IRQ pin driver on r8a7779 + +Update the r8a7779 IRQ code to make use of the +INTC External IRQ pin driver for external +interrupt pins IRQ0 -> IRQ3. + +The r8a7779 SoC can like older SH SoCs configure +to use the IRQ0 -> IRQ3 signals as individual +interrupts or a combined IRL mode. + +Without this patch the r8a7779 SoC code does +not fully support external IRQ pins in individual +IRQ mode. The r8a7779 PFC code does not yet have +gpio_to_irq() support so no need to update such +code. + +At this point the DT reference implementations +are not covered. In the future such code shall +tie in the INTC External IRQ pin driver via +DT, so this kind of verbose code is not needed +for the long term DT case. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit dd9e1fa565b335a5fd2768b772c673a4860196d7) + +Conflicts: + arch/arm/mach-shmobile/Kconfig + arch/arm/mach-shmobile/include/mach/common.h + arch/arm/mach-shmobile/intc-r8a7779.c + +Signed-off-by: Simon Horman <horms@verge.net.au> +--- + arch/arm/mach-shmobile/Kconfig | 1 + arch/arm/mach-shmobile/board-marzen.c | 3 + + arch/arm/mach-shmobile/include/mach/common.h | 2 + + arch/arm/mach-shmobile/intc-r8a7779.c | 52 +++++++++++++++++++++++++++ + 4 files changed, 57 insertions(+), 1 deletion(-) + +--- a/arch/arm/mach-shmobile/Kconfig ++++ b/arch/arm/mach-shmobile/Kconfig +@@ -40,6 +40,7 @@ config ARCH_R8A7779 + select SH_CLK_CPG + select ARM_GIC + select ARCH_WANT_OPTIONAL_GPIOLIB ++ select RENESAS_INTC_IRQPIN + + config ARCH_EMEV2 + bool "Emma Mobile EV2" +--- a/arch/arm/mach-shmobile/board-marzen.c ++++ b/arch/arm/mach-shmobile/board-marzen.c +@@ -53,7 +53,7 @@ static struct resource smsc911x_resource + .flags = IORESOURCE_MEM, + }, + [1] = { +- .start = gic_spi(28), /* IRQ 1 */ ++ .start = irq_pin(1), /* IRQ1 */ + .flags = IORESOURCE_IRQ, + }, + }; +@@ -84,6 +84,7 @@ static void __init marzen_init(void) + regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies)); + + r8a7779_pinmux_init(); ++ r8a7779_init_irq_extpin(1); /* IRQ1 as individual interrupt */ + + /* SCIF2 (CN18: DEBUG0) */ + gpio_request(GPIO_FN_TX2_C, NULL); +--- a/arch/arm/mach-shmobile/include/mach/common.h ++++ b/arch/arm/mach-shmobile/include/mach/common.h +@@ -66,6 +66,8 @@ extern void r8a7740_reserve_memory(void) + extern void r8a7740_pinmux_init(void); + + extern void r8a7779_init_irq(void); ++extern void r8a7779_init_irq_extpin(int irlm); ++extern void r8a7779_init_irq_dt(void); + extern void r8a7779_map_io(void); + extern void r8a7779_add_early_devices(void); + extern void r8a7779_add_standard_devices(void); +--- a/arch/arm/mach-shmobile/intc-r8a7779.c ++++ b/arch/arm/mach-shmobile/intc-r8a7779.c +@@ -19,11 +19,15 @@ + */ + #include <linux/kernel.h> + #include <linux/init.h> ++#include <linux/platform_device.h> + #include <linux/interrupt.h> + #include <linux/irq.h> + #include <linux/io.h> ++#include <linux/platform_data/irq-renesas-intc-irqpin.h> ++#include <linux/irqchip.h> + #include <mach/common.h> + #include <mach/intc.h> ++#include <mach/irqs.h> + #include <mach/r8a7779.h> + #include <asm/hardware/gic.h> + #include <asm/mach-types.h> +@@ -38,6 +42,54 @@ + #define INT2NTSR0 0xfe700060 + #define INT2NTSR1 0xfe700064 + ++struct renesas_intc_irqpin_config irqpin0_platform_data = { ++ .irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */ ++ .sense_bitfield_width = 2, ++}; ++ ++static struct resource irqpin0_resources[] = { ++ DEFINE_RES_MEM(0xfe78001c, 4), /* ICR1 */ ++ DEFINE_RES_MEM(0xfe780010, 4), /* INTPRI */ ++ DEFINE_RES_MEM(0xfe780024, 4), /* INTREQ */ ++ DEFINE_RES_MEM(0xfe780044, 4), /* INTMSK0 */ ++ DEFINE_RES_MEM(0xfe780064, 4), /* INTMSKCLR0 */ ++ DEFINE_RES_IRQ(gic_spi(27)), /* IRQ0 */ ++ DEFINE_RES_IRQ(gic_spi(28)), /* IRQ1 */ ++ DEFINE_RES_IRQ(gic_spi(29)), /* IRQ2 */ ++ DEFINE_RES_IRQ(gic_spi(30)), /* IRQ3 */ ++}; ++ ++static struct platform_device irqpin0_device = { ++ .name = "renesas_intc_irqpin", ++ .id = 0, ++ .resource = irqpin0_resources, ++ .num_resources = ARRAY_SIZE(irqpin0_resources), ++ .dev = { ++ .platform_data = &irqpin0_platform_data, ++ }, ++}; ++ ++void __init r8a7779_init_irq_extpin(int irlm) ++{ ++ void __iomem *icr0 = ioremap_nocache(0xfe780000, PAGE_SIZE); ++ unsigned long tmp; ++ ++ if (icr0) { ++ tmp = ioread32(icr0); ++ if (irlm) ++ tmp |= 1 << 23; /* IRQ0 -> IRQ3 as individual pins */ ++ else ++ tmp &= ~(1 << 23); /* IRL mode - not supported */ ++ tmp |= (1 << 21); /* LVLMODE = 1 */ ++ iowrite32(tmp, icr0); ++ iounmap(icr0); ++ ++ if (irlm) ++ platform_device_register(&irqpin0_device); ++ } else ++ pr_warn("r8a7779: unable to setup external irq pin mode\n"); ++} ++ + static int r8a7779_set_wake(struct irq_data *data, unsigned int on) + { + return 0; /* always allow wakeup */ diff --git a/patches.misc/0008-irqchip-intc-irqpin-Whitespace-fixes.patch b/patches.misc/0008-irqchip-intc-irqpin-Whitespace-fixes.patch new file mode 100644 index 00000000000..b6d5088a59c --- /dev/null +++ b/patches.misc/0008-irqchip-intc-irqpin-Whitespace-fixes.patch @@ -0,0 +1,34 @@ +From fe6c5fcec31e66f5b03f0380c0644bb7a1637ea4 Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Tue, 26 Feb 2013 20:58:44 +0900 +Subject: irqchip: intc-irqpin: Whitespace fixes + +Remove whitespace damage and add newline +between variables and code. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Reviewed-by: Thomas Gleixner <tglx@linutronix.de> +Tested-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/irq-renesas-intc-irqpin.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/irqchip/irq-renesas-intc-irqpin.c ++++ b/drivers/irqchip/irq-renesas-intc-irqpin.c +@@ -100,6 +100,7 @@ static inline unsigned long intc_irqpin_ + int reg) + { + struct intc_irqpin_iomem *i = &p->iomem[reg]; ++ + return i->read(i->iomem); + } + +@@ -107,6 +108,7 @@ static inline void intc_irqpin_write(str + int reg, unsigned long data) + { + struct intc_irqpin_iomem *i = &p->iomem[reg]; ++ + i->write(i->iomem, data); + } + diff --git a/patches.misc/0009-irqchip-intc-irqpin-Add-force-comments.patch b/patches.misc/0009-irqchip-intc-irqpin-Add-force-comments.patch new file mode 100644 index 00000000000..b8d0e622b88 --- /dev/null +++ b/patches.misc/0009-irqchip-intc-irqpin-Add-force-comments.patch @@ -0,0 +1,41 @@ +From b0791671eb6cc3c794f3649dd37dfbd88980e355 Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Tue, 26 Feb 2013 20:59:04 +0900 +Subject: irqchip: intc-irqpin: Add force comments + +Add comments to describe the special case for +"force" versions of enable and disable functions. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Reviewed-by: Thomas Gleixner <tglx@linutronix.de> +Tested-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/irq-renesas-intc-irqpin.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/irqchip/irq-renesas-intc-irqpin.c ++++ b/drivers/irqchip/irq-renesas-intc-irqpin.c +@@ -199,6 +199,11 @@ static void intc_irqpin_irq_enable_force + int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + + intc_irqpin_irq_enable(d); ++ ++ /* enable interrupt through parent interrupt controller, ++ * assumes non-shared interrupt with 1:1 mapping ++ * needed for busted IRQs on some SoCs like sh73a0 ++ */ + irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); + } + +@@ -207,6 +212,10 @@ static void intc_irqpin_irq_disable_forc + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + int irq = p->irq[irqd_to_hwirq(d)].requested_irq; + ++ /* disable interrupt through parent interrupt controller, ++ * assumes non-shared interrupt with 1:1 mapping ++ * needed for busted IRQs on some SoCs like sh73a0 ++ */ + irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); + intc_irqpin_irq_disable(d); + } diff --git a/patches.misc/0010-irqchip-intc-irqpin-Make-use-of-devm-functions.patch b/patches.misc/0010-irqchip-intc-irqpin-Make-use-of-devm-functions.patch new file mode 100644 index 00000000000..b408f157087 --- /dev/null +++ b/patches.misc/0010-irqchip-intc-irqpin-Make-use-of-devm-functions.patch @@ -0,0 +1,124 @@ +From e89972262ef0aa2b47a1be58aa72fb6d17c4125e Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Tue, 26 Feb 2013 20:59:13 +0900 +Subject: irqchip: intc-irqpin: Make use of devm functions + +Use devm_kzalloc(), devm_ioremap_nocache() +and devm_request_irq() to simplify error +handling. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Reviewed-by: Thomas Gleixner <tglx@linutronix.de> +Tested-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/irq-renesas-intc-irqpin.c | 41 +++++++++--------------------- + 1 file changed, 13 insertions(+), 28 deletions(-) + +--- a/drivers/irqchip/irq-renesas-intc-irqpin.c ++++ b/drivers/irqchip/irq-renesas-intc-irqpin.c +@@ -294,7 +294,7 @@ static int intc_irqpin_probe(struct plat + int ret; + int k; + +- p = kzalloc(sizeof(*p), GFP_KERNEL); ++ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + ret = -ENOMEM; +@@ -316,7 +316,7 @@ static int intc_irqpin_probe(struct plat + if (!io[k]) { + dev_err(&pdev->dev, "not enough IOMEM resources\n"); + ret = -EINVAL; +- goto err1; ++ goto err0; + } + } + +@@ -334,7 +334,7 @@ static int intc_irqpin_probe(struct plat + if (p->number_of_irqs < 1) { + dev_err(&pdev->dev, "not enough IRQ resources\n"); + ret = -EINVAL; +- goto err1; ++ goto err0; + } + + /* ioremap IOMEM and setup read/write callbacks */ +@@ -355,14 +355,15 @@ static int intc_irqpin_probe(struct plat + default: + dev_err(&pdev->dev, "IOMEM size mismatch\n"); + ret = -EINVAL; +- goto err2; ++ goto err0; + } + +- i->iomem = ioremap_nocache(io[k]->start, resource_size(io[k])); ++ i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start, ++ resource_size(io[k])); + if (!i->iomem) { + dev_err(&pdev->dev, "failed to remap IOMEM\n"); + ret = -ENXIO; +- goto err2; ++ goto err0; + } + } + +@@ -395,17 +396,17 @@ static int intc_irqpin_probe(struct plat + if (!p->irq_domain) { + ret = -ENXIO; + dev_err(&pdev->dev, "cannot initialize irq domain\n"); +- goto err2; ++ goto err0; + } + + /* request and set priority on interrupts one by one */ + for (k = 0; k < p->number_of_irqs; k++) { +- if (request_irq(p->irq[k].requested_irq, +- intc_irqpin_irq_handler, +- 0, name, &p->irq[k])) { ++ if (devm_request_irq(&pdev->dev, p->irq[k].requested_irq, ++ intc_irqpin_irq_handler, ++ 0, name, &p->irq[k])) { + dev_err(&pdev->dev, "failed to request low IRQ\n"); + ret = -ENOENT; +- goto err3; ++ goto err1; + } + intc_irqpin_mask_unmask_prio(p, k, 0); + } +@@ -421,16 +422,8 @@ static int intc_irqpin_probe(struct plat + + return 0; + +-err3: +- for (; k >= 0; k--) +- free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); +- +- irq_domain_remove(p->irq_domain); +-err2: +- for (k = 0; k < INTC_IRQPIN_REG_NR; k++) +- iounmap(p->iomem[k].iomem); + err1: +- kfree(p); ++ irq_domain_remove(p->irq_domain); + err0: + return ret; + } +@@ -438,17 +431,9 @@ err0: + static int intc_irqpin_remove(struct platform_device *pdev) + { + struct intc_irqpin_priv *p = platform_get_drvdata(pdev); +- int k; +- +- for (k = 0; k < p->number_of_irqs; k++) +- free_irq(p->irq[k].requested_irq, &p->irq[k]); + + irq_domain_remove(p->irq_domain); + +- for (k = 0; k < INTC_IRQPIN_REG_NR; k++) +- iounmap(p->iomem[k].iomem); +- +- kfree(p); + return 0; + } + diff --git a/patches.misc/0011-irqchip-intc-irqpin-GPL-header-for-platform-data.patch b/patches.misc/0011-irqchip-intc-irqpin-GPL-header-for-platform-data.patch new file mode 100644 index 00000000000..06aeaa838b7 --- /dev/null +++ b/patches.misc/0011-irqchip-intc-irqpin-GPL-header-for-platform-data.patch @@ -0,0 +1,40 @@ +From 8143994e7681c68def57a0b57bfd8c789912eca1 Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Tue, 26 Feb 2013 20:59:23 +0900 +Subject: irqchip: intc-irqpin: GPL header for platform data + +Add GPL header to platform data include file. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Reviewed-by: Thomas Gleixner <tglx@linutronix.de> +Tested-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + include/linux/platform_data/irq-renesas-intc-irqpin.h | 19 ++++++++++++++++++ + 1 file changed, 19 insertions(+) + +--- a/include/linux/platform_data/irq-renesas-intc-irqpin.h ++++ b/include/linux/platform_data/irq-renesas-intc-irqpin.h +@@ -1,3 +1,22 @@ ++/* ++ * Renesas INTC External IRQ Pin Driver ++ * ++ * Copyright (C) 2013 Magnus Damm ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ + #ifndef __IRQ_RENESAS_INTC_IRQPIN_H__ + #define __IRQ_RENESAS_INTC_IRQPIN_H__ + diff --git a/patches.misc/0012-irqchip-Renesas-IRQC-driver.patch b/patches.misc/0012-irqchip-Renesas-IRQC-driver.patch new file mode 100644 index 00000000000..01a17c6d734 --- /dev/null +++ b/patches.misc/0012-irqchip-Renesas-IRQC-driver.patch @@ -0,0 +1,395 @@ +From ce9327175f66480b635210af8c2b38a7780fc573 Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Wed, 27 Feb 2013 17:15:01 +0900 +Subject: irqchip: Renesas IRQC driver + +This patch adds a driver for external IRQ pins connected +to the IRQC hardware block on recent SoCs from Renesas. + +The IRQC hardware block is used together with more +recent ARM based SoCs using the GIC. As usual the GIC +requires external IRQ trigger setup somewhere else +which in this particular case happens to be IRQC. + +This driver implements the glue code needed to configure +IRQ trigger and also handle mask/unmask and demux of +external IRQ pins hooked up from the IRQC to the GIC. + +Tested on r8a73a4 but is designed to work with a wide +range of SoCs. The driver requires one GIC SPI per +external IRQ pin to operate. Each driver instance +will handle up to 32 external IRQ pins. + +The SoCs using this driver are currently mainly used +together with regular platform devices so this driver +allows configuration via platform data to support things +like static interrupt base address. DT support will +be added incrementally in the not so distant future. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Tested-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit beda1f77bba50a4c78ce17bbcec3a0fdab454e88) + +Conflicts: + drivers/irqchip/Kconfig + drivers/irqchip/Makefile + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-renesas-irqc.c | 298 +++++++++++++++++++++++++ + include/linux/platform_data/irq-renesas-irqc.h | 27 ++ + 4 files changed, 330 insertions(+) + create mode 100644 drivers/irqchip/irq-renesas-irqc.c + create mode 100644 include/linux/platform_data/irq-renesas-irqc.h + +--- a/drivers/irqchip/Kconfig ++++ b/drivers/irqchip/Kconfig +@@ -5,3 +5,7 @@ config IRQCHIP + config RENESAS_INTC_IRQPIN + bool + select IRQ_DOMAIN ++ ++config RENESAS_IRQC ++ bool ++ select IRQ_DOMAIN +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -1,3 +1,4 @@ + obj-$(CONFIG_IRQCHIP) += irqchip.o + + obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o ++obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o +--- /dev/null ++++ b/drivers/irqchip/irq-renesas-irqc.c +@@ -0,0 +1,298 @@ ++/* ++ * Renesas IRQC Driver ++ * ++ * Copyright (C) 2013 Magnus Damm ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/irqdomain.h> ++#include <linux/err.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/platform_data/irq-renesas-irqc.h> ++ ++#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ ++ ++#define IRQC_REQ_STS 0x00 ++#define IRQC_EN_STS 0x04 ++#define IRQC_EN_SET 0x08 ++#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) ++#define DETECT_STATUS 0x100 ++#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) ++ ++struct irqc_irq { ++ int hw_irq; ++ int requested_irq; ++ int domain_irq; ++ struct irqc_priv *p; ++}; ++ ++struct irqc_priv { ++ void __iomem *iomem; ++ void __iomem *cpu_int_base; ++ struct irqc_irq irq[IRQC_IRQ_MAX]; ++ struct renesas_irqc_config config; ++ unsigned int number_of_irqs; ++ struct platform_device *pdev; ++ struct irq_chip irq_chip; ++ struct irq_domain *irq_domain; ++}; ++ ++static void irqc_dbg(struct irqc_irq *i, char *str) ++{ ++ dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", ++ str, i->requested_irq, i->hw_irq, i->domain_irq); ++} ++ ++static void irqc_irq_enable(struct irq_data *d) ++{ ++ struct irqc_priv *p = irq_data_get_irq_chip_data(d); ++ int hw_irq = irqd_to_hwirq(d); ++ ++ irqc_dbg(&p->irq[hw_irq], "enable"); ++ iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET); ++} ++ ++static void irqc_irq_disable(struct irq_data *d) ++{ ++ struct irqc_priv *p = irq_data_get_irq_chip_data(d); ++ int hw_irq = irqd_to_hwirq(d); ++ ++ irqc_dbg(&p->irq[hw_irq], "disable"); ++ iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS); ++} ++ ++#define INTC_IRQ_SENSE_VALID 0x10 ++#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) ++ ++static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { ++ [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x01), ++ [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x02), ++ [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x04), /* Synchronous */ ++ [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x08), /* Synchronous */ ++ [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x0c), /* Synchronous */ ++}; ++ ++static int irqc_irq_set_type(struct irq_data *d, unsigned int type) ++{ ++ struct irqc_priv *p = irq_data_get_irq_chip_data(d); ++ int hw_irq = irqd_to_hwirq(d); ++ unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK]; ++ unsigned long tmp; ++ ++ irqc_dbg(&p->irq[hw_irq], "sense"); ++ ++ if (!(value & INTC_IRQ_SENSE_VALID)) ++ return -EINVAL; ++ ++ tmp = ioread32(p->iomem + IRQC_CONFIG(hw_irq)); ++ tmp &= ~0x3f; ++ tmp |= value ^ INTC_IRQ_SENSE_VALID; ++ iowrite32(tmp, p->iomem + IRQC_CONFIG(hw_irq)); ++ return 0; ++} ++ ++static irqreturn_t irqc_irq_handler(int irq, void *dev_id) ++{ ++ struct irqc_irq *i = dev_id; ++ struct irqc_priv *p = i->p; ++ unsigned long bit = BIT(i->hw_irq); ++ ++ irqc_dbg(i, "demux1"); ++ ++ if (ioread32(p->iomem + DETECT_STATUS) & bit) { ++ iowrite32(bit, p->iomem + DETECT_STATUS); ++ irqc_dbg(i, "demux2"); ++ generic_handle_irq(i->domain_irq); ++ return IRQ_HANDLED; ++ } ++ return IRQ_NONE; ++} ++ ++static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq, ++ irq_hw_number_t hw) ++{ ++ struct irqc_priv *p = h->host_data; ++ ++ p->irq[hw].domain_irq = virq; ++ p->irq[hw].hw_irq = hw; ++ ++ irqc_dbg(&p->irq[hw], "map"); ++ irq_set_chip_data(virq, h->host_data); ++ irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); ++ set_irq_flags(virq, IRQF_VALID); /* kill me now */ ++ return 0; ++} ++ ++static struct irq_domain_ops irqc_irq_domain_ops = { ++ .map = irqc_irq_domain_map, ++}; ++ ++static int irqc_probe(struct platform_device *pdev) ++{ ++ struct renesas_irqc_config *pdata = pdev->dev.platform_data; ++ struct irqc_priv *p; ++ struct resource *io; ++ struct resource *irq; ++ struct irq_chip *irq_chip; ++ const char *name = dev_name(&pdev->dev); ++ int ret; ++ int k; ++ ++ p = kzalloc(sizeof(*p), GFP_KERNEL); ++ if (!p) { ++ dev_err(&pdev->dev, "failed to allocate driver data\n"); ++ ret = -ENOMEM; ++ goto err0; ++ } ++ ++ /* deal with driver instance configuration */ ++ if (pdata) ++ memcpy(&p->config, pdata, sizeof(*pdata)); ++ ++ p->pdev = pdev; ++ platform_set_drvdata(pdev, p); ++ ++ /* get hold of manadatory IOMEM */ ++ io = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!io) { ++ dev_err(&pdev->dev, "not enough IOMEM resources\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ ++ /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ ++ for (k = 0; k < IRQC_IRQ_MAX; k++) { ++ irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); ++ if (!irq) ++ break; ++ ++ p->irq[k].p = p; ++ p->irq[k].requested_irq = irq->start; ++ } ++ ++ p->number_of_irqs = k; ++ if (p->number_of_irqs < 1) { ++ dev_err(&pdev->dev, "not enough IRQ resources\n"); ++ ret = -EINVAL; ++ goto err1; ++ } ++ ++ /* ioremap IOMEM and setup read/write callbacks */ ++ p->iomem = ioremap_nocache(io->start, resource_size(io)); ++ if (!p->iomem) { ++ dev_err(&pdev->dev, "failed to remap IOMEM\n"); ++ ret = -ENXIO; ++ goto err2; ++ } ++ ++ p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ ++ ++ irq_chip = &p->irq_chip; ++ irq_chip->name = name; ++ irq_chip->irq_mask = irqc_irq_disable; ++ irq_chip->irq_unmask = irqc_irq_enable; ++ irq_chip->irq_enable = irqc_irq_enable; ++ irq_chip->irq_disable = irqc_irq_disable; ++ irq_chip->irq_set_type = irqc_irq_set_type; ++ irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; ++ ++ p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, ++ p->number_of_irqs, ++ p->config.irq_base, ++ &irqc_irq_domain_ops, p); ++ if (!p->irq_domain) { ++ ret = -ENXIO; ++ dev_err(&pdev->dev, "cannot initialize irq domain\n"); ++ goto err2; ++ } ++ ++ /* request interrupts one by one */ ++ for (k = 0; k < p->number_of_irqs; k++) { ++ if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, ++ 0, name, &p->irq[k])) { ++ dev_err(&pdev->dev, "failed to request IRQ\n"); ++ ret = -ENOENT; ++ goto err3; ++ } ++ } ++ ++ dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); ++ ++ /* warn in case of mismatch if irq base is specified */ ++ if (p->config.irq_base) { ++ if (p->config.irq_base != p->irq[0].domain_irq) ++ dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", ++ p->config.irq_base, p->irq[0].domain_irq); ++ } ++ ++ return 0; ++err3: ++ for (; k >= 0; k--) ++ free_irq(p->irq[k - 1].requested_irq, &p->irq[k - 1]); ++ ++ irq_domain_remove(p->irq_domain); ++err2: ++ iounmap(p->iomem); ++err1: ++ kfree(p); ++err0: ++ return ret; ++} ++ ++static int irqc_remove(struct platform_device *pdev) ++{ ++ struct irqc_priv *p = platform_get_drvdata(pdev); ++ int k; ++ ++ for (k = 0; k < p->number_of_irqs; k++) ++ free_irq(p->irq[k].requested_irq, &p->irq[k]); ++ ++ irq_domain_remove(p->irq_domain); ++ iounmap(p->iomem); ++ kfree(p); ++ return 0; ++} ++ ++static struct platform_driver irqc_device_driver = { ++ .probe = irqc_probe, ++ .remove = irqc_remove, ++ .driver = { ++ .name = "renesas_irqc", ++ } ++}; ++ ++static int __init irqc_init(void) ++{ ++ return platform_driver_register(&irqc_device_driver); ++} ++postcore_initcall(irqc_init); ++ ++static void __exit irqc_exit(void) ++{ ++ platform_driver_unregister(&irqc_device_driver); ++} ++module_exit(irqc_exit); ++ ++MODULE_AUTHOR("Magnus Damm"); ++MODULE_DESCRIPTION("Renesas IRQC Driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/include/linux/platform_data/irq-renesas-irqc.h +@@ -0,0 +1,27 @@ ++/* ++ * Renesas IRQC Driver ++ * ++ * Copyright (C) 2013 Magnus Damm ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __IRQ_RENESAS_IRQC_H__ ++#define __IRQ_RENESAS_IRQC_H__ ++ ++struct renesas_irqc_config { ++ unsigned int irq_base; ++}; ++ ++#endif /* __IRQ_RENESAS_IRQC_H__ */ diff --git a/patches.misc/0013-ARM-shmobile-Make-r8a7779-INTC-irqpin-platform-data-.patch b/patches.misc/0013-ARM-shmobile-Make-r8a7779-INTC-irqpin-platform-data-.patch new file mode 100644 index 00000000000..1c5862eaa67 --- /dev/null +++ b/patches.misc/0013-ARM-shmobile-Make-r8a7779-INTC-irqpin-platform-data-.patch @@ -0,0 +1,29 @@ +From 51d6f21fe0236a78f3a53d2603d62a2df88a3902 Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Wed, 6 Mar 2013 15:10:06 +0900 +Subject: ARM: shmobile: Make r8a7779 INTC irqpin platform data static + +The platform data for the INTC irq pin driver +seems to be global symbols, make it static to +allow multi-soc build. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit dd09a3e6cc0c5f7cc5a05612535e55e78820f404) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + arch/arm/mach-shmobile/intc-r8a7779.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/mach-shmobile/intc-r8a7779.c ++++ b/arch/arm/mach-shmobile/intc-r8a7779.c +@@ -42,7 +42,7 @@ + #define INT2NTSR0 0xfe700060 + #define INT2NTSR1 0xfe700064 + +-struct renesas_intc_irqpin_config irqpin0_platform_data = { ++static struct renesas_intc_irqpin_config irqpin0_platform_data = { + .irq_base = irq_pin(0), /* IRQ0 -> IRQ3 */ + .sense_bitfield_width = 2, + }; diff --git a/patches.misc/0014-irqchip-intc-irqpin-Initial-DT-support.patch b/patches.misc/0014-irqchip-intc-irqpin-Initial-DT-support.patch new file mode 100644 index 00000000000..4aeb362ee9e --- /dev/null +++ b/patches.misc/0014-irqchip-intc-irqpin-Initial-DT-support.patch @@ -0,0 +1,48 @@ +From c13fcfb98291d86e1f47fcb93a419f11952a873d Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Wed, 6 Mar 2013 15:16:08 +0900 +Subject: irqchip: intc-irqpin: Initial DT support + +Add initial DT support to the INTC External IRQ Pin +driver. At this point only hardware with 4-bit wide +sense registers is supported via DT. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit 9d833bbe49953a9a07f9ebd7a9ad170c308bd692) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/irq-renesas-intc-irqpin.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/irqchip/irq-renesas-intc-irqpin.c ++++ b/drivers/irqchip/irq-renesas-intc-irqpin.c +@@ -278,6 +278,7 @@ static int intc_irqpin_irq_domain_map(st + + static struct irq_domain_ops intc_irqpin_irq_domain_ops = { + .map = intc_irqpin_irq_domain_map, ++ .xlate = irq_domain_xlate_twocell, + }; + + static int intc_irqpin_probe(struct platform_device *pdev) +@@ -437,11 +438,19 @@ static int intc_irqpin_remove(struct pla + return 0; + } + ++static const struct of_device_id intc_irqpin_dt_ids[] = { ++ { .compatible = "renesas,intc-irqpin", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); ++ + static struct platform_driver intc_irqpin_device_driver = { + .probe = intc_irqpin_probe, + .remove = intc_irqpin_remove, + .driver = { + .name = "renesas_intc_irqpin", ++ .of_match_table = intc_irqpin_dt_ids, ++ .owner = THIS_MODULE, + } + }; + diff --git a/patches.misc/0015-irqchip-irqc-Add-DT-support.patch b/patches.misc/0015-irqchip-irqc-Add-DT-support.patch new file mode 100644 index 00000000000..bb90db7a824 --- /dev/null +++ b/patches.misc/0015-irqchip-irqc-Add-DT-support.patch @@ -0,0 +1,46 @@ +From 14bd4f0bc85992add01373d6ebed7dfabd85f07a Mon Sep 17 00:00:00 2001 +From: Magnus Damm <damm@opensource.se> +Date: Wed, 6 Mar 2013 15:23:39 +0900 +Subject: irqchip: irqc: Add DT support + +Add DT support to the IRQC External IRQ Pin driver. + +Signed-off-by: Magnus Damm <damm@opensource.se> +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +(cherry picked from commit 3b8dfa7c2f8af7613dae28ac0f3419bf75ead5d0) + +Signed-off-by: Simon Horman <horms+renesas@verge.net.au> +--- + drivers/irqchip/irq-renesas-irqc.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +--- a/drivers/irqchip/irq-renesas-irqc.c ++++ b/drivers/irqchip/irq-renesas-irqc.c +@@ -145,6 +145,7 @@ static int irqc_irq_domain_map(struct ir + + static struct irq_domain_ops irqc_irq_domain_ops = { + .map = irqc_irq_domain_map, ++ .xlate = irq_domain_xlate_twocell, + }; + + static int irqc_probe(struct platform_device *pdev) +@@ -273,11 +274,19 @@ static int irqc_remove(struct platform_d + return 0; + } + ++static const struct of_device_id irqc_dt_ids[] = { ++ { .compatible = "renesas,irqc", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, irqc_dt_ids); ++ + static struct platform_driver irqc_device_driver = { + .probe = irqc_probe, + .remove = irqc_remove, + .driver = { + .name = "renesas_irqc", ++ .of_match_table = irqc_dt_ids, ++ .owner = THIS_MODULE, + } + }; + @@ -786,6 +786,21 @@ patches.codel/codel-refine-one-condition-to-avoid-a-nul-rec_inv_sq.patch # patches.misc/arm-soc-add-per-platform-smp-operations.patch patches.misc/arm-soc-convert-shmobile-smp-to-smp-operations.patch +patches.misc/0001-irqchip-add-basic-infrastructure.patch +patches.misc/0002-irqdomain-augment-add_simple-to-allocate-descs.patch +patches.misc/0003-irqdomain-stop-screaming-about-preallocated-irqdescs.patch +patches.misc/0004-irqchip-Renesas-INTC-External-IRQ-pin-driver.patch +patches.misc/0005-irqchip-intc-irqpin-Cache-mapped-IRQ.patch +patches.misc/0006-ARM-shmobile-irq_pin-for-static-IRQ-pin-assignment.patch +patches.misc/0007-ARM-shmobile-INTC-External-IRQ-pin-driver-on-r8a7779.patch +patches.misc/0008-irqchip-intc-irqpin-Whitespace-fixes.patch +patches.misc/0009-irqchip-intc-irqpin-Add-force-comments.patch +patches.misc/0010-irqchip-intc-irqpin-Make-use-of-devm-functions.patch +patches.misc/0011-irqchip-intc-irqpin-GPL-header-for-platform-data.patch +patches.misc/0012-irqchip-Renesas-IRQC-driver.patch +patches.misc/0013-ARM-shmobile-Make-r8a7779-INTC-irqpin-platform-data-.patch +patches.misc/0014-irqchip-intc-irqpin-Initial-DT-support.patch +patches.misc/0015-irqchip-irqc-Add-DT-support.patch ############################################################################# |