162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2008 Ralf Baechle (ralf@linux-mips.org) 762306a36Sopenharmony_ci * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "irq-mips-gic: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/bitfield.h> 1362306a36Sopenharmony_ci#include <linux/bitmap.h> 1462306a36Sopenharmony_ci#include <linux/clocksource.h> 1562306a36Sopenharmony_ci#include <linux/cpuhotplug.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/irq.h> 1962306a36Sopenharmony_ci#include <linux/irqchip.h> 2062306a36Sopenharmony_ci#include <linux/irqdomain.h> 2162306a36Sopenharmony_ci#include <linux/of_address.h> 2262306a36Sopenharmony_ci#include <linux/percpu.h> 2362306a36Sopenharmony_ci#include <linux/sched.h> 2462306a36Sopenharmony_ci#include <linux/smp.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/mips-cps.h> 2762306a36Sopenharmony_ci#include <asm/setup.h> 2862306a36Sopenharmony_ci#include <asm/traps.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <dt-bindings/interrupt-controller/mips-gic.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define GIC_MAX_INTRS 256 3362306a36Sopenharmony_ci#define GIC_MAX_LONGS BITS_TO_LONGS(GIC_MAX_INTRS) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Add 2 to convert GIC CPU pin to core interrupt */ 3662306a36Sopenharmony_ci#define GIC_CPU_PIN_OFFSET 2 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Mapped interrupt to pin X, then GIC will generate the vector (X+1). */ 3962306a36Sopenharmony_ci#define GIC_PIN_TO_VEC_OFFSET 1 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Convert between local/shared IRQ number and GIC HW IRQ number. */ 4262306a36Sopenharmony_ci#define GIC_LOCAL_HWIRQ_BASE 0 4362306a36Sopenharmony_ci#define GIC_LOCAL_TO_HWIRQ(x) (GIC_LOCAL_HWIRQ_BASE + (x)) 4462306a36Sopenharmony_ci#define GIC_HWIRQ_TO_LOCAL(x) ((x) - GIC_LOCAL_HWIRQ_BASE) 4562306a36Sopenharmony_ci#define GIC_SHARED_HWIRQ_BASE GIC_NUM_LOCAL_INTRS 4662306a36Sopenharmony_ci#define GIC_SHARED_TO_HWIRQ(x) (GIC_SHARED_HWIRQ_BASE + (x)) 4762306a36Sopenharmony_ci#define GIC_HWIRQ_TO_SHARED(x) ((x) - GIC_SHARED_HWIRQ_BASE) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_civoid __iomem *mips_gic_base; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic DEFINE_PER_CPU_READ_MOSTLY(unsigned long[GIC_MAX_LONGS], pcpu_masks); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(gic_lock); 5462306a36Sopenharmony_cistatic struct irq_domain *gic_irq_domain; 5562306a36Sopenharmony_cistatic int gic_shared_intrs; 5662306a36Sopenharmony_cistatic unsigned int gic_cpu_pin; 5762306a36Sopenharmony_cistatic struct irq_chip gic_level_irq_controller, gic_edge_irq_controller; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_IRQ_IPI 6062306a36Sopenharmony_cistatic DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS); 6162306a36Sopenharmony_cistatic DECLARE_BITMAP(ipi_available, GIC_MAX_INTRS); 6262306a36Sopenharmony_ci#endif /* CONFIG_GENERIC_IRQ_IPI */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct gic_all_vpes_chip_data { 6562306a36Sopenharmony_ci u32 map; 6662306a36Sopenharmony_ci bool mask; 6762306a36Sopenharmony_ci} gic_all_vpes_chip_data[GIC_NUM_LOCAL_INTRS]; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void gic_clear_pcpu_masks(unsigned int intr) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci unsigned int i; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Clear the interrupt's bit in all pcpu_masks */ 7462306a36Sopenharmony_ci for_each_possible_cpu(i) 7562306a36Sopenharmony_ci clear_bit(intr, per_cpu_ptr(pcpu_masks, i)); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic bool gic_local_irq_is_routable(int intr) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci u32 vpe_ctl; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* All local interrupts are routable in EIC mode. */ 8362306a36Sopenharmony_ci if (cpu_has_veic) 8462306a36Sopenharmony_ci return true; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci vpe_ctl = read_gic_vl_ctl(); 8762306a36Sopenharmony_ci switch (intr) { 8862306a36Sopenharmony_ci case GIC_LOCAL_INT_TIMER: 8962306a36Sopenharmony_ci return vpe_ctl & GIC_VX_CTL_TIMER_ROUTABLE; 9062306a36Sopenharmony_ci case GIC_LOCAL_INT_PERFCTR: 9162306a36Sopenharmony_ci return vpe_ctl & GIC_VX_CTL_PERFCNT_ROUTABLE; 9262306a36Sopenharmony_ci case GIC_LOCAL_INT_FDC: 9362306a36Sopenharmony_ci return vpe_ctl & GIC_VX_CTL_FDC_ROUTABLE; 9462306a36Sopenharmony_ci case GIC_LOCAL_INT_SWINT0: 9562306a36Sopenharmony_ci case GIC_LOCAL_INT_SWINT1: 9662306a36Sopenharmony_ci return vpe_ctl & GIC_VX_CTL_SWINT_ROUTABLE; 9762306a36Sopenharmony_ci default: 9862306a36Sopenharmony_ci return true; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void gic_bind_eic_interrupt(int irq, int set) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci /* Convert irq vector # to hw int # */ 10562306a36Sopenharmony_ci irq -= GIC_PIN_TO_VEC_OFFSET; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Set irq to use shadow set */ 10862306a36Sopenharmony_ci write_gic_vl_eic_shadow_set(irq, set); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void gic_send_ipi(struct irq_data *d, unsigned int cpu) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci irq_hw_number_t hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(d)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci write_gic_wedge(GIC_WEDGE_RW | hwirq); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint gic_get_c0_compare_int(void) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci if (!gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER)) 12162306a36Sopenharmony_ci return MIPS_CPU_IRQ_BASE + cp0_compare_irq; 12262306a36Sopenharmony_ci return irq_create_mapping(gic_irq_domain, 12362306a36Sopenharmony_ci GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_TIMER)); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciint gic_get_c0_perfcount_int(void) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) { 12962306a36Sopenharmony_ci /* Is the performance counter shared with the timer? */ 13062306a36Sopenharmony_ci if (cp0_perfcount_irq < 0) 13162306a36Sopenharmony_ci return -1; 13262306a36Sopenharmony_ci return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci return irq_create_mapping(gic_irq_domain, 13562306a36Sopenharmony_ci GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR)); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint gic_get_c0_fdc_int(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci if (!gic_local_irq_is_routable(GIC_LOCAL_INT_FDC)) { 14162306a36Sopenharmony_ci /* Is the FDC IRQ even present? */ 14262306a36Sopenharmony_ci if (cp0_fdc_irq < 0) 14362306a36Sopenharmony_ci return -1; 14462306a36Sopenharmony_ci return MIPS_CPU_IRQ_BASE + cp0_fdc_irq; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return irq_create_mapping(gic_irq_domain, 14862306a36Sopenharmony_ci GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC)); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void gic_handle_shared_int(bool chained) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci unsigned int intr; 15462306a36Sopenharmony_ci unsigned long *pcpu_mask; 15562306a36Sopenharmony_ci DECLARE_BITMAP(pending, GIC_MAX_INTRS); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Get per-cpu bitmaps */ 15862306a36Sopenharmony_ci pcpu_mask = this_cpu_ptr(pcpu_masks); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (mips_cm_is64) 16162306a36Sopenharmony_ci __ioread64_copy(pending, addr_gic_pend(), 16262306a36Sopenharmony_ci DIV_ROUND_UP(gic_shared_intrs, 64)); 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci __ioread32_copy(pending, addr_gic_pend(), 16562306a36Sopenharmony_ci DIV_ROUND_UP(gic_shared_intrs, 32)); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci bitmap_and(pending, pending, pcpu_mask, gic_shared_intrs); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for_each_set_bit(intr, pending, gic_shared_intrs) { 17062306a36Sopenharmony_ci if (chained) 17162306a36Sopenharmony_ci generic_handle_domain_irq(gic_irq_domain, 17262306a36Sopenharmony_ci GIC_SHARED_TO_HWIRQ(intr)); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci do_domain_IRQ(gic_irq_domain, 17562306a36Sopenharmony_ci GIC_SHARED_TO_HWIRQ(intr)); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void gic_mask_irq(struct irq_data *d) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci unsigned int intr = GIC_HWIRQ_TO_SHARED(d->hwirq); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci write_gic_rmask(intr); 18462306a36Sopenharmony_ci gic_clear_pcpu_masks(intr); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void gic_unmask_irq(struct irq_data *d) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci unsigned int intr = GIC_HWIRQ_TO_SHARED(d->hwirq); 19062306a36Sopenharmony_ci unsigned int cpu; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci write_gic_smask(intr); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci gic_clear_pcpu_masks(intr); 19562306a36Sopenharmony_ci cpu = cpumask_first(irq_data_get_effective_affinity_mask(d)); 19662306a36Sopenharmony_ci set_bit(intr, per_cpu_ptr(pcpu_masks, cpu)); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void gic_ack_irq(struct irq_data *d) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci write_gic_wedge(irq); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int gic_set_type(struct irq_data *d, unsigned int type) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci unsigned int irq, pol, trig, dual; 20962306a36Sopenharmony_ci unsigned long flags; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci irq = GIC_HWIRQ_TO_SHARED(d->hwirq); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci raw_spin_lock_irqsave(&gic_lock, flags); 21462306a36Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 21562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 21662306a36Sopenharmony_ci pol = GIC_POL_FALLING_EDGE; 21762306a36Sopenharmony_ci trig = GIC_TRIG_EDGE; 21862306a36Sopenharmony_ci dual = GIC_DUAL_SINGLE; 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 22162306a36Sopenharmony_ci pol = GIC_POL_RISING_EDGE; 22262306a36Sopenharmony_ci trig = GIC_TRIG_EDGE; 22362306a36Sopenharmony_ci dual = GIC_DUAL_SINGLE; 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 22662306a36Sopenharmony_ci pol = 0; /* Doesn't matter */ 22762306a36Sopenharmony_ci trig = GIC_TRIG_EDGE; 22862306a36Sopenharmony_ci dual = GIC_DUAL_DUAL; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 23162306a36Sopenharmony_ci pol = GIC_POL_ACTIVE_LOW; 23262306a36Sopenharmony_ci trig = GIC_TRIG_LEVEL; 23362306a36Sopenharmony_ci dual = GIC_DUAL_SINGLE; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 23662306a36Sopenharmony_ci default: 23762306a36Sopenharmony_ci pol = GIC_POL_ACTIVE_HIGH; 23862306a36Sopenharmony_ci trig = GIC_TRIG_LEVEL; 23962306a36Sopenharmony_ci dual = GIC_DUAL_SINGLE; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci change_gic_pol(irq, pol); 24462306a36Sopenharmony_ci change_gic_trig(irq, trig); 24562306a36Sopenharmony_ci change_gic_dual(irq, dual); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (trig == GIC_TRIG_EDGE) 24862306a36Sopenharmony_ci irq_set_chip_handler_name_locked(d, &gic_edge_irq_controller, 24962306a36Sopenharmony_ci handle_edge_irq, NULL); 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci irq_set_chip_handler_name_locked(d, &gic_level_irq_controller, 25262306a36Sopenharmony_ci handle_level_irq, NULL); 25362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gic_lock, flags); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci#ifdef CONFIG_SMP 25962306a36Sopenharmony_cistatic int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, 26062306a36Sopenharmony_ci bool force) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); 26362306a36Sopenharmony_ci unsigned long flags; 26462306a36Sopenharmony_ci unsigned int cpu; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci cpu = cpumask_first_and(cpumask, cpu_online_mask); 26762306a36Sopenharmony_ci if (cpu >= NR_CPUS) 26862306a36Sopenharmony_ci return -EINVAL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Assumption : cpumask refers to a single CPU */ 27162306a36Sopenharmony_ci raw_spin_lock_irqsave(&gic_lock, flags); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Re-route this IRQ */ 27462306a36Sopenharmony_ci write_gic_map_vp(irq, BIT(mips_cm_vp_id(cpu))); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Update the pcpu_masks */ 27762306a36Sopenharmony_ci gic_clear_pcpu_masks(irq); 27862306a36Sopenharmony_ci if (read_gic_mask(irq)) 27962306a36Sopenharmony_ci set_bit(irq, per_cpu_ptr(pcpu_masks, cpu)); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(cpu)); 28262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gic_lock, flags); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return IRQ_SET_MASK_OK; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci#endif 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic struct irq_chip gic_level_irq_controller = { 28962306a36Sopenharmony_ci .name = "MIPS GIC", 29062306a36Sopenharmony_ci .irq_mask = gic_mask_irq, 29162306a36Sopenharmony_ci .irq_unmask = gic_unmask_irq, 29262306a36Sopenharmony_ci .irq_set_type = gic_set_type, 29362306a36Sopenharmony_ci#ifdef CONFIG_SMP 29462306a36Sopenharmony_ci .irq_set_affinity = gic_set_affinity, 29562306a36Sopenharmony_ci#endif 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct irq_chip gic_edge_irq_controller = { 29962306a36Sopenharmony_ci .name = "MIPS GIC", 30062306a36Sopenharmony_ci .irq_ack = gic_ack_irq, 30162306a36Sopenharmony_ci .irq_mask = gic_mask_irq, 30262306a36Sopenharmony_ci .irq_unmask = gic_unmask_irq, 30362306a36Sopenharmony_ci .irq_set_type = gic_set_type, 30462306a36Sopenharmony_ci#ifdef CONFIG_SMP 30562306a36Sopenharmony_ci .irq_set_affinity = gic_set_affinity, 30662306a36Sopenharmony_ci#endif 30762306a36Sopenharmony_ci .ipi_send_single = gic_send_ipi, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void gic_handle_local_int(bool chained) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci unsigned long pending, masked; 31362306a36Sopenharmony_ci unsigned int intr; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci pending = read_gic_vl_pend(); 31662306a36Sopenharmony_ci masked = read_gic_vl_mask(); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci bitmap_and(&pending, &pending, &masked, GIC_NUM_LOCAL_INTRS); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci for_each_set_bit(intr, &pending, GIC_NUM_LOCAL_INTRS) { 32162306a36Sopenharmony_ci if (chained) 32262306a36Sopenharmony_ci generic_handle_domain_irq(gic_irq_domain, 32362306a36Sopenharmony_ci GIC_LOCAL_TO_HWIRQ(intr)); 32462306a36Sopenharmony_ci else 32562306a36Sopenharmony_ci do_domain_IRQ(gic_irq_domain, 32662306a36Sopenharmony_ci GIC_LOCAL_TO_HWIRQ(intr)); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void gic_mask_local_irq(struct irq_data *d) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci write_gic_vl_rmask(BIT(intr)); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void gic_unmask_local_irq(struct irq_data *d) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci write_gic_vl_smask(BIT(intr)); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic struct irq_chip gic_local_irq_controller = { 34562306a36Sopenharmony_ci .name = "MIPS GIC Local", 34662306a36Sopenharmony_ci .irq_mask = gic_mask_local_irq, 34762306a36Sopenharmony_ci .irq_unmask = gic_unmask_local_irq, 34862306a36Sopenharmony_ci}; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void gic_mask_local_irq_all_vpes(struct irq_data *d) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct gic_all_vpes_chip_data *cd; 35362306a36Sopenharmony_ci unsigned long flags; 35462306a36Sopenharmony_ci int intr, cpu; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); 35762306a36Sopenharmony_ci cd = irq_data_get_irq_chip_data(d); 35862306a36Sopenharmony_ci cd->mask = false; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci raw_spin_lock_irqsave(&gic_lock, flags); 36162306a36Sopenharmony_ci for_each_online_cpu(cpu) { 36262306a36Sopenharmony_ci write_gic_vl_other(mips_cm_vp_id(cpu)); 36362306a36Sopenharmony_ci write_gic_vo_rmask(BIT(intr)); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gic_lock, flags); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void gic_unmask_local_irq_all_vpes(struct irq_data *d) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct gic_all_vpes_chip_data *cd; 37162306a36Sopenharmony_ci unsigned long flags; 37262306a36Sopenharmony_ci int intr, cpu; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); 37562306a36Sopenharmony_ci cd = irq_data_get_irq_chip_data(d); 37662306a36Sopenharmony_ci cd->mask = true; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci raw_spin_lock_irqsave(&gic_lock, flags); 37962306a36Sopenharmony_ci for_each_online_cpu(cpu) { 38062306a36Sopenharmony_ci write_gic_vl_other(mips_cm_vp_id(cpu)); 38162306a36Sopenharmony_ci write_gic_vo_smask(BIT(intr)); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gic_lock, flags); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void gic_all_vpes_irq_cpu_online(void) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci static const unsigned int local_intrs[] = { 38962306a36Sopenharmony_ci GIC_LOCAL_INT_TIMER, 39062306a36Sopenharmony_ci GIC_LOCAL_INT_PERFCTR, 39162306a36Sopenharmony_ci GIC_LOCAL_INT_FDC, 39262306a36Sopenharmony_ci }; 39362306a36Sopenharmony_ci unsigned long flags; 39462306a36Sopenharmony_ci int i; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci raw_spin_lock_irqsave(&gic_lock, flags); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(local_intrs); i++) { 39962306a36Sopenharmony_ci unsigned int intr = local_intrs[i]; 40062306a36Sopenharmony_ci struct gic_all_vpes_chip_data *cd; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!gic_local_irq_is_routable(intr)) 40362306a36Sopenharmony_ci continue; 40462306a36Sopenharmony_ci cd = &gic_all_vpes_chip_data[intr]; 40562306a36Sopenharmony_ci write_gic_vl_map(mips_gic_vx_map_reg(intr), cd->map); 40662306a36Sopenharmony_ci if (cd->mask) 40762306a36Sopenharmony_ci write_gic_vl_smask(BIT(intr)); 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gic_lock, flags); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic struct irq_chip gic_all_vpes_local_irq_controller = { 41462306a36Sopenharmony_ci .name = "MIPS GIC Local", 41562306a36Sopenharmony_ci .irq_mask = gic_mask_local_irq_all_vpes, 41662306a36Sopenharmony_ci .irq_unmask = gic_unmask_local_irq_all_vpes, 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void __gic_irq_dispatch(void) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci gic_handle_local_int(false); 42262306a36Sopenharmony_ci gic_handle_shared_int(false); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void gic_irq_dispatch(struct irq_desc *desc) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci gic_handle_local_int(true); 42862306a36Sopenharmony_ci gic_handle_shared_int(true); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, 43262306a36Sopenharmony_ci irq_hw_number_t hw, unsigned int cpu) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci int intr = GIC_HWIRQ_TO_SHARED(hw); 43562306a36Sopenharmony_ci struct irq_data *data; 43662306a36Sopenharmony_ci unsigned long flags; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci data = irq_get_irq_data(virq); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci raw_spin_lock_irqsave(&gic_lock, flags); 44162306a36Sopenharmony_ci write_gic_map_pin(intr, GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin); 44262306a36Sopenharmony_ci write_gic_map_vp(intr, BIT(mips_cm_vp_id(cpu))); 44362306a36Sopenharmony_ci irq_data_update_effective_affinity(data, cpumask_of(cpu)); 44462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gic_lock, flags); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, 45062306a36Sopenharmony_ci const u32 *intspec, unsigned int intsize, 45162306a36Sopenharmony_ci irq_hw_number_t *out_hwirq, 45262306a36Sopenharmony_ci unsigned int *out_type) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci if (intsize != 3) 45562306a36Sopenharmony_ci return -EINVAL; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (intspec[0] == GIC_SHARED) 45862306a36Sopenharmony_ci *out_hwirq = GIC_SHARED_TO_HWIRQ(intspec[1]); 45962306a36Sopenharmony_ci else if (intspec[0] == GIC_LOCAL) 46062306a36Sopenharmony_ci *out_hwirq = GIC_LOCAL_TO_HWIRQ(intspec[1]); 46162306a36Sopenharmony_ci else 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, 46962306a36Sopenharmony_ci irq_hw_number_t hwirq) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct gic_all_vpes_chip_data *cd; 47262306a36Sopenharmony_ci unsigned long flags; 47362306a36Sopenharmony_ci unsigned int intr; 47462306a36Sopenharmony_ci int err, cpu; 47562306a36Sopenharmony_ci u32 map; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (hwirq >= GIC_SHARED_HWIRQ_BASE) { 47862306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_IRQ_IPI 47962306a36Sopenharmony_ci /* verify that shared irqs don't conflict with an IPI irq */ 48062306a36Sopenharmony_ci if (test_bit(GIC_HWIRQ_TO_SHARED(hwirq), ipi_resrv)) 48162306a36Sopenharmony_ci return -EBUSY; 48262306a36Sopenharmony_ci#endif /* CONFIG_GENERIC_IRQ_IPI */ 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, 48562306a36Sopenharmony_ci &gic_level_irq_controller, 48662306a36Sopenharmony_ci NULL); 48762306a36Sopenharmony_ci if (err) 48862306a36Sopenharmony_ci return err; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq))); 49162306a36Sopenharmony_ci return gic_shared_irq_domain_map(d, virq, hwirq, 0); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci intr = GIC_HWIRQ_TO_LOCAL(hwirq); 49562306a36Sopenharmony_ci map = GIC_MAP_PIN_MAP_TO_PIN | gic_cpu_pin; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * If adding support for more per-cpu interrupts, keep the 49962306a36Sopenharmony_ci * array in gic_all_vpes_irq_cpu_online() in sync. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ci switch (intr) { 50262306a36Sopenharmony_ci case GIC_LOCAL_INT_TIMER: 50362306a36Sopenharmony_ci case GIC_LOCAL_INT_PERFCTR: 50462306a36Sopenharmony_ci case GIC_LOCAL_INT_FDC: 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * HACK: These are all really percpu interrupts, but 50762306a36Sopenharmony_ci * the rest of the MIPS kernel code does not use the 50862306a36Sopenharmony_ci * percpu IRQ API for them. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci cd = &gic_all_vpes_chip_data[intr]; 51162306a36Sopenharmony_ci cd->map = map; 51262306a36Sopenharmony_ci err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, 51362306a36Sopenharmony_ci &gic_all_vpes_local_irq_controller, 51462306a36Sopenharmony_ci cd); 51562306a36Sopenharmony_ci if (err) 51662306a36Sopenharmony_ci return err; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci irq_set_handler(virq, handle_percpu_irq); 51962306a36Sopenharmony_ci break; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci default: 52262306a36Sopenharmony_ci err = irq_domain_set_hwirq_and_chip(d, virq, hwirq, 52362306a36Sopenharmony_ci &gic_local_irq_controller, 52462306a36Sopenharmony_ci NULL); 52562306a36Sopenharmony_ci if (err) 52662306a36Sopenharmony_ci return err; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci irq_set_handler(virq, handle_percpu_devid_irq); 52962306a36Sopenharmony_ci irq_set_percpu_devid(virq); 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (!gic_local_irq_is_routable(intr)) 53462306a36Sopenharmony_ci return -EPERM; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci raw_spin_lock_irqsave(&gic_lock, flags); 53762306a36Sopenharmony_ci for_each_online_cpu(cpu) { 53862306a36Sopenharmony_ci write_gic_vl_other(mips_cm_vp_id(cpu)); 53962306a36Sopenharmony_ci write_gic_vo_map(mips_gic_vx_map_reg(intr), map); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gic_lock, flags); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int gic_irq_domain_alloc(struct irq_domain *d, unsigned int virq, 54762306a36Sopenharmony_ci unsigned int nr_irqs, void *arg) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct irq_fwspec *fwspec = arg; 55062306a36Sopenharmony_ci irq_hw_number_t hwirq; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (fwspec->param[0] == GIC_SHARED) 55362306a36Sopenharmony_ci hwirq = GIC_SHARED_TO_HWIRQ(fwspec->param[1]); 55462306a36Sopenharmony_ci else 55562306a36Sopenharmony_ci hwirq = GIC_LOCAL_TO_HWIRQ(fwspec->param[1]); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return gic_irq_domain_map(d, virq, hwirq); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic void gic_irq_domain_free(struct irq_domain *d, unsigned int virq, 56162306a36Sopenharmony_ci unsigned int nr_irqs) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic const struct irq_domain_ops gic_irq_domain_ops = { 56662306a36Sopenharmony_ci .xlate = gic_irq_domain_xlate, 56762306a36Sopenharmony_ci .alloc = gic_irq_domain_alloc, 56862306a36Sopenharmony_ci .free = gic_irq_domain_free, 56962306a36Sopenharmony_ci .map = gic_irq_domain_map, 57062306a36Sopenharmony_ci}; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_IRQ_IPI 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int gic_ipi_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, 57562306a36Sopenharmony_ci const u32 *intspec, unsigned int intsize, 57662306a36Sopenharmony_ci irq_hw_number_t *out_hwirq, 57762306a36Sopenharmony_ci unsigned int *out_type) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci /* 58062306a36Sopenharmony_ci * There's nothing to translate here. hwirq is dynamically allocated and 58162306a36Sopenharmony_ci * the irq type is always edge triggered. 58262306a36Sopenharmony_ci * */ 58362306a36Sopenharmony_ci *out_hwirq = 0; 58462306a36Sopenharmony_ci *out_type = IRQ_TYPE_EDGE_RISING; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic int gic_ipi_domain_alloc(struct irq_domain *d, unsigned int virq, 59062306a36Sopenharmony_ci unsigned int nr_irqs, void *arg) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct cpumask *ipimask = arg; 59362306a36Sopenharmony_ci irq_hw_number_t hwirq, base_hwirq; 59462306a36Sopenharmony_ci int cpu, ret, i; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci base_hwirq = find_first_bit(ipi_available, gic_shared_intrs); 59762306a36Sopenharmony_ci if (base_hwirq == gic_shared_intrs) 59862306a36Sopenharmony_ci return -ENOMEM; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* check that we have enough space */ 60162306a36Sopenharmony_ci for (i = base_hwirq; i < nr_irqs; i++) { 60262306a36Sopenharmony_ci if (!test_bit(i, ipi_available)) 60362306a36Sopenharmony_ci return -EBUSY; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci bitmap_clear(ipi_available, base_hwirq, nr_irqs); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* map the hwirq for each cpu consecutively */ 60862306a36Sopenharmony_ci i = 0; 60962306a36Sopenharmony_ci for_each_cpu(cpu, ipimask) { 61062306a36Sopenharmony_ci hwirq = GIC_SHARED_TO_HWIRQ(base_hwirq + i); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ret = irq_domain_set_hwirq_and_chip(d, virq + i, hwirq, 61362306a36Sopenharmony_ci &gic_edge_irq_controller, 61462306a36Sopenharmony_ci NULL); 61562306a36Sopenharmony_ci if (ret) 61662306a36Sopenharmony_ci goto error; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci ret = irq_domain_set_hwirq_and_chip(d->parent, virq + i, hwirq, 61962306a36Sopenharmony_ci &gic_edge_irq_controller, 62062306a36Sopenharmony_ci NULL); 62162306a36Sopenharmony_ci if (ret) 62262306a36Sopenharmony_ci goto error; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ret = irq_set_irq_type(virq + i, IRQ_TYPE_EDGE_RISING); 62562306a36Sopenharmony_ci if (ret) 62662306a36Sopenharmony_ci goto error; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ret = gic_shared_irq_domain_map(d, virq + i, hwirq, cpu); 62962306a36Sopenharmony_ci if (ret) 63062306a36Sopenharmony_ci goto error; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci i++; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_cierror: 63762306a36Sopenharmony_ci bitmap_set(ipi_available, base_hwirq, nr_irqs); 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic void gic_ipi_domain_free(struct irq_domain *d, unsigned int virq, 64262306a36Sopenharmony_ci unsigned int nr_irqs) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci irq_hw_number_t base_hwirq; 64562306a36Sopenharmony_ci struct irq_data *data; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci data = irq_get_irq_data(virq); 64862306a36Sopenharmony_ci if (!data) 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci base_hwirq = GIC_HWIRQ_TO_SHARED(irqd_to_hwirq(data)); 65262306a36Sopenharmony_ci bitmap_set(ipi_available, base_hwirq, nr_irqs); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node, 65662306a36Sopenharmony_ci enum irq_domain_bus_token bus_token) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci bool is_ipi; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci switch (bus_token) { 66162306a36Sopenharmony_ci case DOMAIN_BUS_IPI: 66262306a36Sopenharmony_ci is_ipi = d->bus_token == bus_token; 66362306a36Sopenharmony_ci return (!node || to_of_node(d->fwnode) == node) && is_ipi; 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci default: 66662306a36Sopenharmony_ci return 0; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic const struct irq_domain_ops gic_ipi_domain_ops = { 67162306a36Sopenharmony_ci .xlate = gic_ipi_domain_xlate, 67262306a36Sopenharmony_ci .alloc = gic_ipi_domain_alloc, 67362306a36Sopenharmony_ci .free = gic_ipi_domain_free, 67462306a36Sopenharmony_ci .match = gic_ipi_domain_match, 67562306a36Sopenharmony_ci}; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int gic_register_ipi_domain(struct device_node *node) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct irq_domain *gic_ipi_domain; 68062306a36Sopenharmony_ci unsigned int v[2], num_ipis; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain, 68362306a36Sopenharmony_ci IRQ_DOMAIN_FLAG_IPI_PER_CPU, 68462306a36Sopenharmony_ci GIC_NUM_LOCAL_INTRS + gic_shared_intrs, 68562306a36Sopenharmony_ci node, &gic_ipi_domain_ops, NULL); 68662306a36Sopenharmony_ci if (!gic_ipi_domain) { 68762306a36Sopenharmony_ci pr_err("Failed to add IPI domain"); 68862306a36Sopenharmony_ci return -ENXIO; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci irq_domain_update_bus_token(gic_ipi_domain, DOMAIN_BUS_IPI); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (node && 69462306a36Sopenharmony_ci !of_property_read_u32_array(node, "mti,reserved-ipi-vectors", v, 2)) { 69562306a36Sopenharmony_ci bitmap_set(ipi_resrv, v[0], v[1]); 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * Reserve 2 interrupts per possible CPU/VP for use as IPIs, 69962306a36Sopenharmony_ci * meeting the requirements of arch/mips SMP. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci num_ipis = 2 * num_possible_cpus(); 70262306a36Sopenharmony_ci bitmap_set(ipi_resrv, gic_shared_intrs - num_ipis, num_ipis); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci bitmap_copy(ipi_available, ipi_resrv, GIC_MAX_INTRS); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci#else /* !CONFIG_GENERIC_IRQ_IPI */ 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic inline int gic_register_ipi_domain(struct device_node *node) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci#endif /* !CONFIG_GENERIC_IRQ_IPI */ 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int gic_cpu_startup(unsigned int cpu) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci /* Enable or disable EIC */ 72262306a36Sopenharmony_ci change_gic_vl_ctl(GIC_VX_CTL_EIC, 72362306a36Sopenharmony_ci cpu_has_veic ? GIC_VX_CTL_EIC : 0); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* Clear all local IRQ masks (ie. disable all local interrupts) */ 72662306a36Sopenharmony_ci write_gic_vl_rmask(~0); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Enable desired interrupts */ 72962306a36Sopenharmony_ci gic_all_vpes_irq_cpu_online(); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return 0; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int __init gic_of_init(struct device_node *node, 73562306a36Sopenharmony_ci struct device_node *parent) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci unsigned int cpu_vec, i, gicconfig; 73862306a36Sopenharmony_ci unsigned long reserved; 73962306a36Sopenharmony_ci phys_addr_t gic_base; 74062306a36Sopenharmony_ci struct resource res; 74162306a36Sopenharmony_ci size_t gic_len; 74262306a36Sopenharmony_ci int ret; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Find the first available CPU vector. */ 74562306a36Sopenharmony_ci i = 0; 74662306a36Sopenharmony_ci reserved = (C_SW0 | C_SW1) >> __ffs(C_SW0); 74762306a36Sopenharmony_ci while (!of_property_read_u32_index(node, "mti,reserved-cpu-vectors", 74862306a36Sopenharmony_ci i++, &cpu_vec)) 74962306a36Sopenharmony_ci reserved |= BIT(cpu_vec); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci cpu_vec = find_first_zero_bit(&reserved, hweight_long(ST0_IM)); 75262306a36Sopenharmony_ci if (cpu_vec == hweight_long(ST0_IM)) { 75362306a36Sopenharmony_ci pr_err("No CPU vectors available\n"); 75462306a36Sopenharmony_ci return -ENODEV; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (of_address_to_resource(node, 0, &res)) { 75862306a36Sopenharmony_ci /* 75962306a36Sopenharmony_ci * Probe the CM for the GIC base address if not specified 76062306a36Sopenharmony_ci * in the device-tree. 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci if (mips_cm_present()) { 76362306a36Sopenharmony_ci gic_base = read_gcr_gic_base() & 76462306a36Sopenharmony_ci ~CM_GCR_GIC_BASE_GICEN; 76562306a36Sopenharmony_ci gic_len = 0x20000; 76662306a36Sopenharmony_ci pr_warn("Using inherited base address %pa\n", 76762306a36Sopenharmony_ci &gic_base); 76862306a36Sopenharmony_ci } else { 76962306a36Sopenharmony_ci pr_err("Failed to get memory range\n"); 77062306a36Sopenharmony_ci return -ENODEV; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci } else { 77362306a36Sopenharmony_ci gic_base = res.start; 77462306a36Sopenharmony_ci gic_len = resource_size(&res); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (mips_cm_present()) { 77862306a36Sopenharmony_ci write_gcr_gic_base(gic_base | CM_GCR_GIC_BASE_GICEN); 77962306a36Sopenharmony_ci /* Ensure GIC region is enabled before trying to access it */ 78062306a36Sopenharmony_ci __sync(); 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci mips_gic_base = ioremap(gic_base, gic_len); 78462306a36Sopenharmony_ci if (!mips_gic_base) { 78562306a36Sopenharmony_ci pr_err("Failed to ioremap gic_base\n"); 78662306a36Sopenharmony_ci return -ENOMEM; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci gicconfig = read_gic_config(); 79062306a36Sopenharmony_ci gic_shared_intrs = FIELD_GET(GIC_CONFIG_NUMINTERRUPTS, gicconfig); 79162306a36Sopenharmony_ci gic_shared_intrs = (gic_shared_intrs + 1) * 8; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (cpu_has_veic) { 79462306a36Sopenharmony_ci /* Always use vector 1 in EIC mode */ 79562306a36Sopenharmony_ci gic_cpu_pin = 0; 79662306a36Sopenharmony_ci set_vi_handler(gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET, 79762306a36Sopenharmony_ci __gic_irq_dispatch); 79862306a36Sopenharmony_ci } else { 79962306a36Sopenharmony_ci gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET; 80062306a36Sopenharmony_ci irq_set_chained_handler(MIPS_CPU_IRQ_BASE + cpu_vec, 80162306a36Sopenharmony_ci gic_irq_dispatch); 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci gic_irq_domain = irq_domain_add_simple(node, GIC_NUM_LOCAL_INTRS + 80562306a36Sopenharmony_ci gic_shared_intrs, 0, 80662306a36Sopenharmony_ci &gic_irq_domain_ops, NULL); 80762306a36Sopenharmony_ci if (!gic_irq_domain) { 80862306a36Sopenharmony_ci pr_err("Failed to add IRQ domain"); 80962306a36Sopenharmony_ci return -ENXIO; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ret = gic_register_ipi_domain(node); 81362306a36Sopenharmony_ci if (ret) 81462306a36Sopenharmony_ci return ret; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci board_bind_eic_interrupt = &gic_bind_eic_interrupt; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Setup defaults */ 81962306a36Sopenharmony_ci for (i = 0; i < gic_shared_intrs; i++) { 82062306a36Sopenharmony_ci change_gic_pol(i, GIC_POL_ACTIVE_HIGH); 82162306a36Sopenharmony_ci change_gic_trig(i, GIC_TRIG_LEVEL); 82262306a36Sopenharmony_ci write_gic_rmask(i); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci return cpuhp_setup_state(CPUHP_AP_IRQ_MIPS_GIC_STARTING, 82662306a36Sopenharmony_ci "irqchip/mips/gic:starting", 82762306a36Sopenharmony_ci gic_cpu_startup, NULL); 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ciIRQCHIP_DECLARE(mips_gic, "mti,gic", gic_of_init); 830