From: Andrew Bresticker Date: Thu, 18 Sep 2014 21:47:19 +0000 (-0700) Subject: MIPS: Move GIC to drivers/irqchip/ X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=8a19b8f19429b86c91e10745bc131bc600e60ede;p=deliverable%2Flinux.git MIPS: Move GIC to drivers/irqchip/ Move GIC irqchip support to drivers/irqchip/ and rename the Kconfig option from IRQ_GIC to MIPS_GIC to avoid confusion with the ARM GIC. Signed-off-by: Andrew Bresticker Acked-by: Jason Cooper Reviewed-by: Qais Yousef Tested-by: Qais Yousef Cc: Thomas Gleixner Cc: Jeffrey Deans Cc: Markos Chandras Cc: Paul Burton Cc: Jonas Gorski Cc: John Crispin Cc: David Daney Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7812/ Signed-off-by: Ralf Baechle --- diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index e08aeec7d916..e0b7c2006900 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -320,7 +320,7 @@ config MIPS_MALTA select GENERIC_ISA_DMA select HAVE_PCSPKR_PLATFORM select IRQ_CPU - select IRQ_GIC + select MIPS_GIC select HW_HAS_PCI select I8253 select I8259 @@ -362,7 +362,7 @@ config MIPS_SEAD3 select CPU_MIPSR2_IRQ_EI select DMA_NONCOHERENT select IRQ_CPU - select IRQ_GIC + select MIPS_GIC select LIBFDT select MIPS_MSC select SYS_HAS_CPU_MIPS32_R1 @@ -1073,10 +1073,6 @@ config IRQ_TXX9 config IRQ_GT641XX bool -config IRQ_GIC - select MIPS_CM - bool - config PCI_GT64XXX_PCI0 bool @@ -1890,7 +1886,7 @@ config FORCE_MAX_ZONEORDER config CEVT_GIC bool "Use GIC global counter for clock events" - depends on IRQ_GIC && !MIPS_SEAD3 + depends on MIPS_GIC && !MIPS_SEAD3 help Use the GIC global counter for the clock events. The R4K clock event driver is always present, so if the platform ends up not diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 008a2fed0584..3982e5138f61 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -68,7 +68,6 @@ obj-$(CONFIG_IRQ_CPU_RM7K) += irq-rm7000.o obj-$(CONFIG_MIPS_MSC) += irq-msc01.o obj-$(CONFIG_IRQ_TXX9) += irq_txx9.o obj-$(CONFIG_IRQ_GT641XX) += irq-gt641xx.o -obj-$(CONFIG_IRQ_GIC) += irq-gic.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_32BIT) += scall32-o32.o diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c index bc127e22fdab..5b8f8e32b47d 100644 --- a/arch/mips/kernel/cevt-r4k.c +++ b/arch/mips/kernel/cevt-r4k.c @@ -85,7 +85,7 @@ void mips_event_handler(struct clock_event_device *dev) */ static int c0_compare_int_pending(void) { -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC if (cpu_has_veic) return gic_get_timer_pending(); #endif diff --git a/arch/mips/kernel/irq-gic.c b/arch/mips/kernel/irq-gic.c deleted file mode 100644 index 582883069ef6..000000000000 --- a/arch/mips/kernel/irq-gic.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -unsigned int gic_frequency; -unsigned int gic_present; -unsigned long _gic_base; -unsigned int gic_irq_base; -unsigned int gic_irq_flags[GIC_NUM_INTRS]; - -/* The index into this array is the vector # of the interrupt. */ -struct gic_shared_intr_map gic_shared_intr_map[GIC_NUM_INTRS]; - -struct gic_pcpu_mask { - DECLARE_BITMAP(pcpu_mask, GIC_NUM_INTRS); -}; - -struct gic_pending_regs { - DECLARE_BITMAP(pending, GIC_NUM_INTRS); -}; - -struct gic_intrmask_regs { - DECLARE_BITMAP(intrmask, GIC_NUM_INTRS); -}; - -static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; -static struct gic_pending_regs pending_regs[NR_CPUS]; -static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; - -#if defined(CONFIG_CSRC_GIC) || defined(CONFIG_CEVT_GIC) -cycle_t gic_read_count(void) -{ - unsigned int hi, hi2, lo; - - do { - GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi); - GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), lo); - GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi2); - } while (hi2 != hi); - - return (((cycle_t) hi) << 32) + lo; -} - -void gic_write_compare(cycle_t cnt) -{ - GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), - (int)(cnt >> 32)); - GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), - (int)(cnt & 0xffffffff)); -} - -void gic_write_cpu_compare(cycle_t cnt, int cpu) -{ - unsigned long flags; - - local_irq_save(flags); - - GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), cpu); - GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_HI), - (int)(cnt >> 32)); - GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_LO), - (int)(cnt & 0xffffffff)); - - local_irq_restore(flags); -} - -cycle_t gic_read_compare(void) -{ - unsigned int hi, lo; - - GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), hi); - GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), lo); - - return (((cycle_t) hi) << 32) + lo; -} -#endif - -unsigned int gic_get_timer_pending(void) -{ - unsigned int vpe_pending; - - GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), 0); - GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_PEND), vpe_pending); - return vpe_pending & GIC_VPE_PEND_TIMER_MSK; -} - -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 */ - GICWRITE(GIC_REG_ADDR(VPE_LOCAL, GIC_VPE_EIC_SS(irq)), set); -} - -void gic_send_ipi(unsigned int intr) -{ - GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), 0x80000000 | intr); -} - -static void gic_eic_irq_dispatch(void) -{ - unsigned int cause = read_c0_cause(); - int irq; - - irq = (cause & ST0_IM) >> STATUSB_IP2; - if (irq == 0) - irq = -1; - - if (irq >= 0) - do_IRQ(gic_irq_base + irq); - else - spurious_interrupt(); -} - -static void __init vpe_local_setup(unsigned int numvpes) -{ - unsigned long timer_intr = GIC_INT_TMR; - unsigned long perf_intr = GIC_INT_PERFCTR; - unsigned int vpe_ctl; - int i; - - if (cpu_has_veic) { - /* - * GIC timer interrupt -> CPU HW Int X (vector X+2) -> - * map to pin X+2-1 (since GIC adds 1) - */ - timer_intr += (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); - /* - * GIC perfcnt interrupt -> CPU HW Int X (vector X+2) -> - * map to pin X+2-1 (since GIC adds 1) - */ - perf_intr += (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); - } - - /* - * Setup the default performance counter timer interrupts - * for all VPEs - */ - for (i = 0; i < numvpes; i++) { - GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); - - /* Are Interrupts locally routable? */ - GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_CTL), vpe_ctl); - if (vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK) - GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), - GIC_MAP_TO_PIN_MSK | timer_intr); - if (cpu_has_veic) { - set_vi_handler(timer_intr + GIC_PIN_TO_VEC_OFFSET, - gic_eic_irq_dispatch); - gic_shared_intr_map[timer_intr + GIC_PIN_TO_VEC_OFFSET].local_intr_mask |= GIC_VPE_RMASK_TIMER_MSK; - } - - if (vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK) - GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP), - GIC_MAP_TO_PIN_MSK | perf_intr); - if (cpu_has_veic) { - set_vi_handler(perf_intr + GIC_PIN_TO_VEC_OFFSET, gic_eic_irq_dispatch); - gic_shared_intr_map[perf_intr + GIC_PIN_TO_VEC_OFFSET].local_intr_mask |= GIC_VPE_RMASK_PERFCNT_MSK; - } - } -} - -unsigned int gic_compare_int(void) -{ - unsigned int pending; - - GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), pending); - if (pending & GIC_VPE_PEND_CMP_MSK) - return 1; - else - return 0; -} - -void gic_get_int_mask(unsigned long *dst, const unsigned long *src) -{ - unsigned int i; - unsigned long *pending, *intrmask, *pcpu_mask; - unsigned long *pending_abs, *intrmask_abs; - - /* Get per-cpu bitmaps */ - pending = pending_regs[smp_processor_id()].pending; - intrmask = intrmask_regs[smp_processor_id()].intrmask; - pcpu_mask = pcpu_masks[smp_processor_id()].pcpu_mask; - - pending_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, - GIC_SH_PEND_31_0_OFS); - intrmask_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, - GIC_SH_MASK_31_0_OFS); - - for (i = 0; i < BITS_TO_LONGS(GIC_NUM_INTRS); i++) { - GICREAD(*pending_abs, pending[i]); - GICREAD(*intrmask_abs, intrmask[i]); - pending_abs++; - intrmask_abs++; - } - - bitmap_and(pending, pending, intrmask, GIC_NUM_INTRS); - bitmap_and(pending, pending, pcpu_mask, GIC_NUM_INTRS); - bitmap_and(dst, src, pending, GIC_NUM_INTRS); -} - -unsigned int gic_get_int(void) -{ - DECLARE_BITMAP(interrupts, GIC_NUM_INTRS); - - bitmap_fill(interrupts, GIC_NUM_INTRS); - gic_get_int_mask(interrupts, interrupts); - - return find_first_bit(interrupts, GIC_NUM_INTRS); -} - -static void gic_mask_irq(struct irq_data *d) -{ - GIC_CLR_INTR_MASK(d->irq - gic_irq_base); -} - -static void gic_unmask_irq(struct irq_data *d) -{ - GIC_SET_INTR_MASK(d->irq - gic_irq_base); -} - -#ifdef CONFIG_SMP -static DEFINE_SPINLOCK(gic_lock); - -static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, - bool force) -{ - unsigned int irq = (d->irq - gic_irq_base); - cpumask_t tmp = CPU_MASK_NONE; - unsigned long flags; - int i; - - cpumask_and(&tmp, cpumask, cpu_online_mask); - if (cpus_empty(tmp)) - return -1; - - /* Assumption : cpumask refers to a single CPU */ - spin_lock_irqsave(&gic_lock, flags); - - /* Re-route this IRQ */ - GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp)); - - /* Update the pcpu_masks */ - for (i = 0; i < NR_CPUS; i++) - clear_bit(irq, pcpu_masks[i].pcpu_mask); - set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask); - - cpumask_copy(d->affinity, cpumask); - spin_unlock_irqrestore(&gic_lock, flags); - - return IRQ_SET_MASK_OK_NOCOPY; -} -#endif - -static struct irq_chip gic_irq_controller = { - .name = "MIPS GIC", - .irq_ack = gic_irq_ack, - .irq_mask = gic_mask_irq, - .irq_mask_ack = gic_mask_irq, - .irq_unmask = gic_unmask_irq, - .irq_eoi = gic_finish_irq, -#ifdef CONFIG_SMP - .irq_set_affinity = gic_set_affinity, -#endif -}; - -static void __init gic_setup_intr(unsigned int intr, unsigned int cpu, - unsigned int pin, unsigned int polarity, unsigned int trigtype, - unsigned int flags) -{ - struct gic_shared_intr_map *map_ptr; - - /* Setup Intr to Pin mapping */ - if (pin & GIC_MAP_TO_NMI_MSK) { - int i; - - GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), pin); - /* FIXME: hack to route NMI to all cpu's */ - for (i = 0; i < NR_CPUS; i += 32) { - GICWRITE(GIC_REG_ADDR(SHARED, - GIC_SH_MAP_TO_VPE_REG_OFF(intr, i)), - 0xffffffff); - } - } else { - GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), - GIC_MAP_TO_PIN_MSK | pin); - /* Setup Intr to CPU mapping */ - GIC_SH_MAP_TO_VPE_SMASK(intr, cpu); - if (cpu_has_veic) { - set_vi_handler(pin + GIC_PIN_TO_VEC_OFFSET, - gic_eic_irq_dispatch); - map_ptr = &gic_shared_intr_map[pin + GIC_PIN_TO_VEC_OFFSET]; - if (map_ptr->num_shared_intr >= GIC_MAX_SHARED_INTR) - BUG(); - map_ptr->intr_list[map_ptr->num_shared_intr++] = intr; - } - } - - /* Setup Intr Polarity */ - GIC_SET_POLARITY(intr, polarity); - - /* Setup Intr Trigger Type */ - GIC_SET_TRIGGER(intr, trigtype); - - /* Init Intr Masks */ - GIC_CLR_INTR_MASK(intr); - - /* Initialise per-cpu Interrupt software masks */ - set_bit(intr, pcpu_masks[cpu].pcpu_mask); - - if ((flags & GIC_FLAG_TRANSPARENT) && (cpu_has_veic == 0)) - GIC_SET_INTR_MASK(intr); - if (trigtype == GIC_TRIG_EDGE) - gic_irq_flags[intr] |= GIC_TRIG_EDGE; -} - -static void __init gic_basic_init(int numintrs, int numvpes, - struct gic_intr_map *intrmap, int mapsize) -{ - unsigned int i, cpu; - unsigned int pin_offset = 0; - - board_bind_eic_interrupt = &gic_bind_eic_interrupt; - - /* Setup defaults */ - for (i = 0; i < numintrs; i++) { - GIC_SET_POLARITY(i, GIC_POL_POS); - GIC_SET_TRIGGER(i, GIC_TRIG_LEVEL); - GIC_CLR_INTR_MASK(i); - if (i < GIC_NUM_INTRS) { - gic_irq_flags[i] = 0; - gic_shared_intr_map[i].num_shared_intr = 0; - gic_shared_intr_map[i].local_intr_mask = 0; - } - } - - /* - * In EIC mode, the HW_INT# is offset by (2-1). Need to subtract - * one because the GIC will add one (since 0=no intr). - */ - if (cpu_has_veic) - pin_offset = (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); - - /* Setup specifics */ - for (i = 0; i < mapsize; i++) { - cpu = intrmap[i].cpunum; - if (cpu == GIC_UNUSED) - continue; - gic_setup_intr(i, - intrmap[i].cpunum, - intrmap[i].pin + pin_offset, - intrmap[i].polarity, - intrmap[i].trigtype, - intrmap[i].flags); - } - - vpe_local_setup(numvpes); -} - -void __init gic_init(unsigned long gic_base_addr, - unsigned long gic_addrspace_size, - struct gic_intr_map *intr_map, unsigned int intr_map_size, - unsigned int irqbase) -{ - unsigned int gicconfig; - int numvpes, numintrs; - - _gic_base = (unsigned long) ioremap_nocache(gic_base_addr, - gic_addrspace_size); - gic_irq_base = irqbase; - - GICREAD(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); - numintrs = (gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK) >> - GIC_SH_CONFIG_NUMINTRS_SHF; - numintrs = ((numintrs + 1) * 8); - - numvpes = (gicconfig & GIC_SH_CONFIG_NUMVPES_MSK) >> - GIC_SH_CONFIG_NUMVPES_SHF; - numvpes = numvpes + 1; - - gic_basic_init(numintrs, numvpes, intr_map, intr_map_size); - - gic_platform_init(numintrs, &gic_irq_controller); -} diff --git a/arch/mips/kernel/smp-mt.c b/arch/mips/kernel/smp-mt.c index 21f23add04f4..d60475fe5957 100644 --- a/arch/mips/kernel/smp-mt.c +++ b/arch/mips/kernel/smp-mt.c @@ -119,7 +119,7 @@ static void vsmp_send_ipi_single(int cpu, unsigned int action) unsigned long flags; int vpflags; -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC if (gic_present) { gic_send_ipi_single(cpu, action); return; @@ -158,7 +158,7 @@ static void vsmp_send_ipi_mask(const struct cpumask *mask, unsigned int action) static void vsmp_init_secondary(void) { -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC /* This is Malta specific: IPI,performance and timer interrupts */ if (gic_present) change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP4 | diff --git a/arch/mips/mti-malta/malta-time.c b/arch/mips/mti-malta/malta-time.c index a4e035c8acf6..17cfc8a379a6 100644 --- a/arch/mips/mti-malta/malta-time.c +++ b/arch/mips/mti-malta/malta-time.c @@ -70,7 +70,7 @@ static void __init estimate_frequencies(void) { unsigned long flags; unsigned int count, start; -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC unsigned int giccount = 0, gicstart = 0; #endif @@ -87,7 +87,7 @@ static void __init estimate_frequencies(void) /* Initialize counters. */ start = read_c0_count(); -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC if (gic_present) GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), gicstart); #endif @@ -97,7 +97,7 @@ static void __init estimate_frequencies(void) while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); count = read_c0_count(); -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC if (gic_present) GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), giccount); #endif @@ -107,7 +107,7 @@ static void __init estimate_frequencies(void) count -= start; mips_hpt_frequency = count; -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC if (gic_present) { giccount -= gicstart; gic_frequency = giccount; @@ -189,7 +189,7 @@ void __init plat_time_init(void) setup_pit_timer(); #endif -#ifdef CONFIG_IRQ_GIC +#ifdef CONFIG_MIPS_GIC if (gic_present) { freq = freqround(gic_frequency, 5000); printk("GIC frequency %d.%02d MHz\n", freq/1000000, diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index b21f12f1766d..f2dde146bbdb 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -125,3 +125,7 @@ config KEYSTONE_IRQ help Support for Texas Instruments Keystone 2 IRQ controller IP which is part of the Keystone 2 IPC mechanism + +config MIPS_GIC + bool + select MIPS_CM diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 173bb5fa2cc9..021833079c91 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o \ irq-bcm7120-l2.o obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o +obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c new file mode 100644 index 000000000000..582883069ef6 --- /dev/null +++ b/drivers/irqchip/irq-mips-gic.c @@ -0,0 +1,402 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +unsigned int gic_frequency; +unsigned int gic_present; +unsigned long _gic_base; +unsigned int gic_irq_base; +unsigned int gic_irq_flags[GIC_NUM_INTRS]; + +/* The index into this array is the vector # of the interrupt. */ +struct gic_shared_intr_map gic_shared_intr_map[GIC_NUM_INTRS]; + +struct gic_pcpu_mask { + DECLARE_BITMAP(pcpu_mask, GIC_NUM_INTRS); +}; + +struct gic_pending_regs { + DECLARE_BITMAP(pending, GIC_NUM_INTRS); +}; + +struct gic_intrmask_regs { + DECLARE_BITMAP(intrmask, GIC_NUM_INTRS); +}; + +static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; +static struct gic_pending_regs pending_regs[NR_CPUS]; +static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; + +#if defined(CONFIG_CSRC_GIC) || defined(CONFIG_CEVT_GIC) +cycle_t gic_read_count(void) +{ + unsigned int hi, hi2, lo; + + do { + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi); + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), lo); + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi2); + } while (hi2 != hi); + + return (((cycle_t) hi) << 32) + lo; +} + +void gic_write_compare(cycle_t cnt) +{ + GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), + (int)(cnt >> 32)); + GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), + (int)(cnt & 0xffffffff)); +} + +void gic_write_cpu_compare(cycle_t cnt, int cpu) +{ + unsigned long flags; + + local_irq_save(flags); + + GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), cpu); + GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_HI), + (int)(cnt >> 32)); + GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_LO), + (int)(cnt & 0xffffffff)); + + local_irq_restore(flags); +} + +cycle_t gic_read_compare(void) +{ + unsigned int hi, lo; + + GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), hi); + GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), lo); + + return (((cycle_t) hi) << 32) + lo; +} +#endif + +unsigned int gic_get_timer_pending(void) +{ + unsigned int vpe_pending; + + GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), 0); + GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_PEND), vpe_pending); + return vpe_pending & GIC_VPE_PEND_TIMER_MSK; +} + +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 */ + GICWRITE(GIC_REG_ADDR(VPE_LOCAL, GIC_VPE_EIC_SS(irq)), set); +} + +void gic_send_ipi(unsigned int intr) +{ + GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), 0x80000000 | intr); +} + +static void gic_eic_irq_dispatch(void) +{ + unsigned int cause = read_c0_cause(); + int irq; + + irq = (cause & ST0_IM) >> STATUSB_IP2; + if (irq == 0) + irq = -1; + + if (irq >= 0) + do_IRQ(gic_irq_base + irq); + else + spurious_interrupt(); +} + +static void __init vpe_local_setup(unsigned int numvpes) +{ + unsigned long timer_intr = GIC_INT_TMR; + unsigned long perf_intr = GIC_INT_PERFCTR; + unsigned int vpe_ctl; + int i; + + if (cpu_has_veic) { + /* + * GIC timer interrupt -> CPU HW Int X (vector X+2) -> + * map to pin X+2-1 (since GIC adds 1) + */ + timer_intr += (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); + /* + * GIC perfcnt interrupt -> CPU HW Int X (vector X+2) -> + * map to pin X+2-1 (since GIC adds 1) + */ + perf_intr += (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); + } + + /* + * Setup the default performance counter timer interrupts + * for all VPEs + */ + for (i = 0; i < numvpes; i++) { + GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); + + /* Are Interrupts locally routable? */ + GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_CTL), vpe_ctl); + if (vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK) + GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), + GIC_MAP_TO_PIN_MSK | timer_intr); + if (cpu_has_veic) { + set_vi_handler(timer_intr + GIC_PIN_TO_VEC_OFFSET, + gic_eic_irq_dispatch); + gic_shared_intr_map[timer_intr + GIC_PIN_TO_VEC_OFFSET].local_intr_mask |= GIC_VPE_RMASK_TIMER_MSK; + } + + if (vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK) + GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP), + GIC_MAP_TO_PIN_MSK | perf_intr); + if (cpu_has_veic) { + set_vi_handler(perf_intr + GIC_PIN_TO_VEC_OFFSET, gic_eic_irq_dispatch); + gic_shared_intr_map[perf_intr + GIC_PIN_TO_VEC_OFFSET].local_intr_mask |= GIC_VPE_RMASK_PERFCNT_MSK; + } + } +} + +unsigned int gic_compare_int(void) +{ + unsigned int pending; + + GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), pending); + if (pending & GIC_VPE_PEND_CMP_MSK) + return 1; + else + return 0; +} + +void gic_get_int_mask(unsigned long *dst, const unsigned long *src) +{ + unsigned int i; + unsigned long *pending, *intrmask, *pcpu_mask; + unsigned long *pending_abs, *intrmask_abs; + + /* Get per-cpu bitmaps */ + pending = pending_regs[smp_processor_id()].pending; + intrmask = intrmask_regs[smp_processor_id()].intrmask; + pcpu_mask = pcpu_masks[smp_processor_id()].pcpu_mask; + + pending_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, + GIC_SH_PEND_31_0_OFS); + intrmask_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, + GIC_SH_MASK_31_0_OFS); + + for (i = 0; i < BITS_TO_LONGS(GIC_NUM_INTRS); i++) { + GICREAD(*pending_abs, pending[i]); + GICREAD(*intrmask_abs, intrmask[i]); + pending_abs++; + intrmask_abs++; + } + + bitmap_and(pending, pending, intrmask, GIC_NUM_INTRS); + bitmap_and(pending, pending, pcpu_mask, GIC_NUM_INTRS); + bitmap_and(dst, src, pending, GIC_NUM_INTRS); +} + +unsigned int gic_get_int(void) +{ + DECLARE_BITMAP(interrupts, GIC_NUM_INTRS); + + bitmap_fill(interrupts, GIC_NUM_INTRS); + gic_get_int_mask(interrupts, interrupts); + + return find_first_bit(interrupts, GIC_NUM_INTRS); +} + +static void gic_mask_irq(struct irq_data *d) +{ + GIC_CLR_INTR_MASK(d->irq - gic_irq_base); +} + +static void gic_unmask_irq(struct irq_data *d) +{ + GIC_SET_INTR_MASK(d->irq - gic_irq_base); +} + +#ifdef CONFIG_SMP +static DEFINE_SPINLOCK(gic_lock); + +static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, + bool force) +{ + unsigned int irq = (d->irq - gic_irq_base); + cpumask_t tmp = CPU_MASK_NONE; + unsigned long flags; + int i; + + cpumask_and(&tmp, cpumask, cpu_online_mask); + if (cpus_empty(tmp)) + return -1; + + /* Assumption : cpumask refers to a single CPU */ + spin_lock_irqsave(&gic_lock, flags); + + /* Re-route this IRQ */ + GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp)); + + /* Update the pcpu_masks */ + for (i = 0; i < NR_CPUS; i++) + clear_bit(irq, pcpu_masks[i].pcpu_mask); + set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask); + + cpumask_copy(d->affinity, cpumask); + spin_unlock_irqrestore(&gic_lock, flags); + + return IRQ_SET_MASK_OK_NOCOPY; +} +#endif + +static struct irq_chip gic_irq_controller = { + .name = "MIPS GIC", + .irq_ack = gic_irq_ack, + .irq_mask = gic_mask_irq, + .irq_mask_ack = gic_mask_irq, + .irq_unmask = gic_unmask_irq, + .irq_eoi = gic_finish_irq, +#ifdef CONFIG_SMP + .irq_set_affinity = gic_set_affinity, +#endif +}; + +static void __init gic_setup_intr(unsigned int intr, unsigned int cpu, + unsigned int pin, unsigned int polarity, unsigned int trigtype, + unsigned int flags) +{ + struct gic_shared_intr_map *map_ptr; + + /* Setup Intr to Pin mapping */ + if (pin & GIC_MAP_TO_NMI_MSK) { + int i; + + GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), pin); + /* FIXME: hack to route NMI to all cpu's */ + for (i = 0; i < NR_CPUS; i += 32) { + GICWRITE(GIC_REG_ADDR(SHARED, + GIC_SH_MAP_TO_VPE_REG_OFF(intr, i)), + 0xffffffff); + } + } else { + GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), + GIC_MAP_TO_PIN_MSK | pin); + /* Setup Intr to CPU mapping */ + GIC_SH_MAP_TO_VPE_SMASK(intr, cpu); + if (cpu_has_veic) { + set_vi_handler(pin + GIC_PIN_TO_VEC_OFFSET, + gic_eic_irq_dispatch); + map_ptr = &gic_shared_intr_map[pin + GIC_PIN_TO_VEC_OFFSET]; + if (map_ptr->num_shared_intr >= GIC_MAX_SHARED_INTR) + BUG(); + map_ptr->intr_list[map_ptr->num_shared_intr++] = intr; + } + } + + /* Setup Intr Polarity */ + GIC_SET_POLARITY(intr, polarity); + + /* Setup Intr Trigger Type */ + GIC_SET_TRIGGER(intr, trigtype); + + /* Init Intr Masks */ + GIC_CLR_INTR_MASK(intr); + + /* Initialise per-cpu Interrupt software masks */ + set_bit(intr, pcpu_masks[cpu].pcpu_mask); + + if ((flags & GIC_FLAG_TRANSPARENT) && (cpu_has_veic == 0)) + GIC_SET_INTR_MASK(intr); + if (trigtype == GIC_TRIG_EDGE) + gic_irq_flags[intr] |= GIC_TRIG_EDGE; +} + +static void __init gic_basic_init(int numintrs, int numvpes, + struct gic_intr_map *intrmap, int mapsize) +{ + unsigned int i, cpu; + unsigned int pin_offset = 0; + + board_bind_eic_interrupt = &gic_bind_eic_interrupt; + + /* Setup defaults */ + for (i = 0; i < numintrs; i++) { + GIC_SET_POLARITY(i, GIC_POL_POS); + GIC_SET_TRIGGER(i, GIC_TRIG_LEVEL); + GIC_CLR_INTR_MASK(i); + if (i < GIC_NUM_INTRS) { + gic_irq_flags[i] = 0; + gic_shared_intr_map[i].num_shared_intr = 0; + gic_shared_intr_map[i].local_intr_mask = 0; + } + } + + /* + * In EIC mode, the HW_INT# is offset by (2-1). Need to subtract + * one because the GIC will add one (since 0=no intr). + */ + if (cpu_has_veic) + pin_offset = (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); + + /* Setup specifics */ + for (i = 0; i < mapsize; i++) { + cpu = intrmap[i].cpunum; + if (cpu == GIC_UNUSED) + continue; + gic_setup_intr(i, + intrmap[i].cpunum, + intrmap[i].pin + pin_offset, + intrmap[i].polarity, + intrmap[i].trigtype, + intrmap[i].flags); + } + + vpe_local_setup(numvpes); +} + +void __init gic_init(unsigned long gic_base_addr, + unsigned long gic_addrspace_size, + struct gic_intr_map *intr_map, unsigned int intr_map_size, + unsigned int irqbase) +{ + unsigned int gicconfig; + int numvpes, numintrs; + + _gic_base = (unsigned long) ioremap_nocache(gic_base_addr, + gic_addrspace_size); + gic_irq_base = irqbase; + + GICREAD(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); + numintrs = (gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK) >> + GIC_SH_CONFIG_NUMINTRS_SHF; + numintrs = ((numintrs + 1) * 8); + + numvpes = (gicconfig & GIC_SH_CONFIG_NUMVPES_MSK) >> + GIC_SH_CONFIG_NUMVPES_SHF; + numvpes = numvpes + 1; + + gic_basic_init(numintrs, numvpes, intr_map, intr_map_size); + + gic_platform_init(numintrs, &gic_irq_controller); +}