linux-stable/drivers/irqchip/irq-mips-gic.c

1156 lines
28 KiB
C
Raw Normal View History

/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2008 Ralf Baechle (ralf@linux-mips.org)
* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
*/
#include <linux/bitmap.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqchip/mips-gic.h>
#include <linux/of_address.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <asm/mips-cm.h>
#include <asm/setup.h>
#include <asm/traps.h>
#include <dt-bindings/interrupt-controller/mips-gic.h>
unsigned int gic_present;
struct gic_pcpu_mask {
DECLARE_BITMAP(pcpu_mask, GIC_MAX_INTRS);
};
struct gic_irq_spec {
enum {
GIC_DEVICE,
GIC_IPI
} type;
union {
struct cpumask *ipimask;
unsigned int hwirq;
};
};
static unsigned long __gic_base_addr;
static void __iomem *gic_base;
static struct gic_pcpu_mask pcpu_masks[NR_CPUS];
static DEFINE_SPINLOCK(gic_lock);
static struct irq_domain *gic_irq_domain;
static struct irq_domain *gic_dev_domain;
static struct irq_domain *gic_ipi_domain;
static int gic_shared_intrs;
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
static int gic_vpes;
static unsigned int gic_cpu_pin;
2015-01-19 15:38:24 +00:00
static unsigned int timer_cpu_pin;
static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
static void __gic_irq_dispatch(void);
static inline u32 gic_read32(unsigned int reg)
{
return __raw_readl(gic_base + reg);
}
static inline u64 gic_read64(unsigned int reg)
{
return __raw_readq(gic_base + reg);
}
static inline unsigned long gic_read(unsigned int reg)
{
if (!mips_cm_is64)
return gic_read32(reg);
else
return gic_read64(reg);
}
static inline void gic_write32(unsigned int reg, u32 val)
{
return __raw_writel(val, gic_base + reg);
}
static inline void gic_write64(unsigned int reg, u64 val)
{
return __raw_writeq(val, gic_base + reg);
}
static inline void gic_write(unsigned int reg, unsigned long val)
{
if (!mips_cm_is64)
return gic_write32(reg, (u32)val);
else
return gic_write64(reg, (u64)val);
}
static inline void gic_update_bits(unsigned int reg, unsigned long mask,
unsigned long val)
{
unsigned long regval;
regval = gic_read(reg);
regval &= ~mask;
regval |= val;
gic_write(reg, regval);
}
static inline void gic_reset_mask(unsigned int intr)
{
gic_write(GIC_REG(SHARED, GIC_SH_RMASK) + GIC_INTR_OFS(intr),
1ul << GIC_INTR_BIT(intr));
}
static inline void gic_set_mask(unsigned int intr)
{
gic_write(GIC_REG(SHARED, GIC_SH_SMASK) + GIC_INTR_OFS(intr),
1ul << GIC_INTR_BIT(intr));
}
static inline void gic_set_polarity(unsigned int intr, unsigned int pol)
{
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_POLARITY) +
GIC_INTR_OFS(intr), 1ul << GIC_INTR_BIT(intr),
(unsigned long)pol << GIC_INTR_BIT(intr));
}
static inline void gic_set_trigger(unsigned int intr, unsigned int trig)
{
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_TRIGGER) +
GIC_INTR_OFS(intr), 1ul << GIC_INTR_BIT(intr),
(unsigned long)trig << GIC_INTR_BIT(intr));
}
static inline void gic_set_dual_edge(unsigned int intr, unsigned int dual)
{
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_DUAL) + GIC_INTR_OFS(intr),
1ul << GIC_INTR_BIT(intr),
(unsigned long)dual << GIC_INTR_BIT(intr));
}
static inline void gic_map_to_pin(unsigned int intr, unsigned int pin)
{
gic_write32(GIC_REG(SHARED, GIC_SH_INTR_MAP_TO_PIN_BASE) +
GIC_SH_MAP_TO_PIN(intr), GIC_MAP_TO_PIN_MSK | pin);
}
static inline void gic_map_to_vpe(unsigned int intr, unsigned int vpe)
{
gic_write(GIC_REG(SHARED, GIC_SH_INTR_MAP_TO_VPE_BASE) +
GIC_SH_MAP_TO_VPE_REG_OFF(intr, vpe),
GIC_SH_MAP_TO_VPE_REG_BIT(vpe));
}
#ifdef CONFIG_CLKSRC_MIPS_GIC
u64 gic_read_count(void)
{
unsigned int hi, hi2, lo;
if (mips_cm_is64)
return (u64)gic_read(GIC_REG(SHARED, GIC_SH_COUNTER));
do {
hi = gic_read32(GIC_REG(SHARED, GIC_SH_COUNTER_63_32));
lo = gic_read32(GIC_REG(SHARED, GIC_SH_COUNTER_31_00));
hi2 = gic_read32(GIC_REG(SHARED, GIC_SH_COUNTER_63_32));
} while (hi2 != hi);
return (((u64) hi) << 32) + lo;
}
unsigned int gic_get_count_width(void)
{
unsigned int bits, config;
config = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
bits = 32 + 4 * ((config & GIC_SH_CONFIG_COUNTBITS_MSK) >>
GIC_SH_CONFIG_COUNTBITS_SHF);
return bits;
}
void gic_write_compare(u64 cnt)
{
if (mips_cm_is64) {
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE), cnt);
} else {
gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI),
(int)(cnt >> 32));
gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO),
(int)(cnt & 0xffffffff));
}
}
void gic_write_cpu_compare(u64 cnt, int cpu)
{
unsigned long flags;
local_irq_save(flags);
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), mips_cm_vp_id(cpu));
if (mips_cm_is64) {
gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE), cnt);
} else {
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_HI),
(int)(cnt >> 32));
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_LO),
(int)(cnt & 0xffffffff));
}
local_irq_restore(flags);
}
u64 gic_read_compare(void)
{
unsigned int hi, lo;
if (mips_cm_is64)
return (u64)gic_read(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE));
hi = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI));
lo = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO));
return (((u64) hi) << 32) + lo;
}
void gic_start_count(void)
{
u32 gicconfig;
/* Start the counter */
gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
gicconfig &= ~(1 << GIC_SH_CONFIG_COUNTSTOP_SHF);
gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
}
void gic_stop_count(void)
{
u32 gicconfig;
/* Stop the counter */
gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
gicconfig |= 1 << GIC_SH_CONFIG_COUNTSTOP_SHF;
gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
}
#endif
unsigned gic_read_local_vp_id(void)
{
unsigned long ident;
ident = gic_read(GIC_REG(VPE_LOCAL, GIC_VP_IDENT));
return ident & GIC_VP_IDENT_VCNUM_MSK;
}
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
static bool gic_local_irq_is_routable(int intr)
{
u32 vpe_ctl;
/* All local interrupts are routable in EIC mode. */
if (cpu_has_veic)
return true;
vpe_ctl = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_CTL));
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
switch (intr) {
case GIC_LOCAL_INT_TIMER:
return vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK;
case GIC_LOCAL_INT_PERFCTR:
return vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK;
case GIC_LOCAL_INT_FDC:
return vpe_ctl & GIC_VPE_CTL_FDC_RTBL_MSK;
case GIC_LOCAL_INT_SWINT0:
case GIC_LOCAL_INT_SWINT1:
return vpe_ctl & GIC_VPE_CTL_SWINT_RTBL_MSK;
default:
return true;
}
}
static void gic_bind_eic_interrupt(int irq, int set)
{
/* Convert irq vector # to hw int # */
irq -= GIC_PIN_TO_VEC_OFFSET;
/* Set irq to use shadow set */
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_EIC_SHADOW_SET_BASE) +
GIC_VPE_EIC_SS(irq), set);
}
static void gic_send_ipi(struct irq_data *d, unsigned int cpu)
{
irq_hw_number_t hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(d));
gic_write(GIC_REG(SHARED, GIC_SH_WEDGE), GIC_SH_WEDGE_SET(hwirq));
}
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
int gic_get_c0_compare_int(void)
{
if (!gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER))
return MIPS_CPU_IRQ_BASE + cp0_compare_irq;
return irq_create_mapping(gic_irq_domain,
GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_TIMER));
}
int gic_get_c0_perfcount_int(void)
{
if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) {
/* Is the performance counter shared with the timer? */
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
if (cp0_perfcount_irq < 0)
return -1;
return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
}
return irq_create_mapping(gic_irq_domain,
GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR));
}
int gic_get_c0_fdc_int(void)
{
if (!gic_local_irq_is_routable(GIC_LOCAL_INT_FDC)) {
/* Is the FDC IRQ even present? */
if (cp0_fdc_irq < 0)
return -1;
return MIPS_CPU_IRQ_BASE + cp0_fdc_irq;
}
return irq_create_mapping(gic_irq_domain,
GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC));
}
int gic_get_usm_range(struct resource *gic_usm_res)
{
if (!gic_present)
return -1;
gic_usm_res->start = __gic_base_addr + USM_VISIBLE_SECTION_OFS;
gic_usm_res->end = gic_usm_res->start + (USM_VISIBLE_SECTION_SIZE - 1);
return 0;
}
static void gic_handle_shared_int(bool chained)
{
unsigned int i, intr, virq, gic_reg_step = mips_cm_is64 ? 8 : 4;
unsigned long *pcpu_mask;
unsigned long pending_reg, intrmask_reg;
DECLARE_BITMAP(pending, GIC_MAX_INTRS);
DECLARE_BITMAP(intrmask, GIC_MAX_INTRS);
/* Get per-cpu bitmaps */
pcpu_mask = pcpu_masks[smp_processor_id()].pcpu_mask;
pending_reg = GIC_REG(SHARED, GIC_SH_PEND);
intrmask_reg = GIC_REG(SHARED, GIC_SH_MASK);
for (i = 0; i < BITS_TO_LONGS(gic_shared_intrs); i++) {
pending[i] = gic_read(pending_reg);
intrmask[i] = gic_read(intrmask_reg);
pending_reg += gic_reg_step;
intrmask_reg += gic_reg_step;
tree-wide: replace config_enabled() with IS_ENABLED() The use of config_enabled() against config options is ambiguous. In practical terms, config_enabled() is equivalent to IS_BUILTIN(), but the author might have used it for the meaning of IS_ENABLED(). Using IS_ENABLED(), IS_BUILTIN(), IS_MODULE() etc. makes the intention clearer. This commit replaces config_enabled() with IS_ENABLED() where possible. This commit is only touching bool config options. I noticed two cases where config_enabled() is used against a tristate option: - config_enabled(CONFIG_HWMON) [ drivers/net/wireless/ath/ath10k/thermal.c ] - config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE) [ drivers/gpu/drm/gma500/opregion.c ] I did not touch them because they should be converted to IS_BUILTIN() in order to keep the logic, but I was not sure it was the authors' intention. Link: http://lkml.kernel.org/r/1465215656-20569-1-git-send-email-yamada.masahiro@socionext.com Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Acked-by: Kees Cook <keescook@chromium.org> Cc: Stas Sergeev <stsp@list.ru> Cc: Matt Redfearn <matt.redfearn@imgtec.com> Cc: Joshua Kinard <kumba@gentoo.org> Cc: Jiri Slaby <jslaby@suse.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Borislav Petkov <bp@suse.de> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: "Dmitry V. Levin" <ldv@altlinux.org> Cc: yu-cheng yu <yu-cheng.yu@intel.com> Cc: James Hogan <james.hogan@imgtec.com> Cc: Brian Gerst <brgerst@gmail.com> Cc: Johannes Berg <johannes@sipsolutions.net> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Will Drewry <wad@chromium.org> Cc: Nikolay Martynov <mar.kolya@gmail.com> Cc: Huacai Chen <chenhc@lemote.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com> Cc: Rafal Milecki <zajec5@gmail.com> Cc: James Cowgill <James.Cowgill@imgtec.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: Alex Smith <alex.smith@imgtec.com> Cc: Adam Buchbinder <adam.buchbinder@gmail.com> Cc: Qais Yousef <qais.yousef@imgtec.com> Cc: Jiang Liu <jiang.liu@linux.intel.com> Cc: Mikko Rapeli <mikko.rapeli@iki.fi> Cc: Paul Gortmaker <paul.gortmaker@windriver.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: Brian Norris <computersforpeace@gmail.com> Cc: Hidehiro Kawai <hidehiro.kawai.ez@hitachi.com> Cc: "Luis R. Rodriguez" <mcgrof@do-not-panic.com> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Ingo Molnar <mingo@redhat.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Cc: Roland McGrath <roland@hack.frob.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Kalle Valo <kvalo@qca.qualcomm.com> Cc: Viresh Kumar <viresh.kumar@linaro.org> Cc: Tony Wu <tung7970@gmail.com> Cc: Huaitong Han <huaitong.han@intel.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Juergen Gross <jgross@suse.com> Cc: Jason Cooper <jason@lakedaemon.net> Cc: "David S. Miller" <davem@davemloft.net> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Andrea Gelmini <andrea.gelmini@gelma.net> Cc: David Woodhouse <dwmw2@infradead.org> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Rabin Vincent <rabin@rab.in> Cc: "Maciej W. Rozycki" <macro@imgtec.com> Cc: David Daney <david.daney@cavium.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-08-03 20:45:50 +00:00
if (!IS_ENABLED(CONFIG_64BIT) || mips_cm_is64)
continue;
pending[i] |= (u64)gic_read(pending_reg) << 32;
intrmask[i] |= (u64)gic_read(intrmask_reg) << 32;
pending_reg += gic_reg_step;
intrmask_reg += gic_reg_step;
}
bitmap_and(pending, pending, intrmask, gic_shared_intrs);
bitmap_and(pending, pending, pcpu_mask, gic_shared_intrs);
for_each_set_bit(intr, pending, gic_shared_intrs) {
virq = irq_linear_revmap(gic_irq_domain,
GIC_SHARED_TO_HWIRQ(intr));
if (chained)
generic_handle_irq(virq);
else
do_IRQ(virq);
}
}
static void gic_mask_irq(struct irq_data *d)
{
gic_reset_mask(GIC_HWIRQ_TO_SHARED(d->hwirq));
}
static void gic_unmask_irq(struct irq_data *d)
{
gic_set_mask(GIC_HWIRQ_TO_SHARED(d->hwirq));
}
static void gic_ack_irq(struct irq_data *d)
{
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
gic_write(GIC_REG(SHARED, GIC_SH_WEDGE), GIC_SH_WEDGE_CLR(irq));
}
static int gic_set_type(struct irq_data *d, unsigned int type)
{
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
unsigned long flags;
bool is_edge;
spin_lock_irqsave(&gic_lock, flags);
switch (type & IRQ_TYPE_SENSE_MASK) {
case IRQ_TYPE_EDGE_FALLING:
gic_set_polarity(irq, GIC_POL_NEG);
gic_set_trigger(irq, GIC_TRIG_EDGE);
gic_set_dual_edge(irq, GIC_TRIG_DUAL_DISABLE);
is_edge = true;
break;
case IRQ_TYPE_EDGE_RISING:
gic_set_polarity(irq, GIC_POL_POS);
gic_set_trigger(irq, GIC_TRIG_EDGE);
gic_set_dual_edge(irq, GIC_TRIG_DUAL_DISABLE);
is_edge = true;
break;
case IRQ_TYPE_EDGE_BOTH:
/* polarity is irrelevant in this case */
gic_set_trigger(irq, GIC_TRIG_EDGE);
gic_set_dual_edge(irq, GIC_TRIG_DUAL_ENABLE);
is_edge = true;
break;
case IRQ_TYPE_LEVEL_LOW:
gic_set_polarity(irq, GIC_POL_NEG);
gic_set_trigger(irq, GIC_TRIG_LEVEL);
gic_set_dual_edge(irq, GIC_TRIG_DUAL_DISABLE);
is_edge = false;
break;
case IRQ_TYPE_LEVEL_HIGH:
default:
gic_set_polarity(irq, GIC_POL_POS);
gic_set_trigger(irq, GIC_TRIG_LEVEL);
gic_set_dual_edge(irq, GIC_TRIG_DUAL_DISABLE);
is_edge = false;
break;
}
if (is_edge)
irq_set_chip_handler_name_locked(d, &gic_edge_irq_controller,
handle_edge_irq, NULL);
else
irq_set_chip_handler_name_locked(d, &gic_level_irq_controller,
handle_level_irq, NULL);
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
}
#ifdef CONFIG_SMP
static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
bool force)
{
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq);
cpumask_t tmp = CPU_MASK_NONE;
unsigned long flags;
int i;
cpumask_and(&tmp, cpumask, cpu_online_mask);
if (cpumask_empty(&tmp))
return -EINVAL;
/* Assumption : cpumask refers to a single CPU */
spin_lock_irqsave(&gic_lock, flags);
/* Re-route this IRQ */
gic_map_to_vpe(irq, mips_cm_vp_id(cpumask_first(&tmp)));
/* Update the pcpu_masks */
for (i = 0; i < min(gic_vpes, NR_CPUS); i++)
clear_bit(irq, pcpu_masks[i].pcpu_mask);
set_bit(irq, pcpu_masks[cpumask_first(&tmp)].pcpu_mask);
cpumask_copy(irq_data_get_affinity_mask(d), cpumask);
spin_unlock_irqrestore(&gic_lock, flags);
return IRQ_SET_MASK_OK_NOCOPY;
}
#endif
static struct irq_chip gic_level_irq_controller = {
.name = "MIPS GIC",
.irq_mask = gic_mask_irq,
.irq_unmask = gic_unmask_irq,
.irq_set_type = gic_set_type,
#ifdef CONFIG_SMP
.irq_set_affinity = gic_set_affinity,
#endif
};
static struct irq_chip gic_edge_irq_controller = {
.name = "MIPS GIC",
.irq_ack = gic_ack_irq,
.irq_mask = gic_mask_irq,
.irq_unmask = gic_unmask_irq,
.irq_set_type = gic_set_type,
#ifdef CONFIG_SMP
.irq_set_affinity = gic_set_affinity,
#endif
.ipi_send_single = gic_send_ipi,
};
static void gic_handle_local_int(bool chained)
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
{
unsigned long pending, masked;
unsigned int intr, virq;
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
pending = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_PEND));
masked = gic_read32(GIC_REG(VPE_LOCAL, GIC_VPE_MASK));
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
bitmap_and(&pending, &pending, &masked, GIC_NUM_LOCAL_INTRS);
for_each_set_bit(intr, &pending, GIC_NUM_LOCAL_INTRS) {
virq = irq_linear_revmap(gic_irq_domain,
GIC_LOCAL_TO_HWIRQ(intr));
if (chained)
generic_handle_irq(virq);
else
do_IRQ(virq);
}
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
}
static void gic_mask_local_irq(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_RMASK), 1 << intr);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
}
static void gic_unmask_local_irq(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), 1 << intr);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
}
static struct irq_chip gic_local_irq_controller = {
.name = "MIPS GIC Local",
.irq_mask = gic_mask_local_irq,
.irq_unmask = gic_unmask_local_irq,
};
static void gic_mask_local_irq_all_vpes(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
int i;
unsigned long flags;
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR),
mips_cm_vp_id(i));
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << intr);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
}
spin_unlock_irqrestore(&gic_lock, flags);
}
static void gic_unmask_local_irq_all_vpes(struct irq_data *d)
{
int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
int i;
unsigned long flags;
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR),
mips_cm_vp_id(i));
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SMASK), 1 << intr);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
}
spin_unlock_irqrestore(&gic_lock, flags);
}
static struct irq_chip gic_all_vpes_local_irq_controller = {
.name = "MIPS GIC Local",
.irq_mask = gic_mask_local_irq_all_vpes,
.irq_unmask = gic_unmask_local_irq_all_vpes,
};
static void __gic_irq_dispatch(void)
{
gic_handle_local_int(false);
gic_handle_shared_int(false);
}
static void gic_irq_dispatch(struct irq_desc *desc)
{
gic_handle_local_int(true);
gic_handle_shared_int(true);
}
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
static void __init gic_basic_init(void)
{
unsigned int i;
board_bind_eic_interrupt = &gic_bind_eic_interrupt;
/* Setup defaults */
for (i = 0; i < gic_shared_intrs; i++) {
gic_set_polarity(i, GIC_POL_POS);
gic_set_trigger(i, GIC_TRIG_LEVEL);
gic_reset_mask(i);
}
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
for (i = 0; i < gic_vpes; i++) {
unsigned int j;
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR),
mips_cm_vp_id(i));
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
for (j = 0; j < GIC_NUM_LOCAL_INTRS; j++) {
if (!gic_local_irq_is_routable(j))
continue;
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << j);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
}
}
}
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw)
{
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
int intr = GIC_HWIRQ_TO_LOCAL(hw);
int ret = 0;
int i;
unsigned long flags;
if (!gic_local_irq_is_routable(intr))
return -EPERM;
spin_lock_irqsave(&gic_lock, flags);
for (i = 0; i < gic_vpes; i++) {
u32 val = GIC_MAP_TO_PIN_MSK | gic_cpu_pin;
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR),
mips_cm_vp_id(i));
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
switch (intr) {
case GIC_LOCAL_INT_WD:
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_WD_MAP), val);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
break;
case GIC_LOCAL_INT_COMPARE:
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_MAP),
val);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
break;
case GIC_LOCAL_INT_TIMER:
2015-01-19 15:38:24 +00:00
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
val = GIC_MAP_TO_PIN_MSK | timer_cpu_pin;
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP),
val);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
break;
case GIC_LOCAL_INT_PERFCTR:
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP),
val);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
break;
case GIC_LOCAL_INT_SWINT0:
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SWINT0_MAP),
val);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
break;
case GIC_LOCAL_INT_SWINT1:
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SWINT1_MAP),
val);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
break;
case GIC_LOCAL_INT_FDC:
gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_FDC_MAP), val);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
break;
default:
pr_err("Invalid local IRQ %d\n", intr);
ret = -EINVAL;
break;
}
}
spin_unlock_irqrestore(&gic_lock, flags);
return ret;
}
static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq,
irq_hw_number_t hw, unsigned int vpe)
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
{
int intr = GIC_HWIRQ_TO_SHARED(hw);
unsigned long flags;
int i;
spin_lock_irqsave(&gic_lock, flags);
gic_map_to_pin(intr, gic_cpu_pin);
gic_map_to_vpe(intr, mips_cm_vp_id(vpe));
for (i = 0; i < min(gic_vpes, NR_CPUS); i++)
clear_bit(intr, pcpu_masks[i].pcpu_mask);
set_bit(intr, pcpu_masks[vpe].pcpu_mask);
spin_unlock_irqrestore(&gic_lock, flags);
return 0;
}
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
static int gic_setup_dev_chip(struct irq_domain *d, unsigned int virq,
unsigned int hwirq)
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
{
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
struct irq_chip *chip;
int err;
if (hwirq >= GIC_SHARED_HWIRQ_BASE) {
err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
&gic_level_irq_controller,
NULL);
} else {
switch (GIC_HWIRQ_TO_LOCAL(hwirq)) {
case GIC_LOCAL_INT_TIMER:
case GIC_LOCAL_INT_PERFCTR:
case GIC_LOCAL_INT_FDC:
/*
* HACK: These are all really percpu interrupts, but
* the rest of the MIPS kernel code does not use the
* percpu IRQ API for them.
*/
chip = &gic_all_vpes_local_irq_controller;
irq_set_handler(virq, handle_percpu_irq);
break;
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
default:
chip = &gic_local_irq_controller;
irq_set_handler(virq, handle_percpu_devid_irq);
irq_set_percpu_devid(virq);
break;
}
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
err = irq_domain_set_hwirq_and_chip(d, virq, hwirq,
chip, NULL);
}
return err;
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
}
static int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
struct gic_irq_spec *spec = arg;
irq_hw_number_t hwirq, base_hwirq;
int cpu, ret, i;
if (spec->type == GIC_DEVICE) {
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
/* verify that shared irqs don't conflict with an IPI irq */
if ((spec->hwirq >= GIC_SHARED_HWIRQ_BASE) &&
test_bit(GIC_HWIRQ_TO_SHARED(spec->hwirq), ipi_resrv))
return -EBUSY;
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
return gic_setup_dev_chip(d, virq, spec->hwirq);
} else {
base_hwirq = find_first_bit(ipi_resrv, gic_shared_intrs);
if (base_hwirq == gic_shared_intrs) {
return -ENOMEM;
}
/* check that we have enough space */
for (i = base_hwirq; i < nr_irqs; i++) {
if (!test_bit(i, ipi_resrv))
return -EBUSY;
}
bitmap_clear(ipi_resrv, base_hwirq, nr_irqs);
/* map the hwirq for each cpu consecutively */
i = 0;
for_each_cpu(cpu, spec->ipimask) {
hwirq = GIC_SHARED_TO_HWIRQ(base_hwirq + i);
ret = irq_domain_set_hwirq_and_chip(d, virq + i, hwirq,
&gic_level_irq_controller,
NULL);
if (ret)
goto error;
irq_set_handler(virq + i, handle_level_irq);
ret = gic_shared_irq_domain_map(d, virq + i, hwirq, cpu);
if (ret)
goto error;
i++;
}
/*
* tell the parent about the base hwirq we allocated so it can
* set its own domain data
*/
spec->hwirq = base_hwirq;
}
return 0;
error:
bitmap_set(ipi_resrv, base_hwirq, nr_irqs);
return ret;
}
void gic_irq_domain_free(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs)
{
irq_hw_number_t base_hwirq;
struct irq_data *data;
data = irq_get_irq_data(virq);
if (!data)
return;
base_hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(data));
bitmap_set(ipi_resrv, base_hwirq, nr_irqs);
}
int gic_irq_domain_match(struct irq_domain *d, struct device_node *node,
enum irq_domain_bus_token bus_token)
{
/* this domain should'nt be accessed directly */
return 0;
}
static const struct irq_domain_ops gic_irq_domain_ops = {
.alloc = gic_irq_domain_alloc,
.free = gic_irq_domain_free,
.match = gic_irq_domain_match,
};
static int gic_dev_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_type)
{
if (intsize != 3)
return -EINVAL;
if (intspec[0] == GIC_SHARED)
*out_hwirq = GIC_SHARED_TO_HWIRQ(intspec[1]);
else if (intspec[0] == GIC_LOCAL)
*out_hwirq = GIC_LOCAL_TO_HWIRQ(intspec[1]);
else
return -EINVAL;
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
static int gic_dev_domain_alloc(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
struct irq_fwspec *fwspec = arg;
struct gic_irq_spec spec = {
.type = GIC_DEVICE,
};
int i, ret;
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
if (fwspec->param[0] == GIC_SHARED)
spec.hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]);
else
spec.hwirq = GIC_LOCAL_TO_HWIRQ(fwspec->param[1]);
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec);
if (ret)
return ret;
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
for (i = 0; i < nr_irqs; i++) {
ret = gic_setup_dev_chip(d, virq + i, spec.hwirq + i);
if (ret)
goto error;
}
return 0;
error:
irq_domain_free_irqs_parent(d, virq, nr_irqs);
return ret;
}
void gic_dev_domain_free(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs)
{
/* no real allocation is done for dev irqs, so no need to free anything */
return;
}
static void gic_dev_domain_activate(struct irq_domain *domain,
struct irq_data *d)
{
irqchip/mips-gic: Fix local interrupts Since the device hierarchy domain was added by commit c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain"), GIC local interrupts have been broken. Users attempting to setup a per-cpu local IRQ, for example the GIC timer clock events code in drivers/clocksource/mips-gic-timer.c, the setup_percpu_irq function would refuse with -EINVAL because the GIC irqchip driver never called irq_set_percpu_devid so the IRQ_PER_CPU_DEVID flag was never set for the IRQ. This happens because irq_set_percpu_devid was being called from the gic_irq_domain_map function which is no longer called. Doing only that runs into further problems because gic_dev_domain_alloc set the struct irq_chip for all interrupts, local or shared, to gic_level_irq_controller despite that only being suitable for shared interrupts. The typical outcome of this is that gic_level_irq_controller callback functions are called for local interrupts, and then hwirq number calculations overflow & the driver ends up attempting to access some invalid register with an address calculated from an invalid hwirq number. Best case scenario is that this then leads to a bus error. This is fixed by abstracting the setup of the hwirq & chip to a new function gic_setup_dev_chip which is used by both the root GIC IRQ domain & the device domain. Finally, decoding local interrupts failed because gic_dev_domain_alloc only called irq_domain_alloc_irqs_parent for shared interrupts. Local ones were therefore never associated with hwirqs in the root GIC IRQ domain and the virq in gic_handle_local_int would always be 0. This is fixed by calling irq_domain_alloc_irqs_parent unconditionally & having gic_irq_domain_alloc handle both local & shared interrupts, which is easy due to the aforementioned abstraction of chip setup into gic_setup_dev_chip. This fixes use of the MIPS GIC timer for clock events, which has been broken since c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") but hadn't been noticed due to a silent fallback to the MIPS coprocessor 0 count/compare clock events device. Fixes: c98c1822ee13 ("irqchip/mips-gic: Add device hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: stable@vger.kernel.org Cc: Marc Zyngier <marc.zyngier@arm.com> Link: http://lkml.kernel.org/r/20160913165335.31389-1-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-09-13 16:53:35 +00:00
if (GIC_HWIRQ_TO_LOCAL(d->hwirq) < GIC_NUM_LOCAL_INTRS)
gic_local_irq_domain_map(domain, d->irq, d->hwirq);
else
gic_shared_irq_domain_map(domain, d->irq, d->hwirq, 0);
}
static struct irq_domain_ops gic_dev_domain_ops = {
.xlate = gic_dev_domain_xlate,
.alloc = gic_dev_domain_alloc,
.free = gic_dev_domain_free,
.activate = gic_dev_domain_activate,
};
static int gic_ipi_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
const u32 *intspec, unsigned int intsize,
irq_hw_number_t *out_hwirq,
unsigned int *out_type)
{
/*
* There's nothing to translate here. hwirq is dynamically allocated and
* the irq type is always edge triggered.
* */
*out_hwirq = 0;
*out_type = IRQ_TYPE_EDGE_RISING;
return 0;
}
static int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
struct cpumask *ipimask = arg;
struct gic_irq_spec spec = {
.type = GIC_IPI,
.ipimask = ipimask
};
int ret, i;
ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, &spec);
if (ret)
return ret;
/* the parent should have set spec.hwirq to the base_hwirq it allocated */
for (i = 0; i < nr_irqs; i++) {
ret = irq_domain_set_hwirq_and_chip(d, virq + i,
GIC_SHARED_TO_HWIRQ(spec.hwirq + i),
&gic_edge_irq_controller,
NULL);
if (ret)
goto error;
ret = irq_set_irq_type(virq + i, IRQ_TYPE_EDGE_RISING);
if (ret)
goto error;
}
return 0;
error:
irq_domain_free_irqs_parent(d, virq, nr_irqs);
return ret;
}
void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs)
{
irq_domain_free_irqs_parent(d, virq, nr_irqs);
}
int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node,
enum irq_domain_bus_token bus_token)
{
bool is_ipi;
switch (bus_token) {
case DOMAIN_BUS_IPI:
is_ipi = d->bus_token == bus_token;
irqchip/mips-gic: Match IPI IRQ domain by bus token only Commit fbde2d7d8290 ("MIPS: Add generic SMP IPI support") introduced code which calls irq_find_matching_host with a NULL node parameter in order to discover IPI IRQ domains which are not associated with the DT root node's interrupt parent. This suggests that implementations of IPI IRQ domains should effectively ignore the node parameter if it is NULL and search purely based upon the bus token. Commit 2af70a962070 ("irqchip/mips-gic: Add a IPI hierarchy domain") did not do this when implementing the GIC IPI IRQ domain, and on MIPS Boston boards this leads to no IPI domain being discovered and a NULL pointer dereference when attempting to send an IPI: CPU 0 Unable to handle kernel paging request at virtual address 0000000000000040, epc == ffffffff8016e70c, ra == ffffffff8010ff5c Oops[#1]: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.7.0-rc6-00223-gad0d1b6 #945 task: a8000000ff066fc0 ti: a8000000ff068000 task.ti: a8000000ff068000 $ 0 : 0000000000000000 0000000000000001 ffffffff80730000 0000000000000003 $ 4 : 0000000000000000 ffffffff8057e5b0 a800000001e3ee00 0000000000000000 $ 8 : 0000000000000000 0000000000000023 0000000000000001 0000000000000001 $12 : 0000000000000000 ffffffff803323d0 0000000000000000 0000000000000000 $16 : 0000000000000000 0000000000000000 0000000000000001 ffffffff801108fc $20 : 0000000000000000 ffffffff8057e5b0 0000000000000001 0000000000000000 $24 : 0000000000000000 ffffffff8012de28 $28 : a8000000ff068000 a8000000ff06fbc0 0000000000000000 ffffffff8010ff5c Hi : ffffffff8014c174 Lo : a800000001e1e140 epc : ffffffff8016e70c __ipi_send_mask+0x24/0x11c ra : ffffffff8010ff5c mips_smp_send_ipi_mask+0x68/0x178 Status: 140084e2 KX SX UX KERNEL EXL Cause : 00800008 (ExcCode 02) BadVA : 0000000000000040 PrId : 0001a920 (MIPS I6400) Process swapper/0 (pid: 1, threadinfo=a8000000ff068000, task=a8000000ff066fc0, tls=0000000000000000) Stack : 0000000000000000 0000000000000000 0000000000000001 ffffffff801108fc 0000000000000000 ffffffff8057e5b0 0000000000000001 ffffffff8010ff5c 0000000000000001 0000000000000020 0000000000000000 0000000000000000 0000000000000000 ffffffff801108fc 0000000000000000 0000000000000001 0000000000000001 0000000000000000 0000000000000000 ffffffff801865e8 a8000000ff0c7500 a8000000ff06fc90 0000000000000001 0000000000000002 ffffffff801108fc ffffffff801868b8 0000000000000000 ffffffff801108fc 0000000000000000 0000000000000003 ffffffff8068c700 0000000000000001 ffffffff80730000 0000000000000001 a8000000ff00a290 ffffffff80110c50 0000000000000003 a800000001e48308 0000000000000003 0000000000000008 ... Call Trace: [<ffffffff8016e70c>] __ipi_send_mask+0x24/0x11c [<ffffffff8010ff5c>] mips_smp_send_ipi_mask+0x68/0x178 [<ffffffff801865e8>] generic_exec_single+0x150/0x170 [<ffffffff801868b8>] smp_call_function_single+0x108/0x160 [<ffffffff80110c50>] cps_boot_secondary+0x328/0x394 [<ffffffff80110534>] __cpu_up+0x38/0x90 [<ffffffff8012de4c>] bringup_cpu+0x24/0xac [<ffffffff8012df40>] cpuhp_up_callbacks+0x58/0xdc [<ffffffff8012e648>] cpu_up+0x118/0x18c [<ffffffff806dc158>] smp_init+0xbc/0xe8 [<ffffffff806d4c18>] kernel_init_freeable+0xa0/0x228 [<ffffffff8056c908>] kernel_init+0x10/0xf0 [<ffffffff80105098>] ret_from_kernel_thread+0x14/0x1c Fix this by allowing the GIC IPI IRQ domain to match purely based upon the bus token if the node provided is NULL. Fixes: 2af70a962070 ("irqchip/mips-gic: Add a IPI hierarchy domain") Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Jason Cooper <jason@lakedaemon.net> Cc: Qais Yousef <qsyousef@gmail.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20160705132600.27730-2-paul.burton@imgtec.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-07-05 13:26:00 +00:00
return (!node || to_of_node(d->fwnode) == node) && is_ipi;
break;
default:
return 0;
}
}
static struct irq_domain_ops gic_ipi_domain_ops = {
.xlate = gic_ipi_domain_xlate,
.alloc = gic_ipi_domain_alloc,
.free = gic_ipi_domain_free,
.match = gic_ipi_domain_match,
};
static void __init gic_map_single_int(struct device_node *node,
unsigned int irq)
{
unsigned int linux_irq;
struct irq_fwspec local_int_fwspec = {
.fwnode = &node->fwnode,
.param_count = 3,
.param = {
[0] = GIC_LOCAL,
[1] = irq,
[2] = IRQ_TYPE_NONE,
},
};
if (!gic_local_irq_is_routable(irq))
return;
linux_irq = irq_create_fwspec_mapping(&local_int_fwspec);
WARN_ON(!linux_irq);
}
static void __init gic_map_interrupts(struct device_node *node)
{
gic_map_single_int(node, GIC_LOCAL_INT_TIMER);
gic_map_single_int(node, GIC_LOCAL_INT_PERFCTR);
gic_map_single_int(node, GIC_LOCAL_INT_FDC);
}
static void __init __gic_init(unsigned long gic_base_addr,
unsigned long gic_addrspace_size,
unsigned int cpu_vec, unsigned int irqbase,
struct device_node *node)
{
unsigned int gicconfig, cpu;
unsigned int v[2];
__gic_base_addr = gic_base_addr;
gic_base = ioremap_nocache(gic_base_addr, gic_addrspace_size);
gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
gic_shared_intrs = (gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK) >>
GIC_SH_CONFIG_NUMINTRS_SHF;
gic_shared_intrs = ((gic_shared_intrs + 1) * 8);
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
gic_vpes = (gicconfig & GIC_SH_CONFIG_NUMVPES_MSK) >>
GIC_SH_CONFIG_NUMVPES_SHF;
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
gic_vpes = gic_vpes + 1;
if (cpu_has_veic) {
/* Set EIC mode for all VPEs */
for_each_present_cpu(cpu) {
gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR),
mips_cm_vp_id(cpu));
gic_write(GIC_REG(VPE_OTHER, GIC_VPE_CTL),
GIC_VPE_CTL_EIC_MODE_MSK);
}
/* Always use vector 1 in EIC mode */
gic_cpu_pin = 0;
2015-01-19 15:38:24 +00:00
timer_cpu_pin = gic_cpu_pin;
set_vi_handler(gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET,
__gic_irq_dispatch);
} else {
gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET;
irq_set_chained_handler(MIPS_CPU_IRQ_BASE + cpu_vec,
gic_irq_dispatch);
2015-01-19 15:38:24 +00:00
/*
* With the CMP implementation of SMP (deprecated), other CPUs
* are started by the bootloader and put into a timer based
* waiting poll loop. We must not re-route those CPU's local
* timer interrupts as the wait instruction will never finish,
* so just handle whatever CPU interrupt it is routed to by
* default.
*
* This workaround should be removed when CMP support is
* dropped.
*/
if (IS_ENABLED(CONFIG_MIPS_CMP) &&
gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER)) {
timer_cpu_pin = gic_read32(GIC_REG(VPE_LOCAL,
2015-01-19 15:38:24 +00:00
GIC_VPE_TIMER_MAP)) &
GIC_MAP_MSK;
irq_set_chained_handler(MIPS_CPU_IRQ_BASE +
GIC_CPU_PIN_OFFSET +
timer_cpu_pin,
gic_irq_dispatch);
} else {
timer_cpu_pin = gic_cpu_pin;
}
}
gic_irq_domain = irq_domain_add_simple(node, GIC_NUM_LOCAL_INTRS +
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
gic_shared_intrs, irqbase,
&gic_irq_domain_ops, NULL);
if (!gic_irq_domain)
panic("Failed to add GIC IRQ domain");
gic_irq_domain->name = "mips-gic-irq";
gic_dev_domain = irq_domain_add_hierarchy(gic_irq_domain, 0,
GIC_NUM_LOCAL_INTRS + gic_shared_intrs,
node, &gic_dev_domain_ops, NULL);
if (!gic_dev_domain)
panic("Failed to add GIC DEV domain");
gic_dev_domain->name = "mips-gic-dev";
gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain,
IRQ_DOMAIN_FLAG_IPI_PER_CPU,
GIC_NUM_LOCAL_INTRS + gic_shared_intrs,
node, &gic_ipi_domain_ops, NULL);
if (!gic_ipi_domain)
panic("Failed to add GIC IPI domain");
gic_ipi_domain->name = "mips-gic-ipi";
gic_ipi_domain->bus_token = DOMAIN_BUS_IPI;
if (node &&
!of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) {
bitmap_set(ipi_resrv, v[0], v[1]);
} else {
/* Make the last 2 * gic_vpes available for IPIs */
bitmap_set(ipi_resrv,
gic_shared_intrs - 2 * gic_vpes,
2 * gic_vpes);
}
irqchip: mips-gic: Support local interrupts The MIPS GIC supports 7 local interrupts, 2 of which are the GIC local watchdog and count/compare timer. The remainder are CPU interrupts which may optionally be re-routed through the GIC. GIC hardware IRQs 0-6 are now used for local interrupts while hardware IRQs 7+ are used for external (shared) interrupts. Note that the 5 CPU interrupts may not be re-routable through the GIC. In that case mapping will fail and the vectors reported in C0_IntCtl should be used instead. gic_get_c0_compare_int() and gic_get_c0_perfcount_int() will return the correct IRQ number to use for the C0 timer and perfcounter interrupts based on the routability of those interrupts through the GIC. A separate irq_chip, with callbacks that mask/unmask the local interrupt on all CPUs, is used for the C0 timer and performance counter interrupts since all other platforms do not use the percpu IRQ API for those interrupts. Malta, SEAD-3, and the GIC clockevent driver have been updated to use local interrupts and the R4K clockevent driver has been updated to poll for C0 timer interrupts through the GIC when the GIC is present. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Acked-by: Jason Cooper <jason@lakedaemon.net> Reviewed-by: Qais Yousef <qais.yousef@imgtec.com> Tested-by: Qais Yousef <qais.yousef@imgtec.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jeffrey Deans <jeffrey.deans@imgtec.com> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jonas Gorski <jogo@openwrt.org> Cc: John Crispin <blogic@openwrt.org> Cc: David Daney <ddaney.cavm@gmail.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7819/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-09-18 21:47:27 +00:00
gic_basic_init();
gic_map_interrupts(node);
}
void __init gic_init(unsigned long gic_base_addr,
unsigned long gic_addrspace_size,
unsigned int cpu_vec, unsigned int irqbase)
{
__gic_init(gic_base_addr, gic_addrspace_size, cpu_vec, irqbase, NULL);
}
static int __init gic_of_init(struct device_node *node,
struct device_node *parent)
{
struct resource res;
unsigned int cpu_vec, i = 0, reserved = 0;
phys_addr_t gic_base;
size_t gic_len;
/* Find the first available CPU vector. */
while (!of_property_read_u32_index(node, "mti,reserved-cpu-vectors",
i++, &cpu_vec))
reserved |= BIT(cpu_vec);
for (cpu_vec = 2; cpu_vec < 8; cpu_vec++) {
if (!(reserved & BIT(cpu_vec)))
break;
}
if (cpu_vec == 8) {
pr_err("No CPU vectors available for GIC\n");
return -ENODEV;
}
if (of_address_to_resource(node, 0, &res)) {
/*
* Probe the CM for the GIC base address if not specified
* in the device-tree.
*/
if (mips_cm_present()) {
gic_base = read_gcr_gic_base() &
~CM_GCR_GIC_BASE_GICEN_MSK;
gic_len = 0x20000;
} else {
pr_err("Failed to get GIC memory range\n");
return -ENODEV;
}
} else {
gic_base = res.start;
gic_len = resource_size(&res);
}
if (mips_cm_present())
write_gcr_gic_base(gic_base | CM_GCR_GIC_BASE_GICEN_MSK);
gic_present = true;
__gic_init(gic_base, gic_len, cpu_vec, 0, node);
return 0;
}
IRQCHIP_DECLARE(mips_gic, "mti,gic", gic_of_init);