mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
dfc25e4503
These cleanup patches are mainly move stuff around and should all be harmless. They are mainly split out so that other branches can be based on top to avoid conflicts. Notable changes are: * We finally remove all mach/timex.h, after CLOCK_TICK_RATE is no longer used. (Uwe Kleine-König) * The Qualcomm MSM platform is split out into legacy mach-msm and new-style mach-qcom, to allow easier maintainance of the new hardware support without regressions. (Kumar Gala) * A rework of some of the Kconfig logic to simplify multiplatform support (Rob Herring) * Samsung Exynos gets closer to supporting multiplatform (Sachin Kamat and others) * mach-bcm3528 gets merged into mach-bcm (Stephen Warren) * at91 gains some common clock framework support (Alexandre Belloni, Jean-Jacques Hiblot and other French people). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIVAwUAUz/yOWCrR//JCVInAQLOPBAAwTMkMrD8S8ggz6vfiQHZNdRPAC7NUJ46 +eYKmBVi5d6EdnjNuRElWENsh0ZosSAUFHrXsIC2NdH9sAJ9HOqWNNLymuA59Jo9 HZ/Ze6xQXDPNV7TROPoXuIli/2OCOXyyQHJsfI7h9V3PCx31qo0B5OdCxU0mtXK6 r1giREhnJFwfQMF/FTdnzhalFJoSjWwv/nkpNmQDJKRLKj9GzwQqItqw68gV6RzU Gnt6YK+9xC1B0cfWTFhAm6kbr9i7mvHoMG5tE3no2uuJMn4K7TgeMqOyvPWhmUeB EZi656szT1m5VfRWOqG+7coZO2VM4GO4NI0Xfin3GHllugOYls1il/FAfCPMLiwh RvuOmQGCkLIpdkuHop5QaI/h1EzlHA59nzTjmGf1+wWPsm0CIg08XOD9izQbRnN9 EmRqn1/8POIi17xcWyeMp8LB0APsTI+IflZFaYprEY9VlLLA/Pd+7udULhs8Bq8y 1l6fB6aPZKnDKCBy/PEIR+y+EHFEbwfrx6zm/pxVDX6P5DlQMFWL78pdBoJUa2h8 3pm/bSzNU5OSz1nJMLJv2jBTtnM5BvFgQBUi2qJ9Lr+nUhJXKCJ80kE/nOlXoCIU J952p3OhkYTQQcjuUVQeTXvRUOGB7mKok0pDFZNE6c7faqxTCudMABQq/KbMFstU eE3cH5FyYj4= =GcBb -----END PGP SIGNATURE----- Merge tag 'cleanup-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull ARM SoC cleanups from Arnd Bergmann: "These cleanup patches are mainly move stuff around and should all be harmless. They are mainly split out so that other branches can be based on top to avoid conflicts. Notable changes are: - We finally remove all mach/timex.h, after CLOCK_TICK_RATE is no longer used (Uwe Kleine-König) - The Qualcomm MSM platform is split out into legacy mach-msm and new-style mach-qcom, to allow easier maintainance of the new hardware support without regressions (Kumar Gala) - A rework of some of the Kconfig logic to simplify multiplatform support (Rob Herring) - Samsung Exynos gets closer to supporting multiplatform (Sachin Kamat and others) - mach-bcm3528 gets merged into mach-bcm (Stephen Warren) - at91 gains some common clock framework support (Alexandre Belloni, Jean-Jacques Hiblot and other French people)" * tag 'cleanup-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (89 commits) ARM: hisi: select HAVE_ARM_SCU only for SMP ARM: efm32: allow uncompress debug output ARM: prima2: build reset code standalone ARM: at91: add PWM clock ARM: at91: move sam9261 SoC to common clk ARM: at91: prepare common clk transition for sam9261 SoC ARM: at91: updated the at91_dt_defconfig with support for the ADS7846 ARM: at91: dt: sam9261: Device Tree support for the at91sam9261ek ARM: at91: dt: defconfig: Added the sam9261 to the list of DT-enabled SOCs ARM: at91: dt: Add at91sam9261 dt SoC support ARM: at91: switch sam9rl to common clock framework ARM: at91/dt: define main clk frequency of at91sam9rlek ARM: at91/dt: define at91sam9rl clocks ARM: at91: prepare common clk transition for sam9rl SoCs ARM: at91: prepare sam9 dt boards transition to common clk ARM: at91: dt: sam9rl: Device Tree for the at91sam9rlek ARM: at91/defconfig: Add the sam9rl to the list of DT-enabled SOCs ARM: at91: Add at91sam9rl DT SoC support ARM: at91: prepare at91sam9rl DT transition ARM: at91/defconfig: refresh at91sam9260_9g20_defconfig ...
493 lines
13 KiB
C
493 lines
13 KiB
C
/*
|
|
* 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/hardirq.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 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 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
|