diff options
Diffstat (limited to 'drivers/irqchip/irq-mmp.c')
-rw-r--r-- | drivers/irqchip/irq-mmp.c | 86 |
1 files changed, 74 insertions, 12 deletions
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 14618dc0bd39..4a74ac7b7c42 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/irq.h> #include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/io.h> #include <linux/ioport.h> @@ -43,6 +44,7 @@ struct icu_chip_data { unsigned int conf_enable; unsigned int conf_disable; unsigned int conf_mask; + unsigned int conf2_mask; unsigned int clr_mfp_irq_base; unsigned int clr_mfp_hwirq; struct irq_domain *domain; @@ -52,9 +54,11 @@ struct mmp_intc_conf { unsigned int conf_enable; unsigned int conf_disable; unsigned int conf_mask; + unsigned int conf2_mask; }; static void __iomem *mmp_icu_base; +static void __iomem *mmp_icu2_base; static struct icu_chip_data icu_data[MAX_ICU_NR]; static int max_icu_nr; @@ -97,6 +101,16 @@ static void icu_mask_irq(struct irq_data *d) r &= ~data->conf_mask; r |= data->conf_disable; writel_relaxed(r, mmp_icu_base + (hwirq << 2)); + + if (data->conf2_mask) { + /* + * ICU1 (above) only controls PJ4 MP1; if using SMP, + * we need to also mask the MP2 and MM cores via ICU2. + */ + r = readl_relaxed(mmp_icu2_base + (hwirq << 2)); + r &= ~data->conf2_mask; + writel_relaxed(r, mmp_icu2_base + (hwirq << 2)); + } } else { r = readl_relaxed(data->reg_mask) | (1 << hwirq); writel_relaxed(r, data->reg_mask); @@ -132,11 +146,14 @@ struct irq_chip icu_irq_chip = { static void icu_mux_irq_demux(struct irq_desc *desc) { unsigned int irq = irq_desc_get_irq(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); struct irq_domain *domain; struct icu_chip_data *data; int i; unsigned long mask, status, n; + chained_irq_enter(chip, desc); + for (i = 1; i < max_icu_nr; i++) { if (irq == icu_data[i].cascade_irq) { domain = icu_data[i].domain; @@ -146,7 +163,7 @@ static void icu_mux_irq_demux(struct irq_desc *desc) } if (i >= max_icu_nr) { pr_err("Spurious irq %d in MMP INTC\n", irq); - return; + goto out; } mask = readl_relaxed(data->reg_mask); @@ -158,6 +175,9 @@ static void icu_mux_irq_demux(struct irq_desc *desc) generic_handle_irq(icu_data[i].virq_base + n); } } + +out: + chained_irq_exit(chip, desc); } static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq, @@ -194,6 +214,14 @@ static const struct mmp_intc_conf mmp2_conf = { MMP2_ICU_INT_ROUTE_PJ4_FIQ, }; +static struct mmp_intc_conf mmp3_conf = { + .conf_enable = 0x20, + .conf_disable = 0x0, + .conf_mask = MMP2_ICU_INT_ROUTE_PJ4_IRQ | + MMP2_ICU_INT_ROUTE_PJ4_FIQ, + .conf2_mask = 0xf0, +}; + static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs) { int hwirq; @@ -395,7 +423,6 @@ static int __init mmp_of_init(struct device_node *node, 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; @@ -414,19 +441,50 @@ static int __init mmp2_of_init(struct device_node *node, 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 mmp3_of_init(struct device_node *node, + struct device_node *parent) +{ + int ret; + + mmp_icu2_base = of_iomap(node, 1); + if (!mmp_icu2_base) { + pr_err("Failed to get interrupt controller register #2\n"); + return -ENODEV; + } + + ret = mmp_init_bases(node); + if (ret < 0) { + iounmap(mmp_icu2_base); + return ret; + } + + icu_data[0].conf_enable = mmp3_conf.conf_enable; + icu_data[0].conf_disable = mmp3_conf.conf_disable; + icu_data[0].conf_mask = mmp3_conf.conf_mask; + icu_data[0].conf2_mask = mmp3_conf.conf2_mask; + + if (!parent) { + /* This is the main interrupt controller. */ + set_handle_irq(mmp2_handle_irq); + } + + max_icu_nr = 1; + return 0; +} +IRQCHIP_DECLARE(mmp3_intc, "marvell,mmp3-intc", mmp3_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; + u32 reg[4]; if (!parent) return -ENODEV; @@ -438,18 +496,22 @@ static int __init mmp2_mux_of_init(struct device_node *node, 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); + + /* + * For historical reasons, the "regs" property of the + * mrvl,mmp2-mux-intc is not a regular "regs" property containing + * addresses on the parent bus, but offsets from the intc's base. + * That is why we can't use of_address_to_resource() here. + */ + ret = of_property_read_variable_u32_array(node, "reg", reg, + ARRAY_SIZE(reg), + ARRAY_SIZE(reg)); 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].reg_status = mmp_icu_base + reg[0]; + icu_data[i].reg_mask = mmp_icu_base + reg[2]; icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0); if (!icu_data[i].cascade_irq) return -EINVAL; |