summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig5
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/exynos-combiner.c44
-rw-r--r--drivers/irqchip/irq-gic.c6
-rw-r--r--drivers/irqchip/irq-imgpdc.c499
-rw-r--r--drivers/irqchip/irq-mmp.c495
-rw-r--r--drivers/irqchip/irq-sirfsoc.c18
7 files changed, 1018 insertions, 51 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 1fea003ed33..3792a1aa52b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -30,6 +30,11 @@ config ARM_VIC_NR
The maximum number of VICs available in the system, for
power management.
+config IMGPDC_IRQ
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config ORION_IRQCHIP
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e65c41a7366..c60b9010b15 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
+obj-$(CONFIG_ARCH_MMP) += irq-mmp.o
obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
@@ -14,6 +15,7 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
obj-$(CONFIG_ARM_GIC) += irq-gic.o
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
obj-$(CONFIG_ARM_VIC) += irq-vic.o
+obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 4c682651390..868ed40cb6b 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -19,10 +19,6 @@
#include <linux/of_irq.h>
#include <asm/mach/irq.h>
-#ifdef CONFIG_EXYNOS_ATAGS
-#include <plat/cpu.h>
-#endif
-
#include "irqchip.h"
#define COMBINER_ENABLE_SET 0x0
@@ -138,7 +134,6 @@ static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
__raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
}
-#ifdef CONFIG_OF
static int combiner_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
@@ -156,16 +151,6 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
return 0;
}
-#else
-static int combiner_irq_domain_xlate(struct irq_domain *d,
- struct device_node *controller,
- const u32 *intspec, unsigned int intsize,
- unsigned long *out_hwirq,
- unsigned int *out_type)
-{
- return -EINVAL;
-}
-#endif
static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
@@ -184,26 +169,6 @@ static struct irq_domain_ops combiner_irq_domain_ops = {
.map = combiner_irq_domain_map,
};
-static unsigned int combiner_lookup_irq(int group)
-{
-#ifdef CONFIG_EXYNOS_ATAGS
- if (group < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
- return IRQ_SPI(group);
-
- switch (group) {
- case 16:
- return IRQ_SPI(107);
- case 17:
- return IRQ_SPI(108);
- case 18:
- return IRQ_SPI(48);
- case 19:
- return IRQ_SPI(42);
- }
-#endif
- return 0;
-}
-
static void __init combiner_init(void __iomem *combiner_base,
struct device_node *np,
unsigned int max_nr,
@@ -229,12 +194,7 @@ static void __init combiner_init(void __iomem *combiner_base,
}
for (i = 0; i < max_nr; i++) {
-#ifdef CONFIG_OF
- if (np)
- irq = irq_of_parse_and_map(np, i);
- else
-#endif
- irq = combiner_lookup_irq(i);
+ irq = irq_of_parse_and_map(np, i);
combiner_init_one(&combiner_data[i], i,
combiner_base + (i >> 2) * 0x10, irq);
@@ -242,7 +202,6 @@ static void __init combiner_init(void __iomem *combiner_base,
}
}
-#ifdef CONFIG_OF
static int __init combiner_of_init(struct device_node *np,
struct device_node *parent)
{
@@ -275,4 +234,3 @@ static int __init combiner_of_init(struct device_node *np,
}
IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner",
combiner_of_init);
-#endif
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index ee7c5031206..d0e948084ea 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -453,6 +453,12 @@ static void gic_cpu_init(struct gic_chip_data *gic)
writel_relaxed(1, base + GIC_CPU_CTRL);
}
+void gic_cpu_if_down(void)
+{
+ void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
+ writel_relaxed(0, cpu_base + GIC_CPU_CTRL);
+}
+
#ifdef CONFIG_CPU_PM
/*
* Saves the GIC distributor registers during suspend or idle. Must be called
diff --git a/drivers/irqchip/irq-imgpdc.c b/drivers/irqchip/irq-imgpdc.c
new file mode 100644
index 00000000000..8071c2eb024
--- /dev/null
+++ b/drivers/irqchip/irq-imgpdc.c
@@ -0,0 +1,499 @@
+/*
+ * IMG PowerDown Controller (PDC)
+ *
+ * Copyright 2010-2013 Imagination Technologies Ltd.
+ *
+ * Exposes the syswake and PDC peripheral wake interrupts to the system.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* PDC interrupt register numbers */
+
+#define PDC_IRQ_STATUS 0x310
+#define PDC_IRQ_ENABLE 0x314
+#define PDC_IRQ_CLEAR 0x318
+#define PDC_IRQ_ROUTE 0x31c
+#define PDC_SYS_WAKE_BASE 0x330
+#define PDC_SYS_WAKE_STRIDE 0x8
+#define PDC_SYS_WAKE_CONFIG_BASE 0x334
+#define PDC_SYS_WAKE_CONFIG_STRIDE 0x8
+
+/* PDC interrupt register field masks */
+
+#define PDC_IRQ_SYS3 0x08
+#define PDC_IRQ_SYS2 0x04
+#define PDC_IRQ_SYS1 0x02
+#define PDC_IRQ_SYS0 0x01
+#define PDC_IRQ_ROUTE_WU_EN_SYS3 0x08000000
+#define PDC_IRQ_ROUTE_WU_EN_SYS2 0x04000000
+#define PDC_IRQ_ROUTE_WU_EN_SYS1 0x02000000
+#define PDC_IRQ_ROUTE_WU_EN_SYS0 0x01000000
+#define PDC_IRQ_ROUTE_WU_EN_WD 0x00040000
+#define PDC_IRQ_ROUTE_WU_EN_IR 0x00020000
+#define PDC_IRQ_ROUTE_WU_EN_RTC 0x00010000
+#define PDC_IRQ_ROUTE_EXT_EN_SYS3 0x00000800
+#define PDC_IRQ_ROUTE_EXT_EN_SYS2 0x00000400
+#define PDC_IRQ_ROUTE_EXT_EN_SYS1 0x00000200
+#define PDC_IRQ_ROUTE_EXT_EN_SYS0 0x00000100
+#define PDC_IRQ_ROUTE_EXT_EN_WD 0x00000004
+#define PDC_IRQ_ROUTE_EXT_EN_IR 0x00000002
+#define PDC_IRQ_ROUTE_EXT_EN_RTC 0x00000001
+#define PDC_SYS_WAKE_RESET 0x00000010
+#define PDC_SYS_WAKE_INT_MODE 0x0000000e
+#define PDC_SYS_WAKE_INT_MODE_SHIFT 1
+#define PDC_SYS_WAKE_PIN_VAL 0x00000001
+
+/* PDC interrupt constants */
+
+#define PDC_SYS_WAKE_INT_LOW 0x0
+#define PDC_SYS_WAKE_INT_HIGH 0x1
+#define PDC_SYS_WAKE_INT_DOWN 0x2
+#define PDC_SYS_WAKE_INT_UP 0x3
+#define PDC_SYS_WAKE_INT_CHANGE 0x6
+#define PDC_SYS_WAKE_INT_NONE 0x4
+
+/**
+ * struct pdc_intc_priv - private pdc interrupt data.
+ * @nr_perips: Number of peripheral interrupt signals.
+ * @nr_syswakes: Number of syswake signals.
+ * @perip_irqs: List of peripheral IRQ numbers handled.
+ * @syswake_irq: Shared PDC syswake IRQ number.
+ * @domain: IRQ domain for PDC peripheral and syswake IRQs.
+ * @pdc_base: Base of PDC registers.
+ * @irq_route: Cached version of PDC_IRQ_ROUTE register.
+ * @lock: Lock to protect the PDC syswake registers and the cached
+ * values of those registers in this struct.
+ */
+struct pdc_intc_priv {
+ unsigned int nr_perips;
+ unsigned int nr_syswakes;
+ unsigned int *perip_irqs;
+ unsigned int syswake_irq;
+ struct irq_domain *domain;
+ void __iomem *pdc_base;
+
+ u32 irq_route;
+ raw_spinlock_t lock;
+};
+
+static void pdc_write(struct pdc_intc_priv *priv, unsigned int reg_offs,
+ unsigned int data)
+{
+ iowrite32(data, priv->pdc_base + reg_offs);
+}
+
+static unsigned int pdc_read(struct pdc_intc_priv *priv,
+ unsigned int reg_offs)
+{
+ return ioread32(priv->pdc_base + reg_offs);
+}
+
+/* Generic IRQ callbacks */
+
+#define SYS0_HWIRQ 8
+
+static unsigned int hwirq_is_syswake(irq_hw_number_t hw)
+{
+ return hw >= SYS0_HWIRQ;
+}
+
+static unsigned int hwirq_to_syswake(irq_hw_number_t hw)
+{
+ return hw - SYS0_HWIRQ;
+}
+
+static irq_hw_number_t syswake_to_hwirq(unsigned int syswake)
+{
+ return SYS0_HWIRQ + syswake;
+}
+
+static struct pdc_intc_priv *irqd_to_priv(struct irq_data *data)
+{
+ return (struct pdc_intc_priv *)data->domain->host_data;
+}
+
+/*
+ * perip_irq_mask() and perip_irq_unmask() use IRQ_ROUTE which also contains
+ * wake bits, therefore we cannot use the generic irqchip mask callbacks as they
+ * cache the mask.
+ */
+
+static void perip_irq_mask(struct irq_data *data)
+{
+ struct pdc_intc_priv *priv = irqd_to_priv(data);
+
+ raw_spin_lock(&priv->lock);
+ priv->irq_route &= ~data->mask;
+ pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+ raw_spin_unlock(&priv->lock);
+}
+
+static void perip_irq_unmask(struct irq_data *data)
+{
+ struct pdc_intc_priv *priv = irqd_to_priv(data);
+
+ raw_spin_lock(&priv->lock);
+ priv->irq_route |= data->mask;
+ pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+ raw_spin_unlock(&priv->lock);
+}
+
+static int syswake_irq_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct pdc_intc_priv *priv = irqd_to_priv(data);
+ unsigned int syswake = hwirq_to_syswake(data->hwirq);
+ unsigned int irq_mode;
+ unsigned int soc_sys_wake_regoff, soc_sys_wake;
+
+ /* translate to syswake IRQ mode */
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ irq_mode = PDC_SYS_WAKE_INT_CHANGE;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ irq_mode = PDC_SYS_WAKE_INT_UP;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ irq_mode = PDC_SYS_WAKE_INT_DOWN;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ irq_mode = PDC_SYS_WAKE_INT_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ irq_mode = PDC_SYS_WAKE_INT_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ raw_spin_lock(&priv->lock);
+
+ /* set the IRQ mode */
+ soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + syswake*PDC_SYS_WAKE_STRIDE;
+ soc_sys_wake = pdc_read(priv, soc_sys_wake_regoff);
+ soc_sys_wake &= ~PDC_SYS_WAKE_INT_MODE;
+ soc_sys_wake |= irq_mode << PDC_SYS_WAKE_INT_MODE_SHIFT;
+ pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
+
+ /* and update the handler */
+ irq_setup_alt_chip(data, flow_type);
+
+ raw_spin_unlock(&priv->lock);
+
+ return 0;
+}
+
+/* applies to both peripheral and syswake interrupts */
+static int pdc_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct pdc_intc_priv *priv = irqd_to_priv(data);
+ irq_hw_number_t hw = data->hwirq;
+ unsigned int mask = (1 << 16) << hw;
+ unsigned int dst_irq;
+
+ raw_spin_lock(&priv->lock);
+ if (on)
+ priv->irq_route |= mask;
+ else
+ priv->irq_route &= ~mask;
+ pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+ raw_spin_unlock(&priv->lock);
+
+ /* control the destination IRQ wakeup too for standby mode */
+ if (hwirq_is_syswake(hw))
+ dst_irq = priv->syswake_irq;
+ else
+ dst_irq = priv->perip_irqs[hw];
+ irq_set_irq_wake(dst_irq, on);
+
+ return 0;
+}
+
+static void pdc_intc_perip_isr(unsigned int irq, struct irq_desc *desc)
+{
+ struct pdc_intc_priv *priv;
+ unsigned int i, irq_no;
+
+ priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
+
+ /* find the peripheral number */
+ for (i = 0; i < priv->nr_perips; ++i)
+ if (irq == priv->perip_irqs[i])
+ goto found;
+
+ /* should never get here */
+ return;
+found:
+
+ /* pass on the interrupt */
+ irq_no = irq_linear_revmap(priv->domain, i);
+ generic_handle_irq(irq_no);
+}
+
+static void pdc_intc_syswake_isr(unsigned int irq, struct irq_desc *desc)
+{
+ struct pdc_intc_priv *priv;
+ unsigned int syswake, irq_no;
+ unsigned int status;
+
+ priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc);
+
+ status = pdc_read(priv, PDC_IRQ_STATUS) &
+ pdc_read(priv, PDC_IRQ_ENABLE);
+ status &= (1 << priv->nr_syswakes) - 1;
+
+ for (syswake = 0; status; status >>= 1, ++syswake) {
+ /* Has this sys_wake triggered? */
+ if (!(status & 1))
+ continue;
+
+ irq_no = irq_linear_revmap(priv->domain,
+ syswake_to_hwirq(syswake));
+ generic_handle_irq(irq_no);
+ }
+}
+
+static void pdc_intc_setup(struct pdc_intc_priv *priv)
+{
+ int i;
+ unsigned int soc_sys_wake_regoff;
+ unsigned int soc_sys_wake;
+
+ /*
+ * Mask all syswake interrupts before routing, or we could receive an
+ * interrupt before we're ready to handle it.
+ */
+ pdc_write(priv, PDC_IRQ_ENABLE, 0);
+
+ /*
+ * Enable routing of all syswakes
+ * Disable all wake sources
+ */
+ priv->irq_route = ((PDC_IRQ_ROUTE_EXT_EN_SYS0 << priv->nr_syswakes) -
+ PDC_IRQ_ROUTE_EXT_EN_SYS0);
+ pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route);
+
+ /* Initialise syswake IRQ */
+ for (i = 0; i < priv->nr_syswakes; ++i) {
+ /* set the IRQ mode to none */
+ soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + i*PDC_SYS_WAKE_STRIDE;
+ soc_sys_wake = PDC_SYS_WAKE_INT_NONE
+ << PDC_SYS_WAKE_INT_MODE_SHIFT;
+ pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake);
+ }
+}
+
+static int pdc_intc_probe(struct platform_device *pdev)
+{
+ struct pdc_intc_priv *priv;
+ struct device_node *node = pdev->dev.of_node;
+ struct resource *res_regs;
+ struct irq_chip_generic *gc;
+ unsigned int i;
+ int irq, ret;
+ u32 val;
+
+ if (!node)
+ return -ENOENT;
+
+ /* Get registers */
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res_regs == NULL) {
+ dev_err(&pdev->dev, "cannot find registers resource\n");
+ return -ENOENT;
+ }
+
+ /* Allocate driver data */
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "cannot allocate device data\n");
+ return -ENOMEM;
+ }
+ raw_spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+
+ /* Ioremap the registers */
+ priv->pdc_base = devm_ioremap(&pdev->dev, res_regs->start,
+ res_regs->end - res_regs->start);
+ if (!priv->pdc_base)
+ return -EIO;
+
+ /* Get number of peripherals */
+ ret = of_property_read_u32(node, "num-perips", &val);
+ if (ret) {
+ dev_err(&pdev->dev, "No num-perips node property found\n");
+ return -EINVAL;
+ }
+ if (val > SYS0_HWIRQ) {
+ dev_err(&pdev->dev, "num-perips (%u) out of range\n", val);
+ return -EINVAL;
+ }
+ priv->nr_perips = val;
+
+ /* Get number of syswakes */
+ ret = of_property_read_u32(node, "num-syswakes", &val);
+ if (ret) {
+ dev_err(&pdev->dev, "No num-syswakes node property found\n");
+ return -EINVAL;
+ }
+ if (val > SYS0_HWIRQ) {
+ dev_err(&pdev->dev, "num-syswakes (%u) out of range\n", val);
+ return -EINVAL;
+ }
+ priv->nr_syswakes = val;
+
+ /* Get peripheral IRQ numbers */
+ priv->perip_irqs = devm_kzalloc(&pdev->dev, 4 * priv->nr_perips,
+ GFP_KERNEL);
+ if (!priv->perip_irqs) {
+ dev_err(&pdev->dev, "cannot allocate perip IRQ list\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < priv->nr_perips; ++i) {
+ irq = platform_get_irq(pdev, 1 + i);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "cannot find perip IRQ #%u\n", i);
+ return irq;
+ }
+ priv->perip_irqs[i] = irq;
+ }
+ /* check if too many were provided */
+ if (platform_get_irq(pdev, 1 + i) >= 0) {
+ dev_err(&pdev->dev, "surplus perip IRQs detected\n");
+ return -EINVAL;
+ }
+
+ /* Get syswake IRQ number */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "cannot find syswake IRQ\n");
+ return irq;
+ }
+ priv->syswake_irq = irq;
+
+ /* Set up an IRQ domain */
+ priv->domain = irq_domain_add_linear(node, 16, &irq_generic_chip_ops,
+ priv);
+ if (unlikely(!priv->domain)) {
+ dev_err(&pdev->dev, "cannot add IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Set up 2 generic irq chips with 2 chip types.
+ * The first one for peripheral irqs (only 1 chip type used)
+ * The second one for syswake irqs (edge and level chip types)
+ */
+ ret = irq_alloc_domain_generic_chips(priv->domain, 8, 2, "pdc",
+ handle_level_irq, 0, 0,
+ IRQ_GC_INIT_NESTED_LOCK);
+ if (ret)
+ goto err_generic;
+
+ /* peripheral interrupt chip */
+
+ gc = irq_get_domain_generic_chip(priv->domain, 0);
+ gc->unused = ~(BIT(priv->nr_perips) - 1);
+ gc->reg_base = priv->pdc_base;
+ /*
+ * IRQ_ROUTE contains wake bits, so we can't use the generic versions as
+ * they cache the mask
+ */
+ gc->chip_types[0].regs.mask = PDC_IRQ_ROUTE;
+ gc->chip_types[0].chip.irq_mask = perip_irq_mask;
+ gc->chip_types[0].chip.irq_unmask = perip_irq_unmask;
+ gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake;
+
+ /* syswake interrupt chip */
+
+ gc = irq_get_domain_generic_chip(priv->domain, 8);
+ gc->unused = ~(BIT(priv->nr_syswakes) - 1);
+ gc->reg_base = priv->pdc_base;
+
+ /* edge interrupts */
+ gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[0].handler = handle_edge_irq;
+ gc->chip_types[0].regs.ack = PDC_IRQ_CLEAR;
+ gc->chip_types[0].regs.mask = PDC_IRQ_ENABLE;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[0].chip.irq_set_type = syswake_irq_set_type;
+ gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake;
+ /* for standby we pass on to the shared syswake IRQ */
+ gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
+ /* level interrupts */
+ gc->chip_types[1].type = IRQ_TYPE_LEVEL_MASK;
+ gc->chip_types[1].handler = handle_level_irq;
+ gc->chip_types[1].regs.ack = PDC_IRQ_CLEAR;
+ gc->chip_types[1].regs.mask = PDC_IRQ_ENABLE;
+ gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types[1].chip.irq_set_type = syswake_irq_set_type;
+ gc->chip_types[1].chip.irq_set_wake = pdc_irq_set_wake;
+ /* for standby we pass on to the shared syswake IRQ */
+ gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
+ /* Set up the hardware to enable interrupt routing */
+ pdc_intc_setup(priv);
+
+ /* Setup chained handlers for the peripheral IRQs */
+ for (i = 0; i < priv->nr_perips; ++i) {
+ irq = priv->perip_irqs[i];
+ irq_set_handler_data(irq, priv);
+ irq_set_chained_handler(irq, pdc_intc_perip_isr);
+ }
+
+ /* Setup chained handler for the syswake IRQ */
+ irq_set_handler_data(priv->syswake_irq, priv);
+ irq_set_chained_handler(priv->syswake_irq, pdc_intc_syswake_isr);
+
+ dev_info(&pdev->dev,
+ "PDC IRQ controller initialised (%u perip IRQs, %u syswake IRQs)\n",
+ priv->nr_perips,
+ priv->nr_syswakes);
+
+ return 0;
+err_generic:
+ irq_domain_remove(priv->domain);
+ return ret;
+}
+
+static int pdc_intc_remove(struct platform_device *pdev)
+{
+ struct pdc_intc_priv *priv = platform_get_drvdata(pdev);
+
+ irq_domain_remove(priv->domain);
+ return 0;
+}
+
+static const struct of_device_id pdc_intc_match[] = {
+ { .compatible = "img,pdc-intc" },
+ {}
+};
+
+static struct platform_driver pdc_intc_driver = {
+ .driver = {
+ .name = "pdc-intc",
+ .of_match_table = pdc_intc_match,
+ },
+ .probe = pdc_intc_probe,
+ .remove = pdc_intc_remove,
+};
+
+static int __init pdc_intc_init(void)
+{
+ return platform_driver_register(&pdc_intc_driver);
+}
+core_initcall(pdc_intc_init);
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c
new file mode 100644
index 00000000000..2cb7cd0bc2f
--- /dev/null
+++ b/drivers/irqchip/irq-mmp.c
@@ -0,0 +1,495 @@
+/*
+ * linux/arch/arm/mach-mmp/irq.c
+ *
+ * Generic IRQ handling, GPIO IRQ demultiplexing, etc.
+ * Copyright (C) 2008 - 2012 Marvell Technology Group Ltd.
+ *
+ * Author: Bin Yang <bin.yang@marvell.com>
+ * Haojian Zhuang <haojian.zhuang@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <asm/exception.h>
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
+
+#define MAX_ICU_NR 16
+
+#define PJ1_INT_SEL 0x10c
+#define PJ4_INT_SEL 0x104
+
+/* bit fields in PJ1_INT_SEL and PJ4_INT_SEL */
+#define SEL_INT_PENDING (1 << 6)
+#define SEL_INT_NUM_MASK 0x3f
+
+struct icu_chip_data {
+ int nr_irqs;
+ unsigned int virq_base;
+ unsigned int cascade_irq;
+ void __iomem *reg_status;
+ void __iomem *reg_mask;
+ unsigned int conf_enable;
+ unsigned int conf_disable;
+ unsigned int conf_mask;
+ unsigned int clr_mfp_irq_base;
+ unsigned int clr_mfp_hwirq;
+ struct irq_domain *domain;
+};
+
+struct mmp_intc_conf {
+ unsigned int conf_enable;
+ unsigned int conf_disable;
+ unsigned int conf_mask;
+};
+
+static void __iomem *mmp_icu_base;
+static struct icu_chip_data icu_data[MAX_ICU_NR];
+static int max_icu_nr;
+
+extern void mmp2_clear_pmic_int(void);
+
+static void icu_mask_ack_irq(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct icu_chip_data *data = (struct icu_chip_data *)domain->host_data;
+ int hwirq;
+ u32 r;
+
+ hwirq = d->irq - data->virq_base;
+ if (data == &icu_data[0]) {
+ r = readl_relaxed(mmp_icu_base + (hwirq << 2));
+ r &= ~data->conf_mask;
+ r |= data->conf_disable;
+ writel_relaxed(r, mmp_icu_base + (hwirq << 2));
+ } else {
+#ifdef CONFIG_CPU_MMP2
+ if ((data->virq_base == data->clr_mfp_irq_base)
+ && (hwirq == data->clr_mfp_hwirq))
+ mmp2_clear_pmic_int();
+#endif
+ r = readl_relaxed(data->reg_mask) | (1 << hwirq);
+ writel_relaxed(r, data->reg_mask);
+ }
+}
+
+static void icu_mask_irq(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct icu_chip_data *data = (struct icu_chip_data *)domain->host_data;
+ int hwirq;
+ u32 r;
+
+ hwirq = d->irq - data->virq_base;
+ if (data == &icu_data[0]) {
+ r = readl_relaxed(mmp_icu_base + (hwirq << 2));
+ r &= ~data->conf_mask;
+ r |= data->conf_disable;
+ writel_relaxed(r, mmp_icu_base + (hwirq << 2));
+ } else {
+ r = readl_relaxed(data->reg_mask) | (1 << hwirq);
+ writel_relaxed(r, data->reg_mask);
+ }
+}
+
+static void icu_unmask_irq(struct irq_data *d)
+{
+ struct irq_domain *domain = d->domain;
+ struct icu_chip_data *data = (struct icu_chip_data *)domain->host_data;
+ int hwirq;
+ u32 r;
+
+ hwirq = d->irq - data->virq_base;
+ if (data == &icu_data[0]) {
+ r = readl_relaxed(mmp_icu_base + (hwirq << 2));
+ r &= ~data->conf_mask;
+ r |= data->conf_enable;
+ writel_relaxed(r, mmp_icu_base + (hwirq << 2));
+ } else {
+ r = readl_relaxed(data->reg_mask) & ~(1 << hwirq);
+ writel_relaxed(r, data->reg_mask);
+ }
+}
+
+struct irq_chip icu_irq_chip = {
+ .name = "icu_irq",
+ .irq_mask = icu_mask_irq,
+ .irq_mask_ack = icu_mask_ack_irq,
+ .irq_unmask = icu_unmask_irq,
+};
+
+static void icu_mux_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_domain *domain;
+ struct icu_chip_data *data;
+ int i;
+ unsigned long mask, status, n;
+
+ for (i = 1; i < max_icu_nr; i++) {
+ if (irq == icu_data[i].cascade_irq) {
+ domain = icu_data[i].domain;
+ data = (struct icu_chip_data *)domain->host_data;
+ break;
+ }
+ }
+ if (i >= max_icu_nr) {
+ pr_err("Spurious irq %d in MMP INTC\n", irq);
+ return;
+ }
+
+ mask = readl_relaxed(data->reg_mask);
+ while (1) {
+ status = readl_relaxed(data->reg_status) & ~mask;
+ if (status == 0)
+ break;
+ for_each_set_bit(n, &status, BITS_PER_LONG) {
+ generic_handle_irq(icu_data[i].virq_base + n);
+ }
+ }
+}
+
+static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ return 0;
+}
+
+static int mmp_irq_domain_xlate(struct irq_domain *d, struct device_node *node,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ *out_hwirq = intspec[0];
+ return 0;
+}
+
+const struct irq_domain_ops mmp_irq_domain_ops = {
+ .map = mmp_irq_domain_map,
+ .xlate = mmp_irq_domain_xlate,
+};
+
+static struct mmp_intc_conf mmp_conf = {
+ .conf_enable = 0x51,
+ .conf_disable = 0x0,
+ .conf_mask = 0x7f,
+};
+
+static struct mmp_intc_conf mmp2_conf = {
+ .conf_enable = 0x20,
+ .conf_disable = 0x0,
+ .conf_mask = 0x7f,
+};
+
+static asmlinkage void __exception_irq_entry
+mmp_handle_irq(struct pt_regs *regs)
+{
+ int irq, hwirq;
+
+ hwirq = readl_relaxed(mmp_icu_base + PJ1_INT_SEL);
+ if (!(hwirq & SEL_INT_PENDING))
+ return;
+ hwirq &= SEL_INT_NUM_MASK;
+ irq = irq_find_mapping(icu_data[0].domain, hwirq);
+ handle_IRQ(irq, regs);
+}
+
+static asmlinkage void __exception_irq_entry
+mmp2_handle_irq(struct pt_regs *regs)
+{
+ int irq, hwirq;
+
+ hwirq = readl_relaxed(mmp_icu_base + PJ4_INT_SEL);
+ if (!(hwirq & SEL_INT_PENDING))
+ return;
+ hwirq &= SEL_INT_NUM_MASK;
+ irq = irq_find_mapping(icu_data[0].domain, hwirq);
+ handle_IRQ(irq, regs);
+}
+
+/* MMP (ARMv5) */
+void __init icu_init_irq(void)
+{
+ int irq;
+
+ max_icu_nr = 1;
+ mmp_icu_base = ioremap(0xd4282000, 0x1000);
+ icu_data[0].conf_enable = mmp_conf.conf_enable;
+ icu_data[0].conf_disable = mmp_conf.conf_disable;
+ icu_data[0].conf_mask = mmp_conf.conf_mask;
+ icu_data[0].nr_irqs = 64;
+ icu_data[0].virq_base = 0;
+ icu_data[0].domain = irq_domain_add_legacy(NULL, 64, 0, 0,
+ &irq_domain_simple_ops,
+ &icu_data[0]);
+ for (irq = 0; irq < 64; irq++) {
+ icu_mask_irq(irq_get_irq_data(irq));
+ irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+ irq_set_default_host(icu_data[0].domain);
+ set_handle_irq(mmp_handle_irq);
+}
+
+/* MMP2 (ARMv7) */
+void __init mmp2_init_icu(void)
+{
+ int irq, end;
+
+ max_icu_nr = 8;
+ mmp_icu_base = ioremap(0xd4282000, 0x1000);
+ icu_data[0].conf_enable = mmp2_conf.conf_enable;
+ icu_data[0].conf_disable = mmp2_conf.conf_disable;
+ icu_data[0].conf_mask = mmp2_conf.conf_mask;
+ icu_data[0].nr_irqs = 64;
+ icu_data[0].virq_base = 0;
+ icu_data[0].domain = irq_domain_add_legacy(NULL, 64, 0, 0,
+ &irq_domain_simple_ops,
+ &icu_data[0]);
+ icu_data[1].reg_status = mmp_icu_base + 0x150;
+ icu_data[1].reg_mask = mmp_icu_base + 0x168;
+ icu_data[1].clr_mfp_irq_base = icu_data[0].virq_base +
+ icu_data[0].nr_irqs;
+ icu_data[1].clr_mfp_hwirq = 1; /* offset to IRQ_MMP2_PMIC_BASE */
+ icu_data[1].nr_irqs = 2;
+ icu_data[1].cascade_irq = 4;
+ icu_data[1].virq_base = icu_data[0].virq_base + icu_data[0].nr_irqs;
+ icu_data[1].domain = irq_domain_add_legacy(NULL, icu_data[1].nr_irqs,
+ icu_data[1].virq_base, 0,
+ &irq_domain_simple_ops,
+ &icu_data[1]);
+ icu_data[2].reg_status = mmp_icu_base + 0x154;
+ icu_data[2].reg_mask = mmp_icu_base + 0x16c;
+ icu_data[2].nr_irqs = 2;
+ icu_data[2].cascade_irq = 5;
+ icu_data[2].virq_base = icu_data[1].virq_base + icu_data[1].nr_irqs;
+ icu_data[2].domain = irq_domain_add_legacy(NULL, icu_data[2].nr_irqs,
+ icu_data[2].virq_base, 0,
+ &irq_domain_simple_ops,
+ &icu_data[2]);
+ icu_data[3].reg_status = mmp_icu_base + 0x180;
+ icu_data[3].reg_mask = mmp_icu_base + 0x17c;
+ icu_data[3].nr_irqs = 3;
+ icu_data[3].cascade_irq = 9;
+ icu_data[3].virq_base = icu_data[2].virq_base + icu_data[2].nr_irqs;
+ icu_data[3].domain = irq_domain_add_legacy(NULL, icu_data[3].nr_irqs,
+ icu_data[3].virq_base, 0,
+ &irq_domain_simple_ops,
+ &icu_data[3]);
+ icu_data[4].reg_status = mmp_icu_base + 0x158;
+ icu_data[4].reg_mask = mmp_icu_base + 0x170;
+ icu_data[4].nr_irqs = 5;
+ icu_data[4].cascade_irq = 17;
+ icu_data[4].virq_base = icu_data[3].virq_base + icu_data[3].nr_irqs;
+ icu_data[4].domain = irq_domain_add_legacy(NULL, icu_data[4].nr_irqs,
+ icu_data[4].virq_base, 0,
+ &irq_domain_simple_ops,
+ &icu_data[4]);
+ icu_data[5].reg_status = mmp_icu_base + 0x15c;
+ icu_data[5].reg_mask = mmp_icu_base + 0x174;
+ icu_data[5].nr_irqs = 15;
+ icu_data[5].cascade_irq = 35;
+ icu_data[5].virq_base = icu_data[4].virq_base + icu_data[4].nr_irqs;
+ icu_data[5].domain = irq_domain_add_legacy(NULL, icu_data[5].nr_irqs,
+ icu_data[5].virq_base, 0,
+ &irq_domain_simple_ops,
+ &icu_data[5]);
+ icu_data[6].reg_status = mmp_icu_base + 0x160;
+ icu_data[6].reg_mask = mmp_icu_base + 0x178;
+ icu_data[6].nr_irqs = 2;
+ icu_data[6].cascade_irq = 51;
+ icu_data[6].virq_base = icu_data[5].virq_base + icu_data[5].nr_irqs;
+ icu_data[6].domain = irq_domain_add_legacy(NULL, icu_data[6].nr_irqs,
+ icu_data[6].virq_base, 0,
+ &irq_domain_simple_ops,
+ &icu_data[6]);
+ icu_data[7].reg_status = mmp_icu_base + 0x188;
+ icu_data[7].reg_mask = mmp_icu_base + 0x184;
+ icu_data[7].nr_irqs = 2;
+ icu_data[7].cascade_irq = 55;
+ icu_data[7].virq_base = icu_data[6].virq_base + icu_data[6].nr_irqs;
+ icu_data[7].domain = irq_domain_add_legacy(NULL, icu_data[7].nr_irqs,
+ icu_data[7].virq_base, 0,
+ &irq_domain_simple_ops,
+ &icu_data[7]);
+ end = icu_data[7].virq_base + icu_data[7].nr_irqs;
+ for (irq = 0; irq < end; irq++) {
+ icu_mask_irq(irq_get_irq_data(irq));
+ if (irq == icu_data[1].cascade_irq ||
+ irq == icu_data[2].cascade_irq ||
+ irq == icu_data[3].cascade_irq ||
+ irq == icu_data[4].cascade_irq ||
+ irq == icu_data[5].cascade_irq ||
+ irq == icu_data[6].cascade_irq ||
+ irq == icu_data[7].cascade_irq) {
+ irq_set_chip(irq, &icu_irq_chip);
+ irq_set_chained_handler(irq, icu_mux_irq_demux);
+ } else {
+ irq_set_chip_and_handler(irq, &icu_irq_chip,
+ handle_level_irq);
+ }
+ set_irq_flags(irq, IRQF_VALID);
+ }
+ irq_set_default_host(icu_data[0].domain);
+ set_handle_irq(mmp2_handle_irq);
+}
+
+#ifdef CONFIG_OF
+static int __init mmp_init_bases(struct device_node *node)
+{
+ int ret, nr_irqs, irq, i = 0;
+
+ ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", &nr_irqs);
+ if (ret) {
+ pr_err("Not found mrvl,intc-nr-irqs property\n");
+ return ret;
+ }
+
+ mmp_icu_base = of_iomap(node, 0);
+ if (!mmp_icu_base) {
+ pr_err("Failed to get interrupt controller register\n");
+ return -ENOMEM;
+ }
+
+ icu_data[0].virq_base = 0;
+ icu_data[0].domain = irq_domain_add_linear(node, nr_irqs,
+ &mmp_irq_domain_ops,
+ &icu_data[0]);
+ for (irq = 0; irq < nr_irqs; irq++) {
+ ret = irq_create_mapping(icu_data[0].domain, irq);
+ if (!ret) {
+ pr_err("Failed to mapping hwirq\n");
+ goto err;
+ }
+ if (!irq)
+ icu_data[0].virq_base = ret;
+ }
+ icu_data[0].nr_irqs = nr_irqs;
+ return 0;
+err:
+ if (icu_data[0].virq_base) {
+ for (i = 0; i < irq; i++)
+ irq_dispose_mapping(icu_data[0].virq_base + i);
+ }
+ irq_domain_remove(icu_data[0].domain);
+ iounmap(mmp_icu_base);
+ return -EINVAL;
+}
+
+static int __init mmp_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ int ret;
+
+ ret = mmp_init_bases(node);
+ if (ret < 0)
+ return ret;
+
+ icu_data[0].conf_enable = mmp_conf.conf_enable;
+ icu_data[0].conf_disable = mmp_conf.conf_disable;
+ icu_data[0].conf_mask = mmp_conf.conf_mask;
+ irq_set_default_host(icu_data[0].domain);
+ set_handle_irq(mmp_handle_irq);
+ max_icu_nr = 1;
+ return 0;
+}
+IRQCHIP_DECLARE(mmp_intc, "mrvl,mmp-intc", mmp_of_init);
+
+static int __init mmp2_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ int ret;
+
+ ret = mmp_init_bases(node);
+ if (ret < 0)
+ return ret;
+
+ icu_data[0].conf_enable = mmp2_conf.conf_enable;
+ icu_data[0].conf_disable = mmp2_conf.conf_disable;
+ icu_data[0].conf_mask = mmp2_conf.conf_mask;
+ irq_set_default_host(icu_data[0].domain);
+ set_handle_irq(mmp2_handle_irq);
+ max_icu_nr = 1;
+ return 0;
+}
+IRQCHIP_DECLARE(mmp2_intc, "mrvl,mmp2-intc", mmp2_of_init);
+
+static int __init mmp2_mux_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct resource res;
+ int i, ret, irq, j = 0;
+ u32 nr_irqs, mfp_irq;
+
+ if (!parent)
+ return -ENODEV;
+
+ i = max_icu_nr;
+ ret = of_property_read_u32(node, "mrvl,intc-nr-irqs",
+ &nr_irqs);
+ if (ret) {
+ pr_err("Not found mrvl,intc-nr-irqs property\n");
+ return -EINVAL;
+ }
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret < 0) {
+ pr_err("Not found reg property\n");
+ return -EINVAL;
+ }
+ icu_data[i].reg_status = mmp_icu_base + res.start;
+ ret = of_address_to_resource(node, 1, &res);
+ if (ret < 0) {
+ pr_err("Not found reg property\n");
+ return -EINVAL;
+ }
+ icu_data[i].reg_mask = mmp_icu_base + res.start;
+ icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0);
+ if (!icu_data[i].cascade_irq)
+ return -EINVAL;
+
+ icu_data[i].virq_base = 0;
+ icu_data[i].domain = irq_domain_add_linear(node, nr_irqs,
+ &mmp_irq_domain_ops,
+ &icu_data[i]);
+ for (irq = 0; irq < nr_irqs; irq++) {
+ ret = irq_create_mapping(icu_data[i].domain, irq);
+ if (!ret) {
+ pr_err("Failed to mapping hwirq\n");
+ goto err;
+ }
+ if (!irq)
+ icu_data[i].virq_base = ret;
+ }
+ icu_data[i].nr_irqs = nr_irqs;
+ if (!of_property_read_u32(node, "mrvl,clr-mfp-irq",
+ &mfp_irq)) {
+ icu_data[i].clr_mfp_irq_base = icu_data[i].virq_base;
+ icu_data[i].clr_mfp_hwirq = mfp_irq;
+ }
+ irq_set_chained_handler(icu_data[i].cascade_irq,
+ icu_mux_irq_demux);
+ max_icu_nr++;
+ return 0;
+err:
+ if (icu_data[i].virq_base) {
+ for (j = 0; j < irq; j++)
+ irq_dispose_mapping(icu_data[i].virq_base + j);
+ }
+ irq_domain_remove(icu_data[i].domain);
+ return -EINVAL;
+}
+IRQCHIP_DECLARE(mmp2_mux_intc, "mrvl,mmp2-mux-intc", mmp2_mux_of_init);
+#endif
diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c
index 69ea44ebcf6..4851afae38d 100644
--- a/drivers/irqchip/irq-sirfsoc.c
+++ b/drivers/irqchip/irq-sirfsoc.c
@@ -23,7 +23,7 @@
#define SIRFSOC_INT_RISC_LEVEL1 0x0024
#define SIRFSOC_INIT_IRQ_ID 0x0038
-#define SIRFSOC_NUM_IRQS 128
+#define SIRFSOC_NUM_IRQS 64
static struct irq_domain *sirfsoc_irqdomain;
@@ -32,15 +32,18 @@ sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
+ int ret;
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
- gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq);
- ct = gc->chip_types;
+ ret = irq_alloc_domain_generic_chips(sirfsoc_irqdomain, num, 1, "irq_sirfsoc",
+ handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ gc = irq_get_domain_generic_chip(sirfsoc_irqdomain, irq_start);
+ gc->reg_base = base;
+ ct = gc->chip_types;
ct->chip.irq_mask = irq_gc_mask_clr_bit;
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->regs.mask = SIRFSOC_INT_RISC_MASK0;
-
- irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0);
}
static asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs)
@@ -60,9 +63,8 @@ static int __init sirfsoc_irq_init(struct device_node *np, struct device_node *p
if (!base)
panic("unable to map intc cpu registers\n");
- /* using legacy because irqchip_generic does not work with linear */
- sirfsoc_irqdomain = irq_domain_add_legacy(np, SIRFSOC_NUM_IRQS, 0, 0,
- &irq_domain_simple_ops, base);
+ sirfsoc_irqdomain = irq_domain_add_linear(np, SIRFSOC_NUM_IRQS,
+ &irq_generic_chip_ops, base);
sirfsoc_alloc_gc(base, 0, 32);
sirfsoc_alloc_gc(base + 4, 32, SIRFSOC_NUM_IRQS - 32);