18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2002 ARM Limited, All Rights Reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Interrupt architecture for the GIC: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * o There is one Interrupt Distributor, which receives interrupts 88c2ecf20Sopenharmony_ci * from system devices and sends them to the Interrupt Controllers. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * o There is one CPU Interface per CPU, which sends interrupts sent 118c2ecf20Sopenharmony_ci * by the Distributor, and interrupts generated locally, to the 128c2ecf20Sopenharmony_ci * associated CPU. The base address of the CPU interface is usually 138c2ecf20Sopenharmony_ci * aliased so that the same address points to different chips depending 148c2ecf20Sopenharmony_ci * on the CPU it is accessed from. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Note that IRQs 0-31 are special - they are local to each CPU. 178c2ecf20Sopenharmony_ci * As such, the enable set/clear, pending set/clear and active bit 188c2ecf20Sopenharmony_ci * registers are banked per-cpu for these sources. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/list.h> 258c2ecf20Sopenharmony_ci#include <linux/smp.h> 268c2ecf20Sopenharmony_ci#include <linux/cpu.h> 278c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h> 288c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 298c2ecf20Sopenharmony_ci#include <linux/io.h> 308c2ecf20Sopenharmony_ci#include <linux/of.h> 318c2ecf20Sopenharmony_ci#include <linux/of_address.h> 328c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 338c2ecf20Sopenharmony_ci#include <linux/acpi.h> 348c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 358c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 368c2ecf20Sopenharmony_ci#include <linux/percpu.h> 378c2ecf20Sopenharmony_ci#include <linux/slab.h> 388c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 398c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 408c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <asm/cputype.h> 438c2ecf20Sopenharmony_ci#include <asm/irq.h> 448c2ecf20Sopenharmony_ci#include <asm/exception.h> 458c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 468c2ecf20Sopenharmony_ci#include <asm/virt.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include "irq-gic-common.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64 518c2ecf20Sopenharmony_ci#include <asm/cpufeature.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void gic_check_cpu_features(void) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci WARN_TAINT_ONCE(this_cpu_has_cap(ARM64_HAS_SYSREG_GIC_CPUIF), 568c2ecf20Sopenharmony_ci TAINT_CPU_OUT_OF_SPEC, 578c2ecf20Sopenharmony_ci "GICv3 system registers enabled, broken firmware!\n"); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci#else 608c2ecf20Sopenharmony_ci#define gic_check_cpu_features() do { } while(0) 618c2ecf20Sopenharmony_ci#endif 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciunion gic_base { 648c2ecf20Sopenharmony_ci void __iomem *common_base; 658c2ecf20Sopenharmony_ci void __percpu * __iomem *percpu_base; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistruct gic_chip_data { 698c2ecf20Sopenharmony_ci struct irq_chip chip; 708c2ecf20Sopenharmony_ci union gic_base dist_base; 718c2ecf20Sopenharmony_ci union gic_base cpu_base; 728c2ecf20Sopenharmony_ci void __iomem *raw_dist_base; 738c2ecf20Sopenharmony_ci void __iomem *raw_cpu_base; 748c2ecf20Sopenharmony_ci u32 percpu_offset; 758c2ecf20Sopenharmony_ci#if defined(CONFIG_CPU_PM) || defined(CONFIG_ARM_GIC_PM) 768c2ecf20Sopenharmony_ci u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; 778c2ecf20Sopenharmony_ci u32 saved_spi_active[DIV_ROUND_UP(1020, 32)]; 788c2ecf20Sopenharmony_ci u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)]; 798c2ecf20Sopenharmony_ci u32 saved_spi_target[DIV_ROUND_UP(1020, 4)]; 808c2ecf20Sopenharmony_ci u32 __percpu *saved_ppi_enable; 818c2ecf20Sopenharmony_ci u32 __percpu *saved_ppi_active; 828c2ecf20Sopenharmony_ci u32 __percpu *saved_ppi_conf; 838c2ecf20Sopenharmony_ci#endif 848c2ecf20Sopenharmony_ci struct irq_domain *domain; 858c2ecf20Sopenharmony_ci unsigned int gic_irqs; 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#ifdef CONFIG_BL_SWITCHER 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(cpu_map_lock); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define gic_lock_irqsave(f) \ 938c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&cpu_map_lock, (f)) 948c2ecf20Sopenharmony_ci#define gic_unlock_irqrestore(f) \ 958c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&cpu_map_lock, (f)) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define gic_lock() raw_spin_lock(&cpu_map_lock) 988c2ecf20Sopenharmony_ci#define gic_unlock() raw_spin_unlock(&cpu_map_lock) 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#else 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define gic_lock_irqsave(f) do { (void)(f); } while(0) 1038c2ecf20Sopenharmony_ci#define gic_unlock_irqrestore(f) do { (void)(f); } while(0) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define gic_lock() do { } while(0) 1068c2ecf20Sopenharmony_ci#define gic_unlock() do { } while(0) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#endif 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic DEFINE_STATIC_KEY_FALSE(needs_rmw_access); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * The GIC mapping of CPU interfaces does not necessarily match 1148c2ecf20Sopenharmony_ci * the logical CPU numbering. Let's use a mapping as returned 1158c2ecf20Sopenharmony_ci * by the GIC itself. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci#define NR_GIC_CPU_IF 8 1188c2ecf20Sopenharmony_cistatic u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic DEFINE_STATIC_KEY_TRUE(supports_deactivate_key); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic struct gic_kvm_info gic_v2_kvm_info; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(u32, sgi_intid); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#ifdef CONFIG_GIC_NON_BANKED 1298c2ecf20Sopenharmony_cistatic DEFINE_STATIC_KEY_FALSE(frankengic_key); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void enable_frankengic(void) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci static_branch_enable(&frankengic_key); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic inline void __iomem *__get_base(union gic_base *base) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci if (static_branch_unlikely(&frankengic_key)) 1398c2ecf20Sopenharmony_ci return raw_cpu_read(*base->percpu_base); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return base->common_base; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#define gic_data_dist_base(d) __get_base(&(d)->dist_base) 1458c2ecf20Sopenharmony_ci#define gic_data_cpu_base(d) __get_base(&(d)->cpu_base) 1468c2ecf20Sopenharmony_ci#else 1478c2ecf20Sopenharmony_ci#define gic_data_dist_base(d) ((d)->dist_base.common_base) 1488c2ecf20Sopenharmony_ci#define gic_data_cpu_base(d) ((d)->cpu_base.common_base) 1498c2ecf20Sopenharmony_ci#define enable_frankengic() do { } while(0) 1508c2ecf20Sopenharmony_ci#endif 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic inline void __iomem *gic_dist_base(struct irq_data *d) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); 1558c2ecf20Sopenharmony_ci return gic_data_dist_base(gic_data); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic inline void __iomem *gic_cpu_base(struct irq_data *d) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); 1618c2ecf20Sopenharmony_ci return gic_data_cpu_base(gic_data); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic inline unsigned int gic_irq(struct irq_data *d) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return d->hwirq; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic inline bool cascading_gic_irq(struct irq_data *d) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci void *data = irq_data_get_irq_handler_data(d); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * If handler_data is set, this is a cascading interrupt, and 1758c2ecf20Sopenharmony_ci * it cannot possibly be forwarded. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci return data != NULL; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * Routines to acknowledge, disable and enable interrupts 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic void gic_poke_irq(struct irq_data *d, u32 offset) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci u32 mask = 1 << (gic_irq(d) % 32); 1868c2ecf20Sopenharmony_ci writel_relaxed(mask, gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int gic_peek_irq(struct irq_data *d, u32 offset) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci u32 mask = 1 << (gic_irq(d) % 32); 1928c2ecf20Sopenharmony_ci return !!(readl_relaxed(gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4) & mask); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void gic_mask_irq(struct irq_data *d) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void gic_eoimode1_mask_irq(struct irq_data *d) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci gic_mask_irq(d); 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * When masking a forwarded interrupt, make sure it is 2058c2ecf20Sopenharmony_ci * deactivated as well. 2068c2ecf20Sopenharmony_ci * 2078c2ecf20Sopenharmony_ci * This ensures that an interrupt that is getting 2088c2ecf20Sopenharmony_ci * disabled/masked will not get "stuck", because there is 2098c2ecf20Sopenharmony_ci * noone to deactivate it (guest is being terminated). 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) 2128c2ecf20Sopenharmony_ci gic_poke_irq(d, GIC_DIST_ACTIVE_CLEAR); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void gic_unmask_irq(struct irq_data *d) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci gic_poke_irq(d, GIC_DIST_ENABLE_SET); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void gic_eoi_irq(struct irq_data *d) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u32 hwirq = gic_irq(d); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (hwirq < 16) 2258c2ecf20Sopenharmony_ci hwirq = this_cpu_read(sgi_intid); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci writel_relaxed(hwirq, gic_cpu_base(d) + GIC_CPU_EOI); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void gic_eoimode1_eoi_irq(struct irq_data *d) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci u32 hwirq = gic_irq(d); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Do not deactivate an IRQ forwarded to a vcpu. */ 2358c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) 2368c2ecf20Sopenharmony_ci return; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (hwirq < 16) 2398c2ecf20Sopenharmony_ci hwirq = this_cpu_read(sgi_intid); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci writel_relaxed(hwirq, gic_cpu_base(d) + GIC_CPU_DEACTIVATE); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int gic_irq_set_irqchip_state(struct irq_data *d, 2458c2ecf20Sopenharmony_ci enum irqchip_irq_state which, bool val) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci u32 reg; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci switch (which) { 2508c2ecf20Sopenharmony_ci case IRQCHIP_STATE_PENDING: 2518c2ecf20Sopenharmony_ci reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci case IRQCHIP_STATE_ACTIVE: 2558c2ecf20Sopenharmony_ci reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR; 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci case IRQCHIP_STATE_MASKED: 2598c2ecf20Sopenharmony_ci reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci default: 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci gic_poke_irq(d, reg); 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int gic_irq_get_irqchip_state(struct irq_data *d, 2718c2ecf20Sopenharmony_ci enum irqchip_irq_state which, bool *val) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci switch (which) { 2748c2ecf20Sopenharmony_ci case IRQCHIP_STATE_PENDING: 2758c2ecf20Sopenharmony_ci *val = gic_peek_irq(d, GIC_DIST_PENDING_SET); 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci case IRQCHIP_STATE_ACTIVE: 2798c2ecf20Sopenharmony_ci *val = gic_peek_irq(d, GIC_DIST_ACTIVE_SET); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci case IRQCHIP_STATE_MASKED: 2838c2ecf20Sopenharmony_ci *val = !gic_peek_irq(d, GIC_DIST_ENABLE_SET); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci default: 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int gic_set_type(struct irq_data *d, unsigned int type) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci void __iomem *base = gic_dist_base(d); 2968c2ecf20Sopenharmony_ci unsigned int gicirq = gic_irq(d); 2978c2ecf20Sopenharmony_ci int ret; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Interrupt configuration for SGIs can't be changed */ 3008c2ecf20Sopenharmony_ci if (gicirq < 16) 3018c2ecf20Sopenharmony_ci return type != IRQ_TYPE_EDGE_RISING ? -EINVAL : 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* SPIs have restrictions on the supported types */ 3048c2ecf20Sopenharmony_ci if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && 3058c2ecf20Sopenharmony_ci type != IRQ_TYPE_EDGE_RISING) 3068c2ecf20Sopenharmony_ci return -EINVAL; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = gic_configure_irq(gicirq, type, base + GIC_DIST_CONFIG, NULL); 3098c2ecf20Sopenharmony_ci if (ret && gicirq < 32) { 3108c2ecf20Sopenharmony_ci /* Misconfigured PPIs are usually not fatal */ 3118c2ecf20Sopenharmony_ci pr_warn("GIC: PPI%d is secure or misconfigured\n", gicirq - 16); 3128c2ecf20Sopenharmony_ci ret = 0; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci /* Only interrupts on the primary GIC can be forwarded to a vcpu. */ 3218c2ecf20Sopenharmony_ci if (cascading_gic_irq(d) || gic_irq(d) < 16) 3228c2ecf20Sopenharmony_ci return -EINVAL; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (vcpu) 3258c2ecf20Sopenharmony_ci irqd_set_forwarded_to_vcpu(d); 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci irqd_clr_forwarded_to_vcpu(d); 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int gic_retrigger(struct irq_data *data) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci return !gic_irq_set_irqchip_state(data, IRQCHIP_STATE_PENDING, true); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci u32 irqstat, irqnr; 3398c2ecf20Sopenharmony_ci struct gic_chip_data *gic = &gic_data[0]; 3408c2ecf20Sopenharmony_ci void __iomem *cpu_base = gic_data_cpu_base(gic); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci do { 3438c2ecf20Sopenharmony_ci irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); 3448c2ecf20Sopenharmony_ci irqnr = irqstat & GICC_IAR_INT_ID_MASK; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (unlikely(irqnr >= 1020)) 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (static_branch_likely(&supports_deactivate_key)) 3508c2ecf20Sopenharmony_ci writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); 3518c2ecf20Sopenharmony_ci isb(); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * Ensure any shared data written by the CPU sending the IPI 3558c2ecf20Sopenharmony_ci * is read after we've read the ACK register on the GIC. 3568c2ecf20Sopenharmony_ci * 3578c2ecf20Sopenharmony_ci * Pairs with the write barrier in gic_ipi_send_mask 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci if (irqnr <= 15) { 3608c2ecf20Sopenharmony_ci smp_rmb(); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* 3638c2ecf20Sopenharmony_ci * The GIC encodes the source CPU in GICC_IAR, 3648c2ecf20Sopenharmony_ci * leading to the deactivation to fail if not 3658c2ecf20Sopenharmony_ci * written back as is to GICC_EOI. Stash the INTID 3668c2ecf20Sopenharmony_ci * away for gic_eoi_irq() to write back. This only 3678c2ecf20Sopenharmony_ci * works because we don't nest SGIs... 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci this_cpu_write(sgi_intid, irqstat); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci handle_domain_irq(gic->domain, irqnr, regs); 3738c2ecf20Sopenharmony_ci } while (1); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void gic_handle_cascade_irq(struct irq_desc *desc) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct gic_chip_data *chip_data = irq_desc_get_handler_data(desc); 3798c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 3808c2ecf20Sopenharmony_ci unsigned int cascade_irq, gic_irq; 3818c2ecf20Sopenharmony_ci unsigned long status; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci gic_irq = (status & GICC_IAR_INT_ID_MASK); 3888c2ecf20Sopenharmony_ci if (gic_irq == GICC_INT_SPURIOUS) 3898c2ecf20Sopenharmony_ci goto out; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci cascade_irq = irq_find_mapping(chip_data->domain, gic_irq); 3928c2ecf20Sopenharmony_ci if (unlikely(gic_irq < 32 || gic_irq > 1020)) { 3938c2ecf20Sopenharmony_ci handle_bad_irq(desc); 3948c2ecf20Sopenharmony_ci } else { 3958c2ecf20Sopenharmony_ci isb(); 3968c2ecf20Sopenharmony_ci generic_handle_irq(cascade_irq); 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci out: 4008c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic const struct irq_chip gic_chip = { 4048c2ecf20Sopenharmony_ci .irq_mask = gic_mask_irq, 4058c2ecf20Sopenharmony_ci .irq_unmask = gic_unmask_irq, 4068c2ecf20Sopenharmony_ci .irq_eoi = gic_eoi_irq, 4078c2ecf20Sopenharmony_ci .irq_set_type = gic_set_type, 4088c2ecf20Sopenharmony_ci .irq_retrigger = gic_retrigger, 4098c2ecf20Sopenharmony_ci .irq_get_irqchip_state = gic_irq_get_irqchip_state, 4108c2ecf20Sopenharmony_ci .irq_set_irqchip_state = gic_irq_set_irqchip_state, 4118c2ecf20Sopenharmony_ci .flags = IRQCHIP_SET_TYPE_MASKED | 4128c2ecf20Sopenharmony_ci IRQCHIP_SKIP_SET_WAKE | 4138c2ecf20Sopenharmony_ci IRQCHIP_MASK_ON_SUSPEND, 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_civoid __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); 4198c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, 4208c2ecf20Sopenharmony_ci &gic_data[gic_nr]); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic u8 gic_get_cpumask(struct gic_chip_data *gic) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci void __iomem *base = gic_data_dist_base(gic); 4268c2ecf20Sopenharmony_ci u32 mask, i; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci for (i = mask = 0; i < 32; i += 4) { 4298c2ecf20Sopenharmony_ci mask = readl_relaxed(base + GIC_DIST_TARGET + i); 4308c2ecf20Sopenharmony_ci mask |= mask >> 16; 4318c2ecf20Sopenharmony_ci mask |= mask >> 8; 4328c2ecf20Sopenharmony_ci if (mask) 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (!mask && num_possible_cpus() > 1) 4378c2ecf20Sopenharmony_ci pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return mask; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic bool gic_check_gicv2(void __iomem *base) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci u32 val = readl_relaxed(base + GIC_CPU_IDENT); 4458c2ecf20Sopenharmony_ci return (val & 0xff0fff) == 0x02043B; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void gic_cpu_if_up(struct gic_chip_data *gic) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci void __iomem *cpu_base = gic_data_cpu_base(gic); 4518c2ecf20Sopenharmony_ci u32 bypass = 0; 4528c2ecf20Sopenharmony_ci u32 mode = 0; 4538c2ecf20Sopenharmony_ci int i; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (gic == &gic_data[0] && static_branch_likely(&supports_deactivate_key)) 4568c2ecf20Sopenharmony_ci mode = GIC_CPU_CTRL_EOImodeNS; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (gic_check_gicv2(cpu_base)) 4598c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 4608c2ecf20Sopenharmony_ci writel_relaxed(0, cpu_base + GIC_CPU_ACTIVEPRIO + i * 4); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * Preserve bypass disable bits to be written back later 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci bypass = readl(cpu_base + GIC_CPU_CTRL); 4668c2ecf20Sopenharmony_ci bypass &= GICC_DIS_BYPASS_MASK; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci writel_relaxed(bypass | mode | GICC_ENABLE, cpu_base + GIC_CPU_CTRL); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void gic_dist_init(struct gic_chip_data *gic) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci unsigned int i; 4758c2ecf20Sopenharmony_ci u32 cpumask; 4768c2ecf20Sopenharmony_ci unsigned int gic_irqs = gic->gic_irqs; 4778c2ecf20Sopenharmony_ci void __iomem *base = gic_data_dist_base(gic); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci writel_relaxed(GICD_DISABLE, base + GIC_DIST_CTRL); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* 4828c2ecf20Sopenharmony_ci * Set all global interrupts to this CPU only. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci cpumask = gic_get_cpumask(gic); 4858c2ecf20Sopenharmony_ci cpumask |= cpumask << 8; 4868c2ecf20Sopenharmony_ci cpumask |= cpumask << 16; 4878c2ecf20Sopenharmony_ci for (i = 32; i < gic_irqs; i += 4) 4888c2ecf20Sopenharmony_ci writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci gic_dist_config(base, gic_irqs, NULL); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int gic_cpu_init(struct gic_chip_data *gic) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci void __iomem *dist_base = gic_data_dist_base(gic); 4988c2ecf20Sopenharmony_ci void __iomem *base = gic_data_cpu_base(gic); 4998c2ecf20Sopenharmony_ci unsigned int cpu_mask, cpu = smp_processor_id(); 5008c2ecf20Sopenharmony_ci int i; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* 5038c2ecf20Sopenharmony_ci * Setting up the CPU map is only relevant for the primary GIC 5048c2ecf20Sopenharmony_ci * because any nested/secondary GICs do not directly interface 5058c2ecf20Sopenharmony_ci * with the CPU(s). 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci if (gic == &gic_data[0]) { 5088c2ecf20Sopenharmony_ci /* 5098c2ecf20Sopenharmony_ci * Get what the GIC says our CPU mask is. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci if (WARN_ON(cpu >= NR_GIC_CPU_IF)) 5128c2ecf20Sopenharmony_ci return -EINVAL; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci gic_check_cpu_features(); 5158c2ecf20Sopenharmony_ci cpu_mask = gic_get_cpumask(gic); 5168c2ecf20Sopenharmony_ci gic_cpu_map[cpu] = cpu_mask; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * Clear our mask from the other map entries in case they're 5208c2ecf20Sopenharmony_ci * still undefined. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci for (i = 0; i < NR_GIC_CPU_IF; i++) 5238c2ecf20Sopenharmony_ci if (i != cpu) 5248c2ecf20Sopenharmony_ci gic_cpu_map[i] &= ~cpu_mask; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci gic_cpu_config(dist_base, 32, NULL); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK); 5308c2ecf20Sopenharmony_ci gic_cpu_if_up(gic); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ciint gic_cpu_if_down(unsigned int gic_nr) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci void __iomem *cpu_base; 5388c2ecf20Sopenharmony_ci u32 val = 0; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (gic_nr >= CONFIG_ARM_GIC_MAX_NR) 5418c2ecf20Sopenharmony_ci return -EINVAL; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); 5448c2ecf20Sopenharmony_ci val = readl(cpu_base + GIC_CPU_CTRL); 5458c2ecf20Sopenharmony_ci val &= ~GICC_ENABLE; 5468c2ecf20Sopenharmony_ci writel_relaxed(val, cpu_base + GIC_CPU_CTRL); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci#if defined(CONFIG_CPU_PM) || defined(CONFIG_ARM_GIC_PM) 5528c2ecf20Sopenharmony_ci/* 5538c2ecf20Sopenharmony_ci * Saves the GIC distributor registers during suspend or idle. Must be called 5548c2ecf20Sopenharmony_ci * with interrupts disabled but before powering down the GIC. After calling 5558c2ecf20Sopenharmony_ci * this function, no interrupts will be delivered by the GIC, and another 5568c2ecf20Sopenharmony_ci * platform-specific wakeup source must be enabled. 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_civoid gic_dist_save(struct gic_chip_data *gic) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci unsigned int gic_irqs; 5618c2ecf20Sopenharmony_ci void __iomem *dist_base; 5628c2ecf20Sopenharmony_ci int i; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (WARN_ON(!gic)) 5658c2ecf20Sopenharmony_ci return; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci gic_irqs = gic->gic_irqs; 5688c2ecf20Sopenharmony_ci dist_base = gic_data_dist_base(gic); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (!dist_base) 5718c2ecf20Sopenharmony_ci return; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) 5748c2ecf20Sopenharmony_ci gic->saved_spi_conf[i] = 5758c2ecf20Sopenharmony_ci readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) 5788c2ecf20Sopenharmony_ci gic->saved_spi_target[i] = 5798c2ecf20Sopenharmony_ci readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) 5828c2ecf20Sopenharmony_ci gic->saved_spi_enable[i] = 5838c2ecf20Sopenharmony_ci readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) 5868c2ecf20Sopenharmony_ci gic->saved_spi_active[i] = 5878c2ecf20Sopenharmony_ci readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* 5918c2ecf20Sopenharmony_ci * Restores the GIC distributor registers during resume or when coming out of 5928c2ecf20Sopenharmony_ci * idle. Must be called before enabling interrupts. If a level interrupt 5938c2ecf20Sopenharmony_ci * that occurred while the GIC was suspended is still present, it will be 5948c2ecf20Sopenharmony_ci * handled normally, but any edge interrupts that occurred will not be seen by 5958c2ecf20Sopenharmony_ci * the GIC and need to be handled by the platform-specific wakeup source. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_civoid gic_dist_restore(struct gic_chip_data *gic) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci unsigned int gic_irqs; 6008c2ecf20Sopenharmony_ci unsigned int i; 6018c2ecf20Sopenharmony_ci void __iomem *dist_base; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (WARN_ON(!gic)) 6048c2ecf20Sopenharmony_ci return; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci gic_irqs = gic->gic_irqs; 6078c2ecf20Sopenharmony_ci dist_base = gic_data_dist_base(gic); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (!dist_base) 6108c2ecf20Sopenharmony_ci return; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci writel_relaxed(GICD_DISABLE, dist_base + GIC_DIST_CTRL); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) 6158c2ecf20Sopenharmony_ci writel_relaxed(gic->saved_spi_conf[i], 6168c2ecf20Sopenharmony_ci dist_base + GIC_DIST_CONFIG + i * 4); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) 6198c2ecf20Sopenharmony_ci writel_relaxed(GICD_INT_DEF_PRI_X4, 6208c2ecf20Sopenharmony_ci dist_base + GIC_DIST_PRI + i * 4); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) 6238c2ecf20Sopenharmony_ci writel_relaxed(gic->saved_spi_target[i], 6248c2ecf20Sopenharmony_ci dist_base + GIC_DIST_TARGET + i * 4); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) { 6278c2ecf20Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 6288c2ecf20Sopenharmony_ci dist_base + GIC_DIST_ENABLE_CLEAR + i * 4); 6298c2ecf20Sopenharmony_ci writel_relaxed(gic->saved_spi_enable[i], 6308c2ecf20Sopenharmony_ci dist_base + GIC_DIST_ENABLE_SET + i * 4); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) { 6348c2ecf20Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 6358c2ecf20Sopenharmony_ci dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4); 6368c2ecf20Sopenharmony_ci writel_relaxed(gic->saved_spi_active[i], 6378c2ecf20Sopenharmony_ci dist_base + GIC_DIST_ACTIVE_SET + i * 4); 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_civoid gic_cpu_save(struct gic_chip_data *gic) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci int i; 6468c2ecf20Sopenharmony_ci u32 *ptr; 6478c2ecf20Sopenharmony_ci void __iomem *dist_base; 6488c2ecf20Sopenharmony_ci void __iomem *cpu_base; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (WARN_ON(!gic)) 6518c2ecf20Sopenharmony_ci return; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci dist_base = gic_data_dist_base(gic); 6548c2ecf20Sopenharmony_ci cpu_base = gic_data_cpu_base(gic); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (!dist_base || !cpu_base) 6578c2ecf20Sopenharmony_ci return; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci ptr = raw_cpu_ptr(gic->saved_ppi_enable); 6608c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(32, 32); i++) 6618c2ecf20Sopenharmony_ci ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci ptr = raw_cpu_ptr(gic->saved_ppi_active); 6648c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(32, 32); i++) 6658c2ecf20Sopenharmony_ci ptr[i] = readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci ptr = raw_cpu_ptr(gic->saved_ppi_conf); 6688c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(32, 16); i++) 6698c2ecf20Sopenharmony_ci ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_civoid gic_cpu_restore(struct gic_chip_data *gic) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci int i; 6768c2ecf20Sopenharmony_ci u32 *ptr; 6778c2ecf20Sopenharmony_ci void __iomem *dist_base; 6788c2ecf20Sopenharmony_ci void __iomem *cpu_base; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (WARN_ON(!gic)) 6818c2ecf20Sopenharmony_ci return; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci dist_base = gic_data_dist_base(gic); 6848c2ecf20Sopenharmony_ci cpu_base = gic_data_cpu_base(gic); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (!dist_base || !cpu_base) 6878c2ecf20Sopenharmony_ci return; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci ptr = raw_cpu_ptr(gic->saved_ppi_enable); 6908c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(32, 32); i++) { 6918c2ecf20Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 6928c2ecf20Sopenharmony_ci dist_base + GIC_DIST_ENABLE_CLEAR + i * 4); 6938c2ecf20Sopenharmony_ci writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci ptr = raw_cpu_ptr(gic->saved_ppi_active); 6978c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(32, 32); i++) { 6988c2ecf20Sopenharmony_ci writel_relaxed(GICD_INT_EN_CLR_X32, 6998c2ecf20Sopenharmony_ci dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4); 7008c2ecf20Sopenharmony_ci writel_relaxed(ptr[i], dist_base + GIC_DIST_ACTIVE_SET + i * 4); 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci ptr = raw_cpu_ptr(gic->saved_ppi_conf); 7048c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(32, 16); i++) 7058c2ecf20Sopenharmony_ci writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(32, 4); i++) 7088c2ecf20Sopenharmony_ci writel_relaxed(GICD_INT_DEF_PRI_X4, 7098c2ecf20Sopenharmony_ci dist_base + GIC_DIST_PRI + i * 4); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK); 7128c2ecf20Sopenharmony_ci gic_cpu_if_up(gic); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci int i; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci for (i = 0; i < CONFIG_ARM_GIC_MAX_NR; i++) { 7208c2ecf20Sopenharmony_ci switch (cmd) { 7218c2ecf20Sopenharmony_ci case CPU_PM_ENTER: 7228c2ecf20Sopenharmony_ci gic_cpu_save(&gic_data[i]); 7238c2ecf20Sopenharmony_ci break; 7248c2ecf20Sopenharmony_ci case CPU_PM_ENTER_FAILED: 7258c2ecf20Sopenharmony_ci case CPU_PM_EXIT: 7268c2ecf20Sopenharmony_ci gic_cpu_restore(&gic_data[i]); 7278c2ecf20Sopenharmony_ci break; 7288c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 7298c2ecf20Sopenharmony_ci gic_dist_save(&gic_data[i]); 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER_FAILED: 7328c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 7338c2ecf20Sopenharmony_ci gic_dist_restore(&gic_data[i]); 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return NOTIFY_OK; 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic struct notifier_block gic_notifier_block = { 7428c2ecf20Sopenharmony_ci .notifier_call = gic_notifier, 7438c2ecf20Sopenharmony_ci}; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic int gic_pm_init(struct gic_chip_data *gic) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, 7488c2ecf20Sopenharmony_ci sizeof(u32)); 7498c2ecf20Sopenharmony_ci if (WARN_ON(!gic->saved_ppi_enable)) 7508c2ecf20Sopenharmony_ci return -ENOMEM; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci gic->saved_ppi_active = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, 7538c2ecf20Sopenharmony_ci sizeof(u32)); 7548c2ecf20Sopenharmony_ci if (WARN_ON(!gic->saved_ppi_active)) 7558c2ecf20Sopenharmony_ci goto free_ppi_enable; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4, 7588c2ecf20Sopenharmony_ci sizeof(u32)); 7598c2ecf20Sopenharmony_ci if (WARN_ON(!gic->saved_ppi_conf)) 7608c2ecf20Sopenharmony_ci goto free_ppi_active; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (gic == &gic_data[0]) 7638c2ecf20Sopenharmony_ci cpu_pm_register_notifier(&gic_notifier_block); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return 0; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cifree_ppi_active: 7688c2ecf20Sopenharmony_ci free_percpu(gic->saved_ppi_active); 7698c2ecf20Sopenharmony_cifree_ppi_enable: 7708c2ecf20Sopenharmony_ci free_percpu(gic->saved_ppi_enable); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci return -ENOMEM; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci#else 7758c2ecf20Sopenharmony_cistatic int gic_pm_init(struct gic_chip_data *gic) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci#endif 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 7828c2ecf20Sopenharmony_cistatic void rmw_writeb(u8 bval, void __iomem *addr) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci static DEFINE_RAW_SPINLOCK(rmw_lock); 7858c2ecf20Sopenharmony_ci unsigned long offset = (unsigned long)addr & 3UL; 7868c2ecf20Sopenharmony_ci unsigned long shift = offset * 8; 7878c2ecf20Sopenharmony_ci unsigned long flags; 7888c2ecf20Sopenharmony_ci u32 val; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&rmw_lock, flags); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci addr -= offset; 7938c2ecf20Sopenharmony_ci val = readl_relaxed(addr); 7948c2ecf20Sopenharmony_ci val &= ~GENMASK(shift + 7, shift); 7958c2ecf20Sopenharmony_ci val |= bval << shift; 7968c2ecf20Sopenharmony_ci writel_relaxed(val, addr); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&rmw_lock, flags); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, 8028c2ecf20Sopenharmony_ci bool force) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + gic_irq(d); 8058c2ecf20Sopenharmony_ci unsigned int cpu; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (!force) 8088c2ecf20Sopenharmony_ci cpu = cpumask_any_and(mask_val, cpu_online_mask); 8098c2ecf20Sopenharmony_ci else 8108c2ecf20Sopenharmony_ci cpu = cpumask_first(mask_val); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids) 8138c2ecf20Sopenharmony_ci return -EINVAL; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (static_branch_unlikely(&needs_rmw_access)) 8168c2ecf20Sopenharmony_ci rmw_writeb(gic_cpu_map[cpu], reg); 8178c2ecf20Sopenharmony_ci else 8188c2ecf20Sopenharmony_ci writeb_relaxed(gic_cpu_map[cpu], reg); 8198c2ecf20Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(cpu)); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK_DONE; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci int cpu; 8278c2ecf20Sopenharmony_ci unsigned long flags, map = 0; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (unlikely(nr_cpu_ids == 1)) { 8308c2ecf20Sopenharmony_ci /* Only one CPU? let's do a self-IPI... */ 8318c2ecf20Sopenharmony_ci writel_relaxed(2 << 24 | d->hwirq, 8328c2ecf20Sopenharmony_ci gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); 8338c2ecf20Sopenharmony_ci return; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci gic_lock_irqsave(flags); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci /* Convert our logical CPU mask into a physical one. */ 8398c2ecf20Sopenharmony_ci for_each_cpu(cpu, mask) 8408c2ecf20Sopenharmony_ci map |= gic_cpu_map[cpu]; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* 8438c2ecf20Sopenharmony_ci * Ensure that stores to Normal memory are visible to the 8448c2ecf20Sopenharmony_ci * other CPUs before they observe us issuing the IPI. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci dmb(ishst); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* this always happens on GIC0 */ 8498c2ecf20Sopenharmony_ci writel_relaxed(map << 16 | d->hwirq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci gic_unlock_irqrestore(flags); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int gic_starting_cpu(unsigned int cpu) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci gic_cpu_init(&gic_data[0]); 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic __init void gic_smp_init(void) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct irq_fwspec sgi_fwspec = { 8638c2ecf20Sopenharmony_ci .fwnode = gic_data[0].domain->fwnode, 8648c2ecf20Sopenharmony_ci .param_count = 1, 8658c2ecf20Sopenharmony_ci }; 8668c2ecf20Sopenharmony_ci int base_sgi; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, 8698c2ecf20Sopenharmony_ci "irqchip/arm/gic:starting", 8708c2ecf20Sopenharmony_ci gic_starting_cpu, NULL); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci base_sgi = __irq_domain_alloc_irqs(gic_data[0].domain, -1, 8, 8738c2ecf20Sopenharmony_ci NUMA_NO_NODE, &sgi_fwspec, 8748c2ecf20Sopenharmony_ci false, NULL); 8758c2ecf20Sopenharmony_ci if (WARN_ON(base_sgi <= 0)) 8768c2ecf20Sopenharmony_ci return; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci set_smp_ipi_range(base_sgi, 8); 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci#else 8818c2ecf20Sopenharmony_ci#define gic_smp_init() do { } while(0) 8828c2ecf20Sopenharmony_ci#define gic_set_affinity NULL 8838c2ecf20Sopenharmony_ci#define gic_ipi_send_mask NULL 8848c2ecf20Sopenharmony_ci#endif 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci#ifdef CONFIG_BL_SWITCHER 8878c2ecf20Sopenharmony_ci/* 8888c2ecf20Sopenharmony_ci * gic_send_sgi - send a SGI directly to given CPU interface number 8898c2ecf20Sopenharmony_ci * 8908c2ecf20Sopenharmony_ci * cpu_id: the ID for the destination CPU interface 8918c2ecf20Sopenharmony_ci * irq: the IPI number to send a SGI for 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_civoid gic_send_sgi(unsigned int cpu_id, unsigned int irq) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci BUG_ON(cpu_id >= NR_GIC_CPU_IF); 8968c2ecf20Sopenharmony_ci cpu_id = 1 << cpu_id; 8978c2ecf20Sopenharmony_ci /* this always happens on GIC0 */ 8988c2ecf20Sopenharmony_ci writel_relaxed((cpu_id << 16) | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci/* 9028c2ecf20Sopenharmony_ci * gic_get_cpu_id - get the CPU interface ID for the specified CPU 9038c2ecf20Sopenharmony_ci * 9048c2ecf20Sopenharmony_ci * @cpu: the logical CPU number to get the GIC ID for. 9058c2ecf20Sopenharmony_ci * 9068c2ecf20Sopenharmony_ci * Return the CPU interface ID for the given logical CPU number, 9078c2ecf20Sopenharmony_ci * or -1 if the CPU number is too large or the interface ID is 9088c2ecf20Sopenharmony_ci * unknown (more than one bit set). 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_ciint gic_get_cpu_id(unsigned int cpu) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci unsigned int cpu_bit; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (cpu >= NR_GIC_CPU_IF) 9158c2ecf20Sopenharmony_ci return -1; 9168c2ecf20Sopenharmony_ci cpu_bit = gic_cpu_map[cpu]; 9178c2ecf20Sopenharmony_ci if (cpu_bit & (cpu_bit - 1)) 9188c2ecf20Sopenharmony_ci return -1; 9198c2ecf20Sopenharmony_ci return __ffs(cpu_bit); 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci/* 9238c2ecf20Sopenharmony_ci * gic_migrate_target - migrate IRQs to another CPU interface 9248c2ecf20Sopenharmony_ci * 9258c2ecf20Sopenharmony_ci * @new_cpu_id: the CPU target ID to migrate IRQs to 9268c2ecf20Sopenharmony_ci * 9278c2ecf20Sopenharmony_ci * Migrate all peripheral interrupts with a target matching the current CPU 9288c2ecf20Sopenharmony_ci * to the interface corresponding to @new_cpu_id. The CPU interface mapping 9298c2ecf20Sopenharmony_ci * is also updated. Targets to other CPU interfaces are unchanged. 9308c2ecf20Sopenharmony_ci * This must be called with IRQs locally disabled. 9318c2ecf20Sopenharmony_ci */ 9328c2ecf20Sopenharmony_civoid gic_migrate_target(unsigned int new_cpu_id) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci unsigned int cur_cpu_id, gic_irqs, gic_nr = 0; 9358c2ecf20Sopenharmony_ci void __iomem *dist_base; 9368c2ecf20Sopenharmony_ci int i, ror_val, cpu = smp_processor_id(); 9378c2ecf20Sopenharmony_ci u32 val, cur_target_mask, active_mask; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci dist_base = gic_data_dist_base(&gic_data[gic_nr]); 9428c2ecf20Sopenharmony_ci if (!dist_base) 9438c2ecf20Sopenharmony_ci return; 9448c2ecf20Sopenharmony_ci gic_irqs = gic_data[gic_nr].gic_irqs; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci cur_cpu_id = __ffs(gic_cpu_map[cpu]); 9478c2ecf20Sopenharmony_ci cur_target_mask = 0x01010101 << cur_cpu_id; 9488c2ecf20Sopenharmony_ci ror_val = (cur_cpu_id - new_cpu_id) & 31; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci gic_lock(); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* Update the target interface for this logical CPU */ 9538c2ecf20Sopenharmony_ci gic_cpu_map[cpu] = 1 << new_cpu_id; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * Find all the peripheral interrupts targeting the current 9578c2ecf20Sopenharmony_ci * CPU interface and migrate them to the new CPU interface. 9588c2ecf20Sopenharmony_ci * We skip DIST_TARGET 0 to 7 as they are read-only. 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci for (i = 8; i < DIV_ROUND_UP(gic_irqs, 4); i++) { 9618c2ecf20Sopenharmony_ci val = readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); 9628c2ecf20Sopenharmony_ci active_mask = val & cur_target_mask; 9638c2ecf20Sopenharmony_ci if (active_mask) { 9648c2ecf20Sopenharmony_ci val &= ~active_mask; 9658c2ecf20Sopenharmony_ci val |= ror32(active_mask, ror_val); 9668c2ecf20Sopenharmony_ci writel_relaxed(val, dist_base + GIC_DIST_TARGET + i*4); 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci gic_unlock(); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* 9738c2ecf20Sopenharmony_ci * Now let's migrate and clear any potential SGIs that might be 9748c2ecf20Sopenharmony_ci * pending for us (cur_cpu_id). Since GIC_DIST_SGI_PENDING_SET 9758c2ecf20Sopenharmony_ci * is a banked register, we can only forward the SGI using 9768c2ecf20Sopenharmony_ci * GIC_DIST_SOFTINT. The original SGI source is lost but Linux 9778c2ecf20Sopenharmony_ci * doesn't use that information anyway. 9788c2ecf20Sopenharmony_ci * 9798c2ecf20Sopenharmony_ci * For the same reason we do not adjust SGI source information 9808c2ecf20Sopenharmony_ci * for previously sent SGIs by us to other CPUs either. 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_ci for (i = 0; i < 16; i += 4) { 9838c2ecf20Sopenharmony_ci int j; 9848c2ecf20Sopenharmony_ci val = readl_relaxed(dist_base + GIC_DIST_SGI_PENDING_SET + i); 9858c2ecf20Sopenharmony_ci if (!val) 9868c2ecf20Sopenharmony_ci continue; 9878c2ecf20Sopenharmony_ci writel_relaxed(val, dist_base + GIC_DIST_SGI_PENDING_CLEAR + i); 9888c2ecf20Sopenharmony_ci for (j = i; j < i + 4; j++) { 9898c2ecf20Sopenharmony_ci if (val & 0xff) 9908c2ecf20Sopenharmony_ci writel_relaxed((1 << (new_cpu_id + 16)) | j, 9918c2ecf20Sopenharmony_ci dist_base + GIC_DIST_SOFTINT); 9928c2ecf20Sopenharmony_ci val >>= 8; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci/* 9988c2ecf20Sopenharmony_ci * gic_get_sgir_physaddr - get the physical address for the SGI register 9998c2ecf20Sopenharmony_ci * 10008c2ecf20Sopenharmony_ci * REturn the physical address of the SGI register to be used 10018c2ecf20Sopenharmony_ci * by some early assembly code when the kernel is not yet available. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_cistatic unsigned long gic_dist_physaddr; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ciunsigned long gic_get_sgir_physaddr(void) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci if (!gic_dist_physaddr) 10088c2ecf20Sopenharmony_ci return 0; 10098c2ecf20Sopenharmony_ci return gic_dist_physaddr + GIC_DIST_SOFTINT; 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic void __init gic_init_physaddr(struct device_node *node) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct resource res; 10158c2ecf20Sopenharmony_ci if (of_address_to_resource(node, 0, &res) == 0) { 10168c2ecf20Sopenharmony_ci gic_dist_physaddr = res.start; 10178c2ecf20Sopenharmony_ci pr_info("GIC physical location is %#lx\n", gic_dist_physaddr); 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci#else 10228c2ecf20Sopenharmony_ci#define gic_init_physaddr(node) do { } while (0) 10238c2ecf20Sopenharmony_ci#endif 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, 10268c2ecf20Sopenharmony_ci irq_hw_number_t hw) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct gic_chip_data *gic = d->host_data; 10298c2ecf20Sopenharmony_ci struct irq_data *irqd = irq_desc_get_irq_data(irq_to_desc(irq)); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci switch (hw) { 10328c2ecf20Sopenharmony_ci case 0 ... 15: 10338c2ecf20Sopenharmony_ci irq_set_percpu_devid(irq); 10348c2ecf20Sopenharmony_ci irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, 10358c2ecf20Sopenharmony_ci handle_percpu_devid_fasteoi_ipi, 10368c2ecf20Sopenharmony_ci NULL, NULL); 10378c2ecf20Sopenharmony_ci break; 10388c2ecf20Sopenharmony_ci case 16 ... 31: 10398c2ecf20Sopenharmony_ci irq_set_percpu_devid(irq); 10408c2ecf20Sopenharmony_ci irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, 10418c2ecf20Sopenharmony_ci handle_percpu_devid_irq, NULL, NULL); 10428c2ecf20Sopenharmony_ci break; 10438c2ecf20Sopenharmony_ci default: 10448c2ecf20Sopenharmony_ci irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, 10458c2ecf20Sopenharmony_ci handle_fasteoi_irq, NULL, NULL); 10468c2ecf20Sopenharmony_ci irq_set_probe(irq); 10478c2ecf20Sopenharmony_ci irqd_set_single_target(irqd); 10488c2ecf20Sopenharmony_ci break; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* Prevents SW retriggers which mess up the ACK/EOI ordering */ 10528c2ecf20Sopenharmony_ci irqd_set_handle_enforce_irqctx(irqd); 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic int gic_irq_domain_translate(struct irq_domain *d, 10618c2ecf20Sopenharmony_ci struct irq_fwspec *fwspec, 10628c2ecf20Sopenharmony_ci unsigned long *hwirq, 10638c2ecf20Sopenharmony_ci unsigned int *type) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci if (fwspec->param_count == 1 && fwspec->param[0] < 16) { 10668c2ecf20Sopenharmony_ci *hwirq = fwspec->param[0]; 10678c2ecf20Sopenharmony_ci *type = IRQ_TYPE_EDGE_RISING; 10688c2ecf20Sopenharmony_ci return 0; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci if (is_of_node(fwspec->fwnode)) { 10728c2ecf20Sopenharmony_ci if (fwspec->param_count < 3) 10738c2ecf20Sopenharmony_ci return -EINVAL; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci switch (fwspec->param[0]) { 10768c2ecf20Sopenharmony_ci case 0: /* SPI */ 10778c2ecf20Sopenharmony_ci *hwirq = fwspec->param[1] + 32; 10788c2ecf20Sopenharmony_ci break; 10798c2ecf20Sopenharmony_ci case 1: /* PPI */ 10808c2ecf20Sopenharmony_ci *hwirq = fwspec->param[1] + 16; 10818c2ecf20Sopenharmony_ci break; 10828c2ecf20Sopenharmony_ci default: 10838c2ecf20Sopenharmony_ci return -EINVAL; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Make it clear that broken DTs are... broken */ 10898c2ecf20Sopenharmony_ci WARN_ON(*type == IRQ_TYPE_NONE); 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (is_fwnode_irqchip(fwspec->fwnode)) { 10948c2ecf20Sopenharmony_ci if(fwspec->param_count != 2) 10958c2ecf20Sopenharmony_ci return -EINVAL; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (fwspec->param[0] < 16) { 10988c2ecf20Sopenharmony_ci pr_err(FW_BUG "Illegal GSI%d translation request\n", 10998c2ecf20Sopenharmony_ci fwspec->param[0]); 11008c2ecf20Sopenharmony_ci return -EINVAL; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci *hwirq = fwspec->param[0]; 11048c2ecf20Sopenharmony_ci *type = fwspec->param[1]; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci WARN_ON(*type == IRQ_TYPE_NONE); 11078c2ecf20Sopenharmony_ci return 0; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci return -EINVAL; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 11148c2ecf20Sopenharmony_ci unsigned int nr_irqs, void *arg) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci int i, ret; 11178c2ecf20Sopenharmony_ci irq_hw_number_t hwirq; 11188c2ecf20Sopenharmony_ci unsigned int type = IRQ_TYPE_NONE; 11198c2ecf20Sopenharmony_ci struct irq_fwspec *fwspec = arg; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type); 11228c2ecf20Sopenharmony_ci if (ret) 11238c2ecf20Sopenharmony_ci return ret; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) { 11268c2ecf20Sopenharmony_ci ret = gic_irq_domain_map(domain, virq + i, hwirq + i); 11278c2ecf20Sopenharmony_ci if (ret) 11288c2ecf20Sopenharmony_ci return ret; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci return 0; 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_cistatic const struct irq_domain_ops gic_irq_domain_hierarchy_ops = { 11358c2ecf20Sopenharmony_ci .translate = gic_irq_domain_translate, 11368c2ecf20Sopenharmony_ci .alloc = gic_irq_domain_alloc, 11378c2ecf20Sopenharmony_ci .free = irq_domain_free_irqs_top, 11388c2ecf20Sopenharmony_ci}; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic const struct irq_domain_ops gic_irq_domain_ops = { 11418c2ecf20Sopenharmony_ci .map = gic_irq_domain_map, 11428c2ecf20Sopenharmony_ci .unmap = gic_irq_domain_unmap, 11438c2ecf20Sopenharmony_ci}; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic void gic_init_chip(struct gic_chip_data *gic, struct device *dev, 11468c2ecf20Sopenharmony_ci const char *name, bool use_eoimode1) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci /* Initialize irq_chip */ 11498c2ecf20Sopenharmony_ci gic->chip = gic_chip; 11508c2ecf20Sopenharmony_ci gic->chip.name = name; 11518c2ecf20Sopenharmony_ci gic->chip.parent_device = dev; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (use_eoimode1) { 11548c2ecf20Sopenharmony_ci gic->chip.irq_mask = gic_eoimode1_mask_irq; 11558c2ecf20Sopenharmony_ci gic->chip.irq_eoi = gic_eoimode1_eoi_irq; 11568c2ecf20Sopenharmony_ci gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (gic == &gic_data[0]) { 11608c2ecf20Sopenharmony_ci gic->chip.irq_set_affinity = gic_set_affinity; 11618c2ecf20Sopenharmony_ci gic->chip.ipi_send_mask = gic_ipi_send_mask; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic int gic_init_bases(struct gic_chip_data *gic, 11668c2ecf20Sopenharmony_ci struct fwnode_handle *handle) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci int gic_irqs, ret; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { 11718c2ecf20Sopenharmony_ci /* Frankein-GIC without banked registers... */ 11728c2ecf20Sopenharmony_ci unsigned int cpu; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci gic->dist_base.percpu_base = alloc_percpu(void __iomem *); 11758c2ecf20Sopenharmony_ci gic->cpu_base.percpu_base = alloc_percpu(void __iomem *); 11768c2ecf20Sopenharmony_ci if (WARN_ON(!gic->dist_base.percpu_base || 11778c2ecf20Sopenharmony_ci !gic->cpu_base.percpu_base)) { 11788c2ecf20Sopenharmony_ci ret = -ENOMEM; 11798c2ecf20Sopenharmony_ci goto error; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 11838c2ecf20Sopenharmony_ci u32 mpidr = cpu_logical_map(cpu); 11848c2ecf20Sopenharmony_ci u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); 11858c2ecf20Sopenharmony_ci unsigned long offset = gic->percpu_offset * core_id; 11868c2ecf20Sopenharmony_ci *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = 11878c2ecf20Sopenharmony_ci gic->raw_dist_base + offset; 11888c2ecf20Sopenharmony_ci *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = 11898c2ecf20Sopenharmony_ci gic->raw_cpu_base + offset; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci enable_frankengic(); 11938c2ecf20Sopenharmony_ci } else { 11948c2ecf20Sopenharmony_ci /* Normal, sane GIC... */ 11958c2ecf20Sopenharmony_ci WARN(gic->percpu_offset, 11968c2ecf20Sopenharmony_ci "GIC_NON_BANKED not enabled, ignoring %08x offset!", 11978c2ecf20Sopenharmony_ci gic->percpu_offset); 11988c2ecf20Sopenharmony_ci gic->dist_base.common_base = gic->raw_dist_base; 11998c2ecf20Sopenharmony_ci gic->cpu_base.common_base = gic->raw_cpu_base; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* 12038c2ecf20Sopenharmony_ci * Find out how many interrupts are supported. 12048c2ecf20Sopenharmony_ci * The GIC only supports up to 1020 interrupt sources. 12058c2ecf20Sopenharmony_ci */ 12068c2ecf20Sopenharmony_ci gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f; 12078c2ecf20Sopenharmony_ci gic_irqs = (gic_irqs + 1) * 32; 12088c2ecf20Sopenharmony_ci if (gic_irqs > 1020) 12098c2ecf20Sopenharmony_ci gic_irqs = 1020; 12108c2ecf20Sopenharmony_ci gic->gic_irqs = gic_irqs; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (handle) { /* DT/ACPI */ 12138c2ecf20Sopenharmony_ci gic->domain = irq_domain_create_linear(handle, gic_irqs, 12148c2ecf20Sopenharmony_ci &gic_irq_domain_hierarchy_ops, 12158c2ecf20Sopenharmony_ci gic); 12168c2ecf20Sopenharmony_ci } else { /* Legacy support */ 12178c2ecf20Sopenharmony_ci /* 12188c2ecf20Sopenharmony_ci * For primary GICs, skip over SGIs. 12198c2ecf20Sopenharmony_ci * No secondary GIC support whatsoever. 12208c2ecf20Sopenharmony_ci */ 12218c2ecf20Sopenharmony_ci int irq_base; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci gic_irqs -= 16; /* calculate # of irqs to allocate */ 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci irq_base = irq_alloc_descs(16, 16, gic_irqs, 12268c2ecf20Sopenharmony_ci numa_node_id()); 12278c2ecf20Sopenharmony_ci if (irq_base < 0) { 12288c2ecf20Sopenharmony_ci WARN(1, "Cannot allocate irq_descs @ IRQ16, assuming pre-allocated\n"); 12298c2ecf20Sopenharmony_ci irq_base = 16; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci gic->domain = irq_domain_add_legacy(NULL, gic_irqs, irq_base, 12338c2ecf20Sopenharmony_ci 16, &gic_irq_domain_ops, gic); 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (WARN_ON(!gic->domain)) { 12378c2ecf20Sopenharmony_ci ret = -ENODEV; 12388c2ecf20Sopenharmony_ci goto error; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci gic_dist_init(gic); 12428c2ecf20Sopenharmony_ci ret = gic_cpu_init(gic); 12438c2ecf20Sopenharmony_ci if (ret) 12448c2ecf20Sopenharmony_ci goto error; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci ret = gic_pm_init(gic); 12478c2ecf20Sopenharmony_ci if (ret) 12488c2ecf20Sopenharmony_ci goto error; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci return 0; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cierror: 12538c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) { 12548c2ecf20Sopenharmony_ci free_percpu(gic->dist_base.percpu_base); 12558c2ecf20Sopenharmony_ci free_percpu(gic->cpu_base.percpu_base); 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci return ret; 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic int __init __gic_init_bases(struct gic_chip_data *gic, 12628c2ecf20Sopenharmony_ci struct fwnode_handle *handle) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci char *name; 12658c2ecf20Sopenharmony_ci int i, ret; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci if (WARN_ON(!gic || gic->domain)) 12688c2ecf20Sopenharmony_ci return -EINVAL; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (gic == &gic_data[0]) { 12718c2ecf20Sopenharmony_ci /* 12728c2ecf20Sopenharmony_ci * Initialize the CPU interface map to all CPUs. 12738c2ecf20Sopenharmony_ci * It will be refined as each CPU probes its ID. 12748c2ecf20Sopenharmony_ci * This is only necessary for the primary GIC. 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci for (i = 0; i < NR_GIC_CPU_IF; i++) 12778c2ecf20Sopenharmony_ci gic_cpu_map[i] = 0xff; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci set_handle_irq(gic_handle_irq); 12808c2ecf20Sopenharmony_ci if (static_branch_likely(&supports_deactivate_key)) 12818c2ecf20Sopenharmony_ci pr_info("GIC: Using split EOI/Deactivate mode\n"); 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (static_branch_likely(&supports_deactivate_key) && gic == &gic_data[0]) { 12858c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "GICv2"); 12868c2ecf20Sopenharmony_ci gic_init_chip(gic, NULL, name, true); 12878c2ecf20Sopenharmony_ci } else { 12888c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0])); 12898c2ecf20Sopenharmony_ci gic_init_chip(gic, NULL, name, false); 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci ret = gic_init_bases(gic, handle); 12938c2ecf20Sopenharmony_ci if (ret) 12948c2ecf20Sopenharmony_ci kfree(name); 12958c2ecf20Sopenharmony_ci else if (gic == &gic_data[0]) 12968c2ecf20Sopenharmony_ci gic_smp_init(); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci return ret; 12998c2ecf20Sopenharmony_ci} 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_civoid __init gic_init(void __iomem *dist_base, void __iomem *cpu_base) 13028c2ecf20Sopenharmony_ci{ 13038c2ecf20Sopenharmony_ci struct gic_chip_data *gic; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci /* 13068c2ecf20Sopenharmony_ci * Non-DT/ACPI systems won't run a hypervisor, so let's not 13078c2ecf20Sopenharmony_ci * bother with these... 13088c2ecf20Sopenharmony_ci */ 13098c2ecf20Sopenharmony_ci static_branch_disable(&supports_deactivate_key); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci gic = &gic_data[0]; 13128c2ecf20Sopenharmony_ci gic->raw_dist_base = dist_base; 13138c2ecf20Sopenharmony_ci gic->raw_cpu_base = cpu_base; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci __gic_init_bases(gic, NULL); 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic void gic_teardown(struct gic_chip_data *gic) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci if (WARN_ON(!gic)) 13218c2ecf20Sopenharmony_ci return; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (gic->raw_dist_base) 13248c2ecf20Sopenharmony_ci iounmap(gic->raw_dist_base); 13258c2ecf20Sopenharmony_ci if (gic->raw_cpu_base) 13268c2ecf20Sopenharmony_ci iounmap(gic->raw_cpu_base); 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 13308c2ecf20Sopenharmony_cistatic int gic_cnt __initdata; 13318c2ecf20Sopenharmony_cistatic bool gicv2_force_probe; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cistatic int __init gicv2_force_probe_cfg(char *buf) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci return strtobool(buf, &gicv2_force_probe); 13368c2ecf20Sopenharmony_ci} 13378c2ecf20Sopenharmony_ciearly_param("irqchip.gicv2_force_probe", gicv2_force_probe_cfg); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic bool gic_check_eoimode(struct device_node *node, void __iomem **base) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci struct resource cpuif_res; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci of_address_to_resource(node, 1, &cpuif_res); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (!is_hyp_mode_available()) 13468c2ecf20Sopenharmony_ci return false; 13478c2ecf20Sopenharmony_ci if (resource_size(&cpuif_res) < SZ_8K) { 13488c2ecf20Sopenharmony_ci void __iomem *alt; 13498c2ecf20Sopenharmony_ci /* 13508c2ecf20Sopenharmony_ci * Check for a stupid firmware that only exposes the 13518c2ecf20Sopenharmony_ci * first page of a GICv2. 13528c2ecf20Sopenharmony_ci */ 13538c2ecf20Sopenharmony_ci if (!gic_check_gicv2(*base)) 13548c2ecf20Sopenharmony_ci return false; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if (!gicv2_force_probe) { 13578c2ecf20Sopenharmony_ci pr_warn("GIC: GICv2 detected, but range too small and irqchip.gicv2_force_probe not set\n"); 13588c2ecf20Sopenharmony_ci return false; 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci alt = ioremap(cpuif_res.start, SZ_8K); 13628c2ecf20Sopenharmony_ci if (!alt) 13638c2ecf20Sopenharmony_ci return false; 13648c2ecf20Sopenharmony_ci if (!gic_check_gicv2(alt + SZ_4K)) { 13658c2ecf20Sopenharmony_ci /* 13668c2ecf20Sopenharmony_ci * The first page was that of a GICv2, and 13678c2ecf20Sopenharmony_ci * the second was *something*. Let's trust it 13688c2ecf20Sopenharmony_ci * to be a GICv2, and update the mapping. 13698c2ecf20Sopenharmony_ci */ 13708c2ecf20Sopenharmony_ci pr_warn("GIC: GICv2 at %pa, but range is too small (broken DT?), assuming 8kB\n", 13718c2ecf20Sopenharmony_ci &cpuif_res.start); 13728c2ecf20Sopenharmony_ci iounmap(*base); 13738c2ecf20Sopenharmony_ci *base = alt; 13748c2ecf20Sopenharmony_ci return true; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci /* 13788c2ecf20Sopenharmony_ci * We detected *two* initial GICv2 pages in a 13798c2ecf20Sopenharmony_ci * row. Could be a GICv2 aliased over two 64kB 13808c2ecf20Sopenharmony_ci * pages. Update the resource, map the iospace, and 13818c2ecf20Sopenharmony_ci * pray. 13828c2ecf20Sopenharmony_ci */ 13838c2ecf20Sopenharmony_ci iounmap(alt); 13848c2ecf20Sopenharmony_ci alt = ioremap(cpuif_res.start, SZ_128K); 13858c2ecf20Sopenharmony_ci if (!alt) 13868c2ecf20Sopenharmony_ci return false; 13878c2ecf20Sopenharmony_ci pr_warn("GIC: Aliased GICv2 at %pa, trying to find the canonical range over 128kB\n", 13888c2ecf20Sopenharmony_ci &cpuif_res.start); 13898c2ecf20Sopenharmony_ci cpuif_res.end = cpuif_res.start + SZ_128K -1; 13908c2ecf20Sopenharmony_ci iounmap(*base); 13918c2ecf20Sopenharmony_ci *base = alt; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci if (resource_size(&cpuif_res) == SZ_128K) { 13948c2ecf20Sopenharmony_ci /* 13958c2ecf20Sopenharmony_ci * Verify that we have the first 4kB of a GICv2 13968c2ecf20Sopenharmony_ci * aliased over the first 64kB by checking the 13978c2ecf20Sopenharmony_ci * GICC_IIDR register on both ends. 13988c2ecf20Sopenharmony_ci */ 13998c2ecf20Sopenharmony_ci if (!gic_check_gicv2(*base) || 14008c2ecf20Sopenharmony_ci !gic_check_gicv2(*base + 0xf000)) 14018c2ecf20Sopenharmony_ci return false; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci /* 14048c2ecf20Sopenharmony_ci * Move the base up by 60kB, so that we have a 8kB 14058c2ecf20Sopenharmony_ci * contiguous region, which allows us to use GICC_DIR 14068c2ecf20Sopenharmony_ci * at its normal offset. Please pass me that bucket. 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_ci *base += 0xf000; 14098c2ecf20Sopenharmony_ci cpuif_res.start += 0xf000; 14108c2ecf20Sopenharmony_ci pr_warn("GIC: Adjusting CPU interface base to %pa\n", 14118c2ecf20Sopenharmony_ci &cpuif_res.start); 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci return true; 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cistatic bool gic_enable_rmw_access(void *data) 14188c2ecf20Sopenharmony_ci{ 14198c2ecf20Sopenharmony_ci /* 14208c2ecf20Sopenharmony_ci * The EMEV2 class of machines has a broken interconnect, and 14218c2ecf20Sopenharmony_ci * locks up on accesses that are less than 32bit. So far, only 14228c2ecf20Sopenharmony_ci * the affinity setting requires it. 14238c2ecf20Sopenharmony_ci */ 14248c2ecf20Sopenharmony_ci if (of_machine_is_compatible("renesas,emev2")) { 14258c2ecf20Sopenharmony_ci static_branch_enable(&needs_rmw_access); 14268c2ecf20Sopenharmony_ci return true; 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci return false; 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_cistatic const struct gic_quirk gic_quirks[] = { 14338c2ecf20Sopenharmony_ci { 14348c2ecf20Sopenharmony_ci .desc = "broken byte access", 14358c2ecf20Sopenharmony_ci .compatible = "arm,pl390", 14368c2ecf20Sopenharmony_ci .init = gic_enable_rmw_access, 14378c2ecf20Sopenharmony_ci }, 14388c2ecf20Sopenharmony_ci { }, 14398c2ecf20Sopenharmony_ci}; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_cistatic int gic_of_setup(struct gic_chip_data *gic, struct device_node *node) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci if (!gic || !node) 14448c2ecf20Sopenharmony_ci return -EINVAL; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci gic->raw_dist_base = of_iomap(node, 0); 14478c2ecf20Sopenharmony_ci if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n")) 14488c2ecf20Sopenharmony_ci goto error; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci gic->raw_cpu_base = of_iomap(node, 1); 14518c2ecf20Sopenharmony_ci if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n")) 14528c2ecf20Sopenharmony_ci goto error; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset)) 14558c2ecf20Sopenharmony_ci gic->percpu_offset = 0; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci gic_enable_of_quirks(node, gic_quirks, gic); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci return 0; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cierror: 14628c2ecf20Sopenharmony_ci gic_teardown(gic); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci return -ENOMEM; 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ciint gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci int ret; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (!dev || !dev->of_node || !gic || !irq) 14728c2ecf20Sopenharmony_ci return -EINVAL; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci *gic = devm_kzalloc(dev, sizeof(**gic), GFP_KERNEL); 14758c2ecf20Sopenharmony_ci if (!*gic) 14768c2ecf20Sopenharmony_ci return -ENOMEM; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci gic_init_chip(*gic, dev, dev->of_node->name, false); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci ret = gic_of_setup(*gic, dev->of_node); 14818c2ecf20Sopenharmony_ci if (ret) 14828c2ecf20Sopenharmony_ci return ret; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci ret = gic_init_bases(*gic, &dev->of_node->fwnode); 14858c2ecf20Sopenharmony_ci if (ret) { 14868c2ecf20Sopenharmony_ci gic_teardown(*gic); 14878c2ecf20Sopenharmony_ci return ret; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, *gic); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci return 0; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic void __init gic_of_setup_kvm_info(struct device_node *node) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci int ret; 14988c2ecf20Sopenharmony_ci struct resource *vctrl_res = &gic_v2_kvm_info.vctrl; 14998c2ecf20Sopenharmony_ci struct resource *vcpu_res = &gic_v2_kvm_info.vcpu; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci gic_v2_kvm_info.type = GIC_V2; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci gic_v2_kvm_info.maint_irq = irq_of_parse_and_map(node, 0); 15048c2ecf20Sopenharmony_ci if (!gic_v2_kvm_info.maint_irq) 15058c2ecf20Sopenharmony_ci return; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci ret = of_address_to_resource(node, 2, vctrl_res); 15088c2ecf20Sopenharmony_ci if (ret) 15098c2ecf20Sopenharmony_ci return; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci ret = of_address_to_resource(node, 3, vcpu_res); 15128c2ecf20Sopenharmony_ci if (ret) 15138c2ecf20Sopenharmony_ci return; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (static_branch_likely(&supports_deactivate_key)) 15168c2ecf20Sopenharmony_ci gic_set_kvm_info(&gic_v2_kvm_info); 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ciint __init 15208c2ecf20Sopenharmony_cigic_of_init(struct device_node *node, struct device_node *parent) 15218c2ecf20Sopenharmony_ci{ 15228c2ecf20Sopenharmony_ci struct gic_chip_data *gic; 15238c2ecf20Sopenharmony_ci int irq, ret; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci if (WARN_ON(!node)) 15268c2ecf20Sopenharmony_ci return -ENODEV; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (WARN_ON(gic_cnt >= CONFIG_ARM_GIC_MAX_NR)) 15298c2ecf20Sopenharmony_ci return -EINVAL; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci gic = &gic_data[gic_cnt]; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci ret = gic_of_setup(gic, node); 15348c2ecf20Sopenharmony_ci if (ret) 15358c2ecf20Sopenharmony_ci return ret; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci /* 15388c2ecf20Sopenharmony_ci * Disable split EOI/Deactivate if either HYP is not available 15398c2ecf20Sopenharmony_ci * or the CPU interface is too small. 15408c2ecf20Sopenharmony_ci */ 15418c2ecf20Sopenharmony_ci if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base)) 15428c2ecf20Sopenharmony_ci static_branch_disable(&supports_deactivate_key); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci ret = __gic_init_bases(gic, &node->fwnode); 15458c2ecf20Sopenharmony_ci if (ret) { 15468c2ecf20Sopenharmony_ci gic_teardown(gic); 15478c2ecf20Sopenharmony_ci return ret; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci if (!gic_cnt) { 15518c2ecf20Sopenharmony_ci gic_init_physaddr(node); 15528c2ecf20Sopenharmony_ci gic_of_setup_kvm_info(node); 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (parent) { 15568c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 15578c2ecf20Sopenharmony_ci gic_cascade_irq(gic_cnt, irq); 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) 15618c2ecf20Sopenharmony_ci gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci gic_cnt++; 15648c2ecf20Sopenharmony_ci return 0; 15658c2ecf20Sopenharmony_ci} 15668c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init); 15678c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(arm11mp_gic, "arm,arm11mp-gic", gic_of_init); 15688c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(arm1176jzf_dc_gic, "arm,arm1176jzf-devchip-gic", gic_of_init); 15698c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init); 15708c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init); 15718c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init); 15728c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); 15738c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); 15748c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(pl390, "arm,pl390", gic_of_init); 15758c2ecf20Sopenharmony_ci#else 15768c2ecf20Sopenharmony_ciint gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci return -ENOTSUPP; 15798c2ecf20Sopenharmony_ci} 15808c2ecf20Sopenharmony_ci#endif 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 15838c2ecf20Sopenharmony_cistatic struct 15848c2ecf20Sopenharmony_ci{ 15858c2ecf20Sopenharmony_ci phys_addr_t cpu_phys_base; 15868c2ecf20Sopenharmony_ci u32 maint_irq; 15878c2ecf20Sopenharmony_ci int maint_irq_mode; 15888c2ecf20Sopenharmony_ci phys_addr_t vctrl_base; 15898c2ecf20Sopenharmony_ci phys_addr_t vcpu_base; 15908c2ecf20Sopenharmony_ci} acpi_data __initdata; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_cistatic int __init 15938c2ecf20Sopenharmony_cigic_acpi_parse_madt_cpu(union acpi_subtable_headers *header, 15948c2ecf20Sopenharmony_ci const unsigned long end) 15958c2ecf20Sopenharmony_ci{ 15968c2ecf20Sopenharmony_ci struct acpi_madt_generic_interrupt *processor; 15978c2ecf20Sopenharmony_ci phys_addr_t gic_cpu_base; 15988c2ecf20Sopenharmony_ci static int cpu_base_assigned; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci processor = (struct acpi_madt_generic_interrupt *)header; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci if (BAD_MADT_GICC_ENTRY(processor, end)) 16038c2ecf20Sopenharmony_ci return -EINVAL; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci /* 16068c2ecf20Sopenharmony_ci * There is no support for non-banked GICv1/2 register in ACPI spec. 16078c2ecf20Sopenharmony_ci * All CPU interface addresses have to be the same. 16088c2ecf20Sopenharmony_ci */ 16098c2ecf20Sopenharmony_ci gic_cpu_base = processor->base_address; 16108c2ecf20Sopenharmony_ci if (cpu_base_assigned && gic_cpu_base != acpi_data.cpu_phys_base) 16118c2ecf20Sopenharmony_ci return -EINVAL; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci acpi_data.cpu_phys_base = gic_cpu_base; 16148c2ecf20Sopenharmony_ci acpi_data.maint_irq = processor->vgic_interrupt; 16158c2ecf20Sopenharmony_ci acpi_data.maint_irq_mode = (processor->flags & ACPI_MADT_VGIC_IRQ_MODE) ? 16168c2ecf20Sopenharmony_ci ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE; 16178c2ecf20Sopenharmony_ci acpi_data.vctrl_base = processor->gich_base_address; 16188c2ecf20Sopenharmony_ci acpi_data.vcpu_base = processor->gicv_base_address; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci cpu_base_assigned = 1; 16218c2ecf20Sopenharmony_ci return 0; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci/* The things you have to do to just *count* something... */ 16258c2ecf20Sopenharmony_cistatic int __init acpi_dummy_func(union acpi_subtable_headers *header, 16268c2ecf20Sopenharmony_ci const unsigned long end) 16278c2ecf20Sopenharmony_ci{ 16288c2ecf20Sopenharmony_ci return 0; 16298c2ecf20Sopenharmony_ci} 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_cistatic bool __init acpi_gic_redist_is_present(void) 16328c2ecf20Sopenharmony_ci{ 16338c2ecf20Sopenharmony_ci return acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR, 16348c2ecf20Sopenharmony_ci acpi_dummy_func, 0) > 0; 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic bool __init gic_validate_dist(struct acpi_subtable_header *header, 16388c2ecf20Sopenharmony_ci struct acpi_probe_entry *ape) 16398c2ecf20Sopenharmony_ci{ 16408c2ecf20Sopenharmony_ci struct acpi_madt_generic_distributor *dist; 16418c2ecf20Sopenharmony_ci dist = (struct acpi_madt_generic_distributor *)header; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci return (dist->version == ape->driver_data && 16448c2ecf20Sopenharmony_ci (dist->version != ACPI_MADT_GIC_VERSION_NONE || 16458c2ecf20Sopenharmony_ci !acpi_gic_redist_is_present())); 16468c2ecf20Sopenharmony_ci} 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K) 16498c2ecf20Sopenharmony_ci#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K) 16508c2ecf20Sopenharmony_ci#define ACPI_GICV2_VCTRL_MEM_SIZE (SZ_4K) 16518c2ecf20Sopenharmony_ci#define ACPI_GICV2_VCPU_MEM_SIZE (SZ_8K) 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_cistatic void __init gic_acpi_setup_kvm_info(void) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci int irq; 16568c2ecf20Sopenharmony_ci struct resource *vctrl_res = &gic_v2_kvm_info.vctrl; 16578c2ecf20Sopenharmony_ci struct resource *vcpu_res = &gic_v2_kvm_info.vcpu; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci gic_v2_kvm_info.type = GIC_V2; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci if (!acpi_data.vctrl_base) 16628c2ecf20Sopenharmony_ci return; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci vctrl_res->flags = IORESOURCE_MEM; 16658c2ecf20Sopenharmony_ci vctrl_res->start = acpi_data.vctrl_base; 16668c2ecf20Sopenharmony_ci vctrl_res->end = vctrl_res->start + ACPI_GICV2_VCTRL_MEM_SIZE - 1; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if (!acpi_data.vcpu_base) 16698c2ecf20Sopenharmony_ci return; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci vcpu_res->flags = IORESOURCE_MEM; 16728c2ecf20Sopenharmony_ci vcpu_res->start = acpi_data.vcpu_base; 16738c2ecf20Sopenharmony_ci vcpu_res->end = vcpu_res->start + ACPI_GICV2_VCPU_MEM_SIZE - 1; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci irq = acpi_register_gsi(NULL, acpi_data.maint_irq, 16768c2ecf20Sopenharmony_ci acpi_data.maint_irq_mode, 16778c2ecf20Sopenharmony_ci ACPI_ACTIVE_HIGH); 16788c2ecf20Sopenharmony_ci if (irq <= 0) 16798c2ecf20Sopenharmony_ci return; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci gic_v2_kvm_info.maint_irq = irq; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci gic_set_kvm_info(&gic_v2_kvm_info); 16848c2ecf20Sopenharmony_ci} 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_cistatic int __init gic_v2_acpi_init(union acpi_subtable_headers *header, 16878c2ecf20Sopenharmony_ci const unsigned long end) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci struct acpi_madt_generic_distributor *dist; 16908c2ecf20Sopenharmony_ci struct fwnode_handle *domain_handle; 16918c2ecf20Sopenharmony_ci struct gic_chip_data *gic = &gic_data[0]; 16928c2ecf20Sopenharmony_ci int count, ret; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci /* Collect CPU base addresses */ 16958c2ecf20Sopenharmony_ci count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, 16968c2ecf20Sopenharmony_ci gic_acpi_parse_madt_cpu, 0); 16978c2ecf20Sopenharmony_ci if (count <= 0) { 16988c2ecf20Sopenharmony_ci pr_err("No valid GICC entries exist\n"); 16998c2ecf20Sopenharmony_ci return -EINVAL; 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci gic->raw_cpu_base = ioremap(acpi_data.cpu_phys_base, ACPI_GIC_CPU_IF_MEM_SIZE); 17038c2ecf20Sopenharmony_ci if (!gic->raw_cpu_base) { 17048c2ecf20Sopenharmony_ci pr_err("Unable to map GICC registers\n"); 17058c2ecf20Sopenharmony_ci return -ENOMEM; 17068c2ecf20Sopenharmony_ci } 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci dist = (struct acpi_madt_generic_distributor *)header; 17098c2ecf20Sopenharmony_ci gic->raw_dist_base = ioremap(dist->base_address, 17108c2ecf20Sopenharmony_ci ACPI_GICV2_DIST_MEM_SIZE); 17118c2ecf20Sopenharmony_ci if (!gic->raw_dist_base) { 17128c2ecf20Sopenharmony_ci pr_err("Unable to map GICD registers\n"); 17138c2ecf20Sopenharmony_ci gic_teardown(gic); 17148c2ecf20Sopenharmony_ci return -ENOMEM; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci /* 17188c2ecf20Sopenharmony_ci * Disable split EOI/Deactivate if HYP is not available. ACPI 17198c2ecf20Sopenharmony_ci * guarantees that we'll always have a GICv2, so the CPU 17208c2ecf20Sopenharmony_ci * interface will always be the right size. 17218c2ecf20Sopenharmony_ci */ 17228c2ecf20Sopenharmony_ci if (!is_hyp_mode_available()) 17238c2ecf20Sopenharmony_ci static_branch_disable(&supports_deactivate_key); 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci /* 17268c2ecf20Sopenharmony_ci * Initialize GIC instance zero (no multi-GIC support). 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_ci domain_handle = irq_domain_alloc_fwnode(&dist->base_address); 17298c2ecf20Sopenharmony_ci if (!domain_handle) { 17308c2ecf20Sopenharmony_ci pr_err("Unable to allocate domain handle\n"); 17318c2ecf20Sopenharmony_ci gic_teardown(gic); 17328c2ecf20Sopenharmony_ci return -ENOMEM; 17338c2ecf20Sopenharmony_ci } 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci ret = __gic_init_bases(gic, domain_handle); 17368c2ecf20Sopenharmony_ci if (ret) { 17378c2ecf20Sopenharmony_ci pr_err("Failed to initialise GIC\n"); 17388c2ecf20Sopenharmony_ci irq_domain_free_fwnode(domain_handle); 17398c2ecf20Sopenharmony_ci gic_teardown(gic); 17408c2ecf20Sopenharmony_ci return ret; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) 17468c2ecf20Sopenharmony_ci gicv2m_init(NULL, gic_data[0].domain); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci if (static_branch_likely(&supports_deactivate_key)) 17498c2ecf20Sopenharmony_ci gic_acpi_setup_kvm_info(); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci return 0; 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ciIRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 17548c2ecf20Sopenharmony_ci gic_validate_dist, ACPI_MADT_GIC_VERSION_V2, 17558c2ecf20Sopenharmony_ci gic_v2_acpi_init); 17568c2ecf20Sopenharmony_ciIRQCHIP_ACPI_DECLARE(gic_v2_maybe, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 17578c2ecf20Sopenharmony_ci gic_validate_dist, ACPI_MADT_GIC_VERSION_NONE, 17588c2ecf20Sopenharmony_ci gic_v2_acpi_init); 17598c2ecf20Sopenharmony_ci#endif 1760