18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013-2017 ARM Limited, All Rights Reserved. 48c2ecf20Sopenharmony_ci * Author: Marc Zyngier <marc.zyngier@arm.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/acpi.h> 88c2ecf20Sopenharmony_ci#include <linux/acpi_iort.h> 98c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 108c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 118c2ecf20Sopenharmony_ci#include <linux/cpu.h> 128c2ecf20Sopenharmony_ci#include <linux/crash_dump.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-iommu.h> 158c2ecf20Sopenharmony_ci#include <linux/efi.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 188c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 198c2ecf20Sopenharmony_ci#include <linux/list.h> 208c2ecf20Sopenharmony_ci#include <linux/log2.h> 218c2ecf20Sopenharmony_ci#include <linux/memblock.h> 228c2ecf20Sopenharmony_ci#include <linux/mm.h> 238c2ecf20Sopenharmony_ci#include <linux/msi.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/of_address.h> 268c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 278c2ecf20Sopenharmony_ci#include <linux/of_pci.h> 288c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 298c2ecf20Sopenharmony_ci#include <linux/percpu.h> 308c2ecf20Sopenharmony_ci#include <linux/slab.h> 318c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 348c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic-v3.h> 358c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic-v4.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <asm/cputype.h> 388c2ecf20Sopenharmony_ci#include <asm/exception.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include "irq-gic-common.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) 438c2ecf20Sopenharmony_ci#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) 448c2ecf20Sopenharmony_ci#define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) 478c2ecf20Sopenharmony_ci#define RDIST_FLAGS_RD_TABLES_PREALLOCATED (1 << 1) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic u32 lpi_id_bits; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * We allocate memory for PROPBASE to cover 2 ^ lpi_id_bits LPIs to 538c2ecf20Sopenharmony_ci * deal with (one configuration byte per interrupt). PENDBASE has to 548c2ecf20Sopenharmony_ci * be 64kB aligned (one bit per LPI, plus 8192 bits for SPI/PPI/SGI). 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#define LPI_NRBITS lpi_id_bits 578c2ecf20Sopenharmony_ci#define LPI_PROPBASE_SZ ALIGN(BIT(LPI_NRBITS), SZ_64K) 588c2ecf20Sopenharmony_ci#define LPI_PENDBASE_SZ ALIGN(BIT(LPI_NRBITS) / 8, SZ_64K) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define LPI_PROP_DEFAULT_PRIO GICD_INT_DEF_PRI 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * Collection structure - just an ID, and a redistributor address to 648c2ecf20Sopenharmony_ci * ping. We use one per CPU as a bag of interrupts assigned to this 658c2ecf20Sopenharmony_ci * CPU. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistruct its_collection { 688c2ecf20Sopenharmony_ci u64 target_address; 698c2ecf20Sopenharmony_ci u16 col_id; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * The ITS_BASER structure - contains memory information, cached 748c2ecf20Sopenharmony_ci * value of BASER register configuration and ITS page size. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistruct its_baser { 778c2ecf20Sopenharmony_ci void *base; 788c2ecf20Sopenharmony_ci u64 val; 798c2ecf20Sopenharmony_ci u32 order; 808c2ecf20Sopenharmony_ci u32 psz; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct its_device; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * The ITS structure - contains most of the infrastructure, with the 878c2ecf20Sopenharmony_ci * top-level MSI domain, the command queue, the collections, and the 888c2ecf20Sopenharmony_ci * list of devices writing to it. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * dev_alloc_lock has to be taken for device allocations, while the 918c2ecf20Sopenharmony_ci * spinlock must be taken to parse data structures such as the device 928c2ecf20Sopenharmony_ci * list. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cistruct its_node { 958c2ecf20Sopenharmony_ci raw_spinlock_t lock; 968c2ecf20Sopenharmony_ci struct mutex dev_alloc_lock; 978c2ecf20Sopenharmony_ci struct list_head entry; 988c2ecf20Sopenharmony_ci void __iomem *base; 998c2ecf20Sopenharmony_ci void __iomem *sgir_base; 1008c2ecf20Sopenharmony_ci phys_addr_t phys_base; 1018c2ecf20Sopenharmony_ci struct its_cmd_block *cmd_base; 1028c2ecf20Sopenharmony_ci struct its_cmd_block *cmd_write; 1038c2ecf20Sopenharmony_ci struct its_baser tables[GITS_BASER_NR_REGS]; 1048c2ecf20Sopenharmony_ci struct its_collection *collections; 1058c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode_handle; 1068c2ecf20Sopenharmony_ci u64 (*get_msi_base)(struct its_device *its_dev); 1078c2ecf20Sopenharmony_ci u64 typer; 1088c2ecf20Sopenharmony_ci u64 cbaser_save; 1098c2ecf20Sopenharmony_ci u32 ctlr_save; 1108c2ecf20Sopenharmony_ci u32 mpidr; 1118c2ecf20Sopenharmony_ci struct list_head its_device_list; 1128c2ecf20Sopenharmony_ci u64 flags; 1138c2ecf20Sopenharmony_ci unsigned long list_nr; 1148c2ecf20Sopenharmony_ci int numa_node; 1158c2ecf20Sopenharmony_ci unsigned int msi_domain_flags; 1168c2ecf20Sopenharmony_ci u32 pre_its_base; /* for Socionext Synquacer */ 1178c2ecf20Sopenharmony_ci int vlpi_redist_offset; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define is_v4(its) (!!((its)->typer & GITS_TYPER_VLPIS)) 1218c2ecf20Sopenharmony_ci#define is_v4_1(its) (!!((its)->typer & GITS_TYPER_VMAPP)) 1228c2ecf20Sopenharmony_ci#define device_ids(its) (FIELD_GET(GITS_TYPER_DEVBITS, (its)->typer) + 1) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define ITS_ITT_ALIGN SZ_256 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* The maximum number of VPEID bits supported by VLPI commands */ 1278c2ecf20Sopenharmony_ci#define ITS_MAX_VPEID_BITS \ 1288c2ecf20Sopenharmony_ci ({ \ 1298c2ecf20Sopenharmony_ci int nvpeid = 16; \ 1308c2ecf20Sopenharmony_ci if (gic_rdists->has_rvpeid && \ 1318c2ecf20Sopenharmony_ci gic_rdists->gicd_typer2 & GICD_TYPER2_VIL) \ 1328c2ecf20Sopenharmony_ci nvpeid = 1 + (gic_rdists->gicd_typer2 & \ 1338c2ecf20Sopenharmony_ci GICD_TYPER2_VID); \ 1348c2ecf20Sopenharmony_ci \ 1358c2ecf20Sopenharmony_ci nvpeid; \ 1368c2ecf20Sopenharmony_ci }) 1378c2ecf20Sopenharmony_ci#define ITS_MAX_VPEID (1 << (ITS_MAX_VPEID_BITS)) 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* Convert page order to size in bytes */ 1408c2ecf20Sopenharmony_ci#define PAGE_ORDER_TO_SIZE(o) (PAGE_SIZE << (o)) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistruct event_lpi_map { 1438c2ecf20Sopenharmony_ci unsigned long *lpi_map; 1448c2ecf20Sopenharmony_ci u16 *col_map; 1458c2ecf20Sopenharmony_ci irq_hw_number_t lpi_base; 1468c2ecf20Sopenharmony_ci int nr_lpis; 1478c2ecf20Sopenharmony_ci raw_spinlock_t vlpi_lock; 1488c2ecf20Sopenharmony_ci struct its_vm *vm; 1498c2ecf20Sopenharmony_ci struct its_vlpi_map *vlpi_maps; 1508c2ecf20Sopenharmony_ci int nr_vlpis; 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * The ITS view of a device - belongs to an ITS, owns an interrupt 1558c2ecf20Sopenharmony_ci * translation table, and a list of interrupts. If it some of its 1568c2ecf20Sopenharmony_ci * LPIs are injected into a guest (GICv4), the event_map.vm field 1578c2ecf20Sopenharmony_ci * indicates which one. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistruct its_device { 1608c2ecf20Sopenharmony_ci struct list_head entry; 1618c2ecf20Sopenharmony_ci struct its_node *its; 1628c2ecf20Sopenharmony_ci struct event_lpi_map event_map; 1638c2ecf20Sopenharmony_ci void *itt; 1648c2ecf20Sopenharmony_ci u32 nr_ites; 1658c2ecf20Sopenharmony_ci u32 device_id; 1668c2ecf20Sopenharmony_ci bool shared; 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct { 1708c2ecf20Sopenharmony_ci raw_spinlock_t lock; 1718c2ecf20Sopenharmony_ci struct its_device *dev; 1728c2ecf20Sopenharmony_ci struct its_vpe **vpes; 1738c2ecf20Sopenharmony_ci int next_victim; 1748c2ecf20Sopenharmony_ci} vpe_proxy; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistruct cpu_lpi_count { 1778c2ecf20Sopenharmony_ci atomic_t managed; 1788c2ecf20Sopenharmony_ci atomic_t unmanaged; 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu_lpi_count, cpu_lpi_count); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic LIST_HEAD(its_nodes); 1848c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(its_lock); 1858c2ecf20Sopenharmony_cistatic struct rdists *gic_rdists; 1868c2ecf20Sopenharmony_cistatic struct irq_domain *its_parent; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic unsigned long its_list_map; 1898c2ecf20Sopenharmony_cistatic u16 vmovp_seq_num; 1908c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(vmovp_lock); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic DEFINE_IDA(its_vpeid_ida); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) 1958c2ecf20Sopenharmony_ci#define gic_data_rdist_cpu(cpu) (per_cpu_ptr(gic_rdists->rdist, cpu)) 1968c2ecf20Sopenharmony_ci#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) 1978c2ecf20Sopenharmony_ci#define gic_data_rdist_vlpi_base() (gic_data_rdist_rd_base() + SZ_128K) 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * Skip ITSs that have no vLPIs mapped, unless we're on GICv4.1, as we 2018c2ecf20Sopenharmony_ci * always have vSGIs mapped. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_cistatic bool require_its_list_vmovp(struct its_vm *vm, struct its_node *its) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci return (gic_rdists->has_rvpeid || vm->vlpi_count[its->list_nr]); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic u16 get_its_list(struct its_vm *vm) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct its_node *its; 2118c2ecf20Sopenharmony_ci unsigned long its_list = 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 2148c2ecf20Sopenharmony_ci if (!is_v4(its)) 2158c2ecf20Sopenharmony_ci continue; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (require_its_list_vmovp(vm, its)) 2188c2ecf20Sopenharmony_ci __set_bit(its->list_nr, &its_list); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return (u16)its_list; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic inline u32 its_get_event_id(struct irq_data *d) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 2278c2ecf20Sopenharmony_ci return d->hwirq - its_dev->event_map.lpi_base; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic struct its_collection *dev_event_to_col(struct its_device *its_dev, 2318c2ecf20Sopenharmony_ci u32 event) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct its_node *its = its_dev->its; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return its->collections + its_dev->event_map.col_map[event]; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct its_vlpi_map *dev_event_to_vlpi_map(struct its_device *its_dev, 2398c2ecf20Sopenharmony_ci u32 event) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(event >= its_dev->event_map.nr_lpis)) 2428c2ecf20Sopenharmony_ci return NULL; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return &its_dev->event_map.vlpi_maps[event]; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic struct its_vlpi_map *get_vlpi_map(struct irq_data *d) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) { 2508c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 2518c2ecf20Sopenharmony_ci u32 event = its_get_event_id(d); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return dev_event_to_vlpi_map(its_dev, event); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return NULL; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int vpe_to_cpuid_lock(struct its_vpe *vpe, unsigned long *flags) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vpe->vpe_lock, *flags); 2628c2ecf20Sopenharmony_ci return vpe->col_idx; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void vpe_to_cpuid_unlock(struct its_vpe *vpe, unsigned long flags) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vpe->vpe_lock, flags); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct irq_chip its_vpe_irq_chip; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int irq_to_cpuid_lock(struct irq_data *d, unsigned long *flags) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct its_vpe *vpe = NULL; 2758c2ecf20Sopenharmony_ci int cpu; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (d->chip == &its_vpe_irq_chip) { 2788c2ecf20Sopenharmony_ci vpe = irq_data_get_irq_chip_data(d); 2798c2ecf20Sopenharmony_ci } else { 2808c2ecf20Sopenharmony_ci struct its_vlpi_map *map = get_vlpi_map(d); 2818c2ecf20Sopenharmony_ci if (map) 2828c2ecf20Sopenharmony_ci vpe = map->vpe; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (vpe) { 2868c2ecf20Sopenharmony_ci cpu = vpe_to_cpuid_lock(vpe, flags); 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci /* Physical LPIs are already locked via the irq_desc lock */ 2898c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 2908c2ecf20Sopenharmony_ci cpu = its_dev->event_map.col_map[its_get_event_id(d)]; 2918c2ecf20Sopenharmony_ci /* Keep GCC quiet... */ 2928c2ecf20Sopenharmony_ci *flags = 0; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return cpu; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void irq_to_cpuid_unlock(struct irq_data *d, unsigned long flags) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct its_vpe *vpe = NULL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (d->chip == &its_vpe_irq_chip) { 3038c2ecf20Sopenharmony_ci vpe = irq_data_get_irq_chip_data(d); 3048c2ecf20Sopenharmony_ci } else { 3058c2ecf20Sopenharmony_ci struct its_vlpi_map *map = get_vlpi_map(d); 3068c2ecf20Sopenharmony_ci if (map) 3078c2ecf20Sopenharmony_ci vpe = map->vpe; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (vpe) 3118c2ecf20Sopenharmony_ci vpe_to_cpuid_unlock(vpe, flags); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic struct its_collection *valid_col(struct its_collection *col) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(col->target_address & GENMASK_ULL(15, 0))) 3178c2ecf20Sopenharmony_ci return NULL; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return col; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic struct its_vpe *valid_vpe(struct its_node *its, struct its_vpe *vpe) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci if (valid_col(its->collections + vpe->col_idx)) 3258c2ecf20Sopenharmony_ci return vpe; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return NULL; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * ITS command descriptors - parameters to be encoded in a command 3328c2ecf20Sopenharmony_ci * block. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_cistruct its_cmd_desc { 3358c2ecf20Sopenharmony_ci union { 3368c2ecf20Sopenharmony_ci struct { 3378c2ecf20Sopenharmony_ci struct its_device *dev; 3388c2ecf20Sopenharmony_ci u32 event_id; 3398c2ecf20Sopenharmony_ci } its_inv_cmd; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci struct { 3428c2ecf20Sopenharmony_ci struct its_device *dev; 3438c2ecf20Sopenharmony_ci u32 event_id; 3448c2ecf20Sopenharmony_ci } its_clear_cmd; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci struct { 3478c2ecf20Sopenharmony_ci struct its_device *dev; 3488c2ecf20Sopenharmony_ci u32 event_id; 3498c2ecf20Sopenharmony_ci } its_int_cmd; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci struct { 3528c2ecf20Sopenharmony_ci struct its_device *dev; 3538c2ecf20Sopenharmony_ci int valid; 3548c2ecf20Sopenharmony_ci } its_mapd_cmd; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci struct { 3578c2ecf20Sopenharmony_ci struct its_collection *col; 3588c2ecf20Sopenharmony_ci int valid; 3598c2ecf20Sopenharmony_ci } its_mapc_cmd; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci struct { 3628c2ecf20Sopenharmony_ci struct its_device *dev; 3638c2ecf20Sopenharmony_ci u32 phys_id; 3648c2ecf20Sopenharmony_ci u32 event_id; 3658c2ecf20Sopenharmony_ci } its_mapti_cmd; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci struct { 3688c2ecf20Sopenharmony_ci struct its_device *dev; 3698c2ecf20Sopenharmony_ci struct its_collection *col; 3708c2ecf20Sopenharmony_ci u32 event_id; 3718c2ecf20Sopenharmony_ci } its_movi_cmd; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci struct { 3748c2ecf20Sopenharmony_ci struct its_device *dev; 3758c2ecf20Sopenharmony_ci u32 event_id; 3768c2ecf20Sopenharmony_ci } its_discard_cmd; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci struct { 3798c2ecf20Sopenharmony_ci struct its_collection *col; 3808c2ecf20Sopenharmony_ci } its_invall_cmd; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci struct { 3838c2ecf20Sopenharmony_ci struct its_vpe *vpe; 3848c2ecf20Sopenharmony_ci } its_vinvall_cmd; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci struct { 3878c2ecf20Sopenharmony_ci struct its_vpe *vpe; 3888c2ecf20Sopenharmony_ci struct its_collection *col; 3898c2ecf20Sopenharmony_ci bool valid; 3908c2ecf20Sopenharmony_ci } its_vmapp_cmd; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci struct { 3938c2ecf20Sopenharmony_ci struct its_vpe *vpe; 3948c2ecf20Sopenharmony_ci struct its_device *dev; 3958c2ecf20Sopenharmony_ci u32 virt_id; 3968c2ecf20Sopenharmony_ci u32 event_id; 3978c2ecf20Sopenharmony_ci bool db_enabled; 3988c2ecf20Sopenharmony_ci } its_vmapti_cmd; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci struct { 4018c2ecf20Sopenharmony_ci struct its_vpe *vpe; 4028c2ecf20Sopenharmony_ci struct its_device *dev; 4038c2ecf20Sopenharmony_ci u32 event_id; 4048c2ecf20Sopenharmony_ci bool db_enabled; 4058c2ecf20Sopenharmony_ci } its_vmovi_cmd; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci struct { 4088c2ecf20Sopenharmony_ci struct its_vpe *vpe; 4098c2ecf20Sopenharmony_ci struct its_collection *col; 4108c2ecf20Sopenharmony_ci u16 seq_num; 4118c2ecf20Sopenharmony_ci u16 its_list; 4128c2ecf20Sopenharmony_ci } its_vmovp_cmd; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci struct { 4158c2ecf20Sopenharmony_ci struct its_vpe *vpe; 4168c2ecf20Sopenharmony_ci } its_invdb_cmd; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci struct { 4198c2ecf20Sopenharmony_ci struct its_vpe *vpe; 4208c2ecf20Sopenharmony_ci u8 sgi; 4218c2ecf20Sopenharmony_ci u8 priority; 4228c2ecf20Sopenharmony_ci bool enable; 4238c2ecf20Sopenharmony_ci bool group; 4248c2ecf20Sopenharmony_ci bool clear; 4258c2ecf20Sopenharmony_ci } its_vsgi_cmd; 4268c2ecf20Sopenharmony_ci }; 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* 4308c2ecf20Sopenharmony_ci * The ITS command block, which is what the ITS actually parses. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_cistruct its_cmd_block { 4338c2ecf20Sopenharmony_ci union { 4348c2ecf20Sopenharmony_ci u64 raw_cmd[4]; 4358c2ecf20Sopenharmony_ci __le64 raw_cmd_le[4]; 4368c2ecf20Sopenharmony_ci }; 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci#define ITS_CMD_QUEUE_SZ SZ_64K 4408c2ecf20Sopenharmony_ci#define ITS_CMD_QUEUE_NR_ENTRIES (ITS_CMD_QUEUE_SZ / sizeof(struct its_cmd_block)) 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_citypedef struct its_collection *(*its_cmd_builder_t)(struct its_node *, 4438c2ecf20Sopenharmony_ci struct its_cmd_block *, 4448c2ecf20Sopenharmony_ci struct its_cmd_desc *); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_citypedef struct its_vpe *(*its_cmd_vbuilder_t)(struct its_node *, 4478c2ecf20Sopenharmony_ci struct its_cmd_block *, 4488c2ecf20Sopenharmony_ci struct its_cmd_desc *); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci u64 mask = GENMASK_ULL(h, l); 4538c2ecf20Sopenharmony_ci *raw_cmd &= ~mask; 4548c2ecf20Sopenharmony_ci *raw_cmd |= (val << l) & mask; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic void its_encode_devid(struct its_cmd_block *cmd, u32 devid) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic void its_encode_event_id(struct its_cmd_block *cmd, u32 id) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[1], id, 31, 0); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void its_encode_size(struct its_cmd_block *cmd, u8 size) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[1], size, 4, 0); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 51, 8); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void its_encode_valid(struct its_cmd_block *cmd, int valid) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 51, 16); 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic void its_encode_collection(struct its_cmd_block *cmd, u16 col) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], col, 15, 0); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic void its_encode_vpeid(struct its_cmd_block *cmd, u16 vpeid) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[1], vpeid, 47, 32); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void its_encode_virt_id(struct its_cmd_block *cmd, u32 virt_id) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], virt_id, 31, 0); 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic void its_encode_db_phys_id(struct its_cmd_block *cmd, u32 db_phys_id) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], db_phys_id, 63, 32); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic void its_encode_db_valid(struct its_cmd_block *cmd, bool db_valid) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], db_valid, 0, 0); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void its_encode_seq_num(struct its_cmd_block *cmd, u16 seq_num) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], seq_num, 47, 32); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void its_encode_its_list(struct its_cmd_block *cmd, u16 its_list) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[1], its_list, 15, 0); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void its_encode_vpt_addr(struct its_cmd_block *cmd, u64 vpt_pa) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[3], vpt_pa >> 16, 51, 16); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void its_encode_vpt_size(struct its_cmd_block *cmd, u8 vpt_size) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[3], vpt_size, 4, 0); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void its_encode_vconf_addr(struct its_cmd_block *cmd, u64 vconf_pa) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], vconf_pa >> 16, 51, 16); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void its_encode_alloc(struct its_cmd_block *cmd, bool alloc) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], alloc, 8, 8); 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void its_encode_ptz(struct its_cmd_block *cmd, bool ptz) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], ptz, 9, 9); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic void its_encode_vmapp_default_db(struct its_cmd_block *cmd, 5588c2ecf20Sopenharmony_ci u32 vpe_db_lpi) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[1], vpe_db_lpi, 31, 0); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void its_encode_vmovp_default_db(struct its_cmd_block *cmd, 5648c2ecf20Sopenharmony_ci u32 vpe_db_lpi) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[3], vpe_db_lpi, 31, 0); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void its_encode_db(struct its_cmd_block *cmd, bool db) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[2], db, 63, 63); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic void its_encode_sgi_intid(struct its_cmd_block *cmd, u8 sgi) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], sgi, 35, 32); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic void its_encode_sgi_priority(struct its_cmd_block *cmd, u8 prio) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], prio >> 4, 23, 20); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void its_encode_sgi_group(struct its_cmd_block *cmd, bool grp) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], grp, 10, 10); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic void its_encode_sgi_clear(struct its_cmd_block *cmd, bool clr) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], clr, 9, 9); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic void its_encode_sgi_enable(struct its_cmd_block *cmd, bool en) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci its_mask_encode(&cmd->raw_cmd[0], en, 8, 8); 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic inline void its_fixup_cmd(struct its_cmd_block *cmd) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci /* Let's fixup BE commands */ 6028c2ecf20Sopenharmony_ci cmd->raw_cmd_le[0] = cpu_to_le64(cmd->raw_cmd[0]); 6038c2ecf20Sopenharmony_ci cmd->raw_cmd_le[1] = cpu_to_le64(cmd->raw_cmd[1]); 6048c2ecf20Sopenharmony_ci cmd->raw_cmd_le[2] = cpu_to_le64(cmd->raw_cmd[2]); 6058c2ecf20Sopenharmony_ci cmd->raw_cmd_le[3] = cpu_to_le64(cmd->raw_cmd[3]); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic struct its_collection *its_build_mapd_cmd(struct its_node *its, 6098c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 6108c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci unsigned long itt_addr; 6138c2ecf20Sopenharmony_ci u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt); 6168c2ecf20Sopenharmony_ci itt_addr = ALIGN(itt_addr, ITS_ITT_ALIGN); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_MAPD); 6198c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_mapd_cmd.dev->device_id); 6208c2ecf20Sopenharmony_ci its_encode_size(cmd, size - 1); 6218c2ecf20Sopenharmony_ci its_encode_itt(cmd, itt_addr); 6228c2ecf20Sopenharmony_ci its_encode_valid(cmd, desc->its_mapd_cmd.valid); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return NULL; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic struct its_collection *its_build_mapc_cmd(struct its_node *its, 6308c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 6318c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_MAPC); 6348c2ecf20Sopenharmony_ci its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); 6358c2ecf20Sopenharmony_ci its_encode_target(cmd, desc->its_mapc_cmd.col->target_address); 6368c2ecf20Sopenharmony_ci its_encode_valid(cmd, desc->its_mapc_cmd.valid); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return desc->its_mapc_cmd.col; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic struct its_collection *its_build_mapti_cmd(struct its_node *its, 6448c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 6458c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct its_collection *col; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci col = dev_event_to_col(desc->its_mapti_cmd.dev, 6508c2ecf20Sopenharmony_ci desc->its_mapti_cmd.event_id); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_MAPTI); 6538c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_mapti_cmd.dev->device_id); 6548c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_mapti_cmd.event_id); 6558c2ecf20Sopenharmony_ci its_encode_phys_id(cmd, desc->its_mapti_cmd.phys_id); 6568c2ecf20Sopenharmony_ci its_encode_collection(cmd, col->col_id); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return valid_col(col); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic struct its_collection *its_build_movi_cmd(struct its_node *its, 6648c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 6658c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct its_collection *col; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci col = dev_event_to_col(desc->its_movi_cmd.dev, 6708c2ecf20Sopenharmony_ci desc->its_movi_cmd.event_id); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_MOVI); 6738c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_movi_cmd.dev->device_id); 6748c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_movi_cmd.event_id); 6758c2ecf20Sopenharmony_ci its_encode_collection(cmd, desc->its_movi_cmd.col->col_id); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return valid_col(col); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic struct its_collection *its_build_discard_cmd(struct its_node *its, 6838c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 6848c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct its_collection *col; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci col = dev_event_to_col(desc->its_discard_cmd.dev, 6898c2ecf20Sopenharmony_ci desc->its_discard_cmd.event_id); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_DISCARD); 6928c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_discard_cmd.dev->device_id); 6938c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_discard_cmd.event_id); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return valid_col(col); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic struct its_collection *its_build_inv_cmd(struct its_node *its, 7018c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 7028c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct its_collection *col; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci col = dev_event_to_col(desc->its_inv_cmd.dev, 7078c2ecf20Sopenharmony_ci desc->its_inv_cmd.event_id); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_INV); 7108c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id); 7118c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_inv_cmd.event_id); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return valid_col(col); 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic struct its_collection *its_build_int_cmd(struct its_node *its, 7198c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 7208c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct its_collection *col; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci col = dev_event_to_col(desc->its_int_cmd.dev, 7258c2ecf20Sopenharmony_ci desc->its_int_cmd.event_id); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_INT); 7288c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_int_cmd.dev->device_id); 7298c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_int_cmd.event_id); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return valid_col(col); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic struct its_collection *its_build_clear_cmd(struct its_node *its, 7378c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 7388c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct its_collection *col; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci col = dev_event_to_col(desc->its_clear_cmd.dev, 7438c2ecf20Sopenharmony_ci desc->its_clear_cmd.event_id); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_CLEAR); 7468c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_clear_cmd.dev->device_id); 7478c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_clear_cmd.event_id); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return valid_col(col); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic struct its_collection *its_build_invall_cmd(struct its_node *its, 7558c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 7568c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_INVALL); 7598c2ecf20Sopenharmony_ci its_encode_collection(cmd, desc->its_invall_cmd.col->col_id); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return desc->its_invall_cmd.col; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vinvall_cmd(struct its_node *its, 7678c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 7688c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_VINVALL); 7718c2ecf20Sopenharmony_ci its_encode_vpeid(cmd, desc->its_vinvall_cmd.vpe->vpe_id); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci return valid_vpe(its, desc->its_vinvall_cmd.vpe); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmapp_cmd(struct its_node *its, 7798c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 7808c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci unsigned long vpt_addr, vconf_addr; 7838c2ecf20Sopenharmony_ci u64 target; 7848c2ecf20Sopenharmony_ci bool alloc; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_VMAPP); 7878c2ecf20Sopenharmony_ci its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id); 7888c2ecf20Sopenharmony_ci its_encode_valid(cmd, desc->its_vmapp_cmd.valid); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (!desc->its_vmapp_cmd.valid) { 7918c2ecf20Sopenharmony_ci if (is_v4_1(its)) { 7928c2ecf20Sopenharmony_ci alloc = !atomic_dec_return(&desc->its_vmapp_cmd.vpe->vmapp_count); 7938c2ecf20Sopenharmony_ci its_encode_alloc(cmd, alloc); 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci goto out; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page)); 8008c2ecf20Sopenharmony_ci target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci its_encode_target(cmd, target); 8038c2ecf20Sopenharmony_ci its_encode_vpt_addr(cmd, vpt_addr); 8048c2ecf20Sopenharmony_ci its_encode_vpt_size(cmd, LPI_NRBITS - 1); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (!is_v4_1(its)) 8078c2ecf20Sopenharmony_ci goto out; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci vconf_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->its_vm->vprop_page)); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci alloc = !atomic_fetch_inc(&desc->its_vmapp_cmd.vpe->vmapp_count); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci its_encode_alloc(cmd, alloc); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci /* We can only signal PTZ when alloc==1. Why do we have two bits? */ 8168c2ecf20Sopenharmony_ci its_encode_ptz(cmd, alloc); 8178c2ecf20Sopenharmony_ci its_encode_vconf_addr(cmd, vconf_addr); 8188c2ecf20Sopenharmony_ci its_encode_vmapp_default_db(cmd, desc->its_vmapp_cmd.vpe->vpe_db_lpi); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ciout: 8218c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return valid_vpe(its, desc->its_vmapp_cmd.vpe); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmapti_cmd(struct its_node *its, 8278c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 8288c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci u32 db; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (!is_v4_1(its) && desc->its_vmapti_cmd.db_enabled) 8338c2ecf20Sopenharmony_ci db = desc->its_vmapti_cmd.vpe->vpe_db_lpi; 8348c2ecf20Sopenharmony_ci else 8358c2ecf20Sopenharmony_ci db = 1023; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_VMAPTI); 8388c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_vmapti_cmd.dev->device_id); 8398c2ecf20Sopenharmony_ci its_encode_vpeid(cmd, desc->its_vmapti_cmd.vpe->vpe_id); 8408c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_vmapti_cmd.event_id); 8418c2ecf20Sopenharmony_ci its_encode_db_phys_id(cmd, db); 8428c2ecf20Sopenharmony_ci its_encode_virt_id(cmd, desc->its_vmapti_cmd.virt_id); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return valid_vpe(its, desc->its_vmapti_cmd.vpe); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmovi_cmd(struct its_node *its, 8508c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 8518c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci u32 db; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (!is_v4_1(its) && desc->its_vmovi_cmd.db_enabled) 8568c2ecf20Sopenharmony_ci db = desc->its_vmovi_cmd.vpe->vpe_db_lpi; 8578c2ecf20Sopenharmony_ci else 8588c2ecf20Sopenharmony_ci db = 1023; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_VMOVI); 8618c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_vmovi_cmd.dev->device_id); 8628c2ecf20Sopenharmony_ci its_encode_vpeid(cmd, desc->its_vmovi_cmd.vpe->vpe_id); 8638c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_vmovi_cmd.event_id); 8648c2ecf20Sopenharmony_ci its_encode_db_phys_id(cmd, db); 8658c2ecf20Sopenharmony_ci its_encode_db_valid(cmd, true); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return valid_vpe(its, desc->its_vmovi_cmd.vpe); 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vmovp_cmd(struct its_node *its, 8738c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 8748c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci u64 target; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci target = desc->its_vmovp_cmd.col->target_address + its->vlpi_redist_offset; 8798c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_VMOVP); 8808c2ecf20Sopenharmony_ci its_encode_seq_num(cmd, desc->its_vmovp_cmd.seq_num); 8818c2ecf20Sopenharmony_ci its_encode_its_list(cmd, desc->its_vmovp_cmd.its_list); 8828c2ecf20Sopenharmony_ci its_encode_vpeid(cmd, desc->its_vmovp_cmd.vpe->vpe_id); 8838c2ecf20Sopenharmony_ci its_encode_target(cmd, target); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (is_v4_1(its)) { 8868c2ecf20Sopenharmony_ci its_encode_db(cmd, true); 8878c2ecf20Sopenharmony_ci its_encode_vmovp_default_db(cmd, desc->its_vmovp_cmd.vpe->vpe_db_lpi); 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return valid_vpe(its, desc->its_vmovp_cmd.vpe); 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vinv_cmd(struct its_node *its, 8968c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 8978c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci struct its_vlpi_map *map; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci map = dev_event_to_vlpi_map(desc->its_inv_cmd.dev, 9028c2ecf20Sopenharmony_ci desc->its_inv_cmd.event_id); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_INV); 9058c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id); 9068c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_inv_cmd.event_id); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return valid_vpe(its, map->vpe); 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vint_cmd(struct its_node *its, 9148c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 9158c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci struct its_vlpi_map *map; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci map = dev_event_to_vlpi_map(desc->its_int_cmd.dev, 9208c2ecf20Sopenharmony_ci desc->its_int_cmd.event_id); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_INT); 9238c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_int_cmd.dev->device_id); 9248c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_int_cmd.event_id); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci return valid_vpe(its, map->vpe); 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vclear_cmd(struct its_node *its, 9328c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 9338c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct its_vlpi_map *map; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci map = dev_event_to_vlpi_map(desc->its_clear_cmd.dev, 9388c2ecf20Sopenharmony_ci desc->its_clear_cmd.event_id); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_CLEAR); 9418c2ecf20Sopenharmony_ci its_encode_devid(cmd, desc->its_clear_cmd.dev->device_id); 9428c2ecf20Sopenharmony_ci its_encode_event_id(cmd, desc->its_clear_cmd.event_id); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return valid_vpe(its, map->vpe); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_invdb_cmd(struct its_node *its, 9508c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 9518c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci if (WARN_ON(!is_v4_1(its))) 9548c2ecf20Sopenharmony_ci return NULL; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_INVDB); 9578c2ecf20Sopenharmony_ci its_encode_vpeid(cmd, desc->its_invdb_cmd.vpe->vpe_id); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return valid_vpe(its, desc->its_invdb_cmd.vpe); 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic struct its_vpe *its_build_vsgi_cmd(struct its_node *its, 9658c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, 9668c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci if (WARN_ON(!is_v4_1(its))) 9698c2ecf20Sopenharmony_ci return NULL; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci its_encode_cmd(cmd, GITS_CMD_VSGI); 9728c2ecf20Sopenharmony_ci its_encode_vpeid(cmd, desc->its_vsgi_cmd.vpe->vpe_id); 9738c2ecf20Sopenharmony_ci its_encode_sgi_intid(cmd, desc->its_vsgi_cmd.sgi); 9748c2ecf20Sopenharmony_ci its_encode_sgi_priority(cmd, desc->its_vsgi_cmd.priority); 9758c2ecf20Sopenharmony_ci its_encode_sgi_group(cmd, desc->its_vsgi_cmd.group); 9768c2ecf20Sopenharmony_ci its_encode_sgi_clear(cmd, desc->its_vsgi_cmd.clear); 9778c2ecf20Sopenharmony_ci its_encode_sgi_enable(cmd, desc->its_vsgi_cmd.enable); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci its_fixup_cmd(cmd); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci return valid_vpe(its, desc->its_vsgi_cmd.vpe); 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic u64 its_cmd_ptr_to_offset(struct its_node *its, 9858c2ecf20Sopenharmony_ci struct its_cmd_block *ptr) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci return (ptr - its->cmd_base) * sizeof(*ptr); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic int its_queue_full(struct its_node *its) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci int widx; 9938c2ecf20Sopenharmony_ci int ridx; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci widx = its->cmd_write - its->cmd_base; 9968c2ecf20Sopenharmony_ci ridx = readl_relaxed(its->base + GITS_CREADR) / sizeof(struct its_cmd_block); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* This is incredibly unlikely to happen, unless the ITS locks up. */ 9998c2ecf20Sopenharmony_ci if (((widx + 1) % ITS_CMD_QUEUE_NR_ENTRIES) == ridx) 10008c2ecf20Sopenharmony_ci return 1; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return 0; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic struct its_cmd_block *its_allocate_entry(struct its_node *its) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct its_cmd_block *cmd; 10088c2ecf20Sopenharmony_ci u32 count = 1000000; /* 1s! */ 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci while (its_queue_full(its)) { 10118c2ecf20Sopenharmony_ci count--; 10128c2ecf20Sopenharmony_ci if (!count) { 10138c2ecf20Sopenharmony_ci pr_err_ratelimited("ITS queue not draining\n"); 10148c2ecf20Sopenharmony_ci return NULL; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci cpu_relax(); 10178c2ecf20Sopenharmony_ci udelay(1); 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci cmd = its->cmd_write++; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* Handle queue wrapping */ 10238c2ecf20Sopenharmony_ci if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES)) 10248c2ecf20Sopenharmony_ci its->cmd_write = its->cmd_base; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Clear command */ 10278c2ecf20Sopenharmony_ci cmd->raw_cmd[0] = 0; 10288c2ecf20Sopenharmony_ci cmd->raw_cmd[1] = 0; 10298c2ecf20Sopenharmony_ci cmd->raw_cmd[2] = 0; 10308c2ecf20Sopenharmony_ci cmd->raw_cmd[3] = 0; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci return cmd; 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic struct its_cmd_block *its_post_commands(struct its_node *its) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci u64 wr = its_cmd_ptr_to_offset(its, its->cmd_write); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci writel_relaxed(wr, its->base + GITS_CWRITER); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci return its->cmd_write; 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci /* 10478c2ecf20Sopenharmony_ci * Make sure the commands written to memory are observable by 10488c2ecf20Sopenharmony_ci * the ITS. 10498c2ecf20Sopenharmony_ci */ 10508c2ecf20Sopenharmony_ci if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING) 10518c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(cmd, sizeof(*cmd)); 10528c2ecf20Sopenharmony_ci else 10538c2ecf20Sopenharmony_ci dsb(ishst); 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic int its_wait_for_range_completion(struct its_node *its, 10578c2ecf20Sopenharmony_ci u64 prev_idx, 10588c2ecf20Sopenharmony_ci struct its_cmd_block *to) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci u64 rd_idx, to_idx, linear_idx; 10618c2ecf20Sopenharmony_ci u32 count = 1000000; /* 1s! */ 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* Linearize to_idx if the command set has wrapped around */ 10648c2ecf20Sopenharmony_ci to_idx = its_cmd_ptr_to_offset(its, to); 10658c2ecf20Sopenharmony_ci if (to_idx < prev_idx) 10668c2ecf20Sopenharmony_ci to_idx += ITS_CMD_QUEUE_SZ; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci linear_idx = prev_idx; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci while (1) { 10718c2ecf20Sopenharmony_ci s64 delta; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci rd_idx = readl_relaxed(its->base + GITS_CREADR); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci /* 10768c2ecf20Sopenharmony_ci * Compute the read pointer progress, taking the 10778c2ecf20Sopenharmony_ci * potential wrap-around into account. 10788c2ecf20Sopenharmony_ci */ 10798c2ecf20Sopenharmony_ci delta = rd_idx - prev_idx; 10808c2ecf20Sopenharmony_ci if (rd_idx < prev_idx) 10818c2ecf20Sopenharmony_ci delta += ITS_CMD_QUEUE_SZ; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci linear_idx += delta; 10848c2ecf20Sopenharmony_ci if (linear_idx >= to_idx) 10858c2ecf20Sopenharmony_ci break; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci count--; 10888c2ecf20Sopenharmony_ci if (!count) { 10898c2ecf20Sopenharmony_ci pr_err_ratelimited("ITS queue timeout (%llu %llu)\n", 10908c2ecf20Sopenharmony_ci to_idx, linear_idx); 10918c2ecf20Sopenharmony_ci return -1; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci prev_idx = rd_idx; 10948c2ecf20Sopenharmony_ci cpu_relax(); 10958c2ecf20Sopenharmony_ci udelay(1); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/* Warning, macro hell follows */ 11028c2ecf20Sopenharmony_ci#define BUILD_SINGLE_CMD_FUNC(name, buildtype, synctype, buildfn) \ 11038c2ecf20Sopenharmony_civoid name(struct its_node *its, \ 11048c2ecf20Sopenharmony_ci buildtype builder, \ 11058c2ecf20Sopenharmony_ci struct its_cmd_desc *desc) \ 11068c2ecf20Sopenharmony_ci{ \ 11078c2ecf20Sopenharmony_ci struct its_cmd_block *cmd, *sync_cmd, *next_cmd; \ 11088c2ecf20Sopenharmony_ci synctype *sync_obj; \ 11098c2ecf20Sopenharmony_ci unsigned long flags; \ 11108c2ecf20Sopenharmony_ci u64 rd_idx; \ 11118c2ecf20Sopenharmony_ci \ 11128c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&its->lock, flags); \ 11138c2ecf20Sopenharmony_ci \ 11148c2ecf20Sopenharmony_ci cmd = its_allocate_entry(its); \ 11158c2ecf20Sopenharmony_ci if (!cmd) { /* We're soooooo screewed... */ \ 11168c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&its->lock, flags); \ 11178c2ecf20Sopenharmony_ci return; \ 11188c2ecf20Sopenharmony_ci } \ 11198c2ecf20Sopenharmony_ci sync_obj = builder(its, cmd, desc); \ 11208c2ecf20Sopenharmony_ci its_flush_cmd(its, cmd); \ 11218c2ecf20Sopenharmony_ci \ 11228c2ecf20Sopenharmony_ci if (sync_obj) { \ 11238c2ecf20Sopenharmony_ci sync_cmd = its_allocate_entry(its); \ 11248c2ecf20Sopenharmony_ci if (!sync_cmd) \ 11258c2ecf20Sopenharmony_ci goto post; \ 11268c2ecf20Sopenharmony_ci \ 11278c2ecf20Sopenharmony_ci buildfn(its, sync_cmd, sync_obj); \ 11288c2ecf20Sopenharmony_ci its_flush_cmd(its, sync_cmd); \ 11298c2ecf20Sopenharmony_ci } \ 11308c2ecf20Sopenharmony_ci \ 11318c2ecf20Sopenharmony_cipost: \ 11328c2ecf20Sopenharmony_ci rd_idx = readl_relaxed(its->base + GITS_CREADR); \ 11338c2ecf20Sopenharmony_ci next_cmd = its_post_commands(its); \ 11348c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&its->lock, flags); \ 11358c2ecf20Sopenharmony_ci \ 11368c2ecf20Sopenharmony_ci if (its_wait_for_range_completion(its, rd_idx, next_cmd)) \ 11378c2ecf20Sopenharmony_ci pr_err_ratelimited("ITS cmd %ps failed\n", builder); \ 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic void its_build_sync_cmd(struct its_node *its, 11418c2ecf20Sopenharmony_ci struct its_cmd_block *sync_cmd, 11428c2ecf20Sopenharmony_ci struct its_collection *sync_col) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci its_encode_cmd(sync_cmd, GITS_CMD_SYNC); 11458c2ecf20Sopenharmony_ci its_encode_target(sync_cmd, sync_col->target_address); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci its_fixup_cmd(sync_cmd); 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic BUILD_SINGLE_CMD_FUNC(its_send_single_command, its_cmd_builder_t, 11518c2ecf20Sopenharmony_ci struct its_collection, its_build_sync_cmd) 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic void its_build_vsync_cmd(struct its_node *its, 11548c2ecf20Sopenharmony_ci struct its_cmd_block *sync_cmd, 11558c2ecf20Sopenharmony_ci struct its_vpe *sync_vpe) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci its_encode_cmd(sync_cmd, GITS_CMD_VSYNC); 11588c2ecf20Sopenharmony_ci its_encode_vpeid(sync_cmd, sync_vpe->vpe_id); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci its_fixup_cmd(sync_cmd); 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic BUILD_SINGLE_CMD_FUNC(its_send_single_vcommand, its_cmd_vbuilder_t, 11648c2ecf20Sopenharmony_ci struct its_vpe, its_build_vsync_cmd) 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic void its_send_int(struct its_device *dev, u32 event_id) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci desc.its_int_cmd.dev = dev; 11718c2ecf20Sopenharmony_ci desc.its_int_cmd.event_id = event_id; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci its_send_single_command(dev->its, its_build_int_cmd, &desc); 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic void its_send_clear(struct its_device *dev, u32 event_id) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci desc.its_clear_cmd.dev = dev; 11818c2ecf20Sopenharmony_ci desc.its_clear_cmd.event_id = event_id; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci its_send_single_command(dev->its, its_build_clear_cmd, &desc); 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic void its_send_inv(struct its_device *dev, u32 event_id) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci desc.its_inv_cmd.dev = dev; 11918c2ecf20Sopenharmony_ci desc.its_inv_cmd.event_id = event_id; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci its_send_single_command(dev->its, its_build_inv_cmd, &desc); 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_cistatic void its_send_mapd(struct its_device *dev, int valid) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci desc.its_mapd_cmd.dev = dev; 12018c2ecf20Sopenharmony_ci desc.its_mapd_cmd.valid = !!valid; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci its_send_single_command(dev->its, its_build_mapd_cmd, &desc); 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic void its_send_mapc(struct its_node *its, struct its_collection *col, 12078c2ecf20Sopenharmony_ci int valid) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci desc.its_mapc_cmd.col = col; 12128c2ecf20Sopenharmony_ci desc.its_mapc_cmd.valid = !!valid; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci its_send_single_command(its, its_build_mapc_cmd, &desc); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic void its_send_mapti(struct its_device *dev, u32 irq_id, u32 id) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci desc.its_mapti_cmd.dev = dev; 12228c2ecf20Sopenharmony_ci desc.its_mapti_cmd.phys_id = irq_id; 12238c2ecf20Sopenharmony_ci desc.its_mapti_cmd.event_id = id; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci its_send_single_command(dev->its, its_build_mapti_cmd, &desc); 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cistatic void its_send_movi(struct its_device *dev, 12298c2ecf20Sopenharmony_ci struct its_collection *col, u32 id) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci desc.its_movi_cmd.dev = dev; 12348c2ecf20Sopenharmony_ci desc.its_movi_cmd.col = col; 12358c2ecf20Sopenharmony_ci desc.its_movi_cmd.event_id = id; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci its_send_single_command(dev->its, its_build_movi_cmd, &desc); 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic void its_send_discard(struct its_device *dev, u32 id) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci desc.its_discard_cmd.dev = dev; 12458c2ecf20Sopenharmony_ci desc.its_discard_cmd.event_id = id; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci its_send_single_command(dev->its, its_build_discard_cmd, &desc); 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic void its_send_invall(struct its_node *its, struct its_collection *col) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci desc.its_invall_cmd.col = col; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci its_send_single_command(its, its_build_invall_cmd, &desc); 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_cistatic void its_send_vmapti(struct its_device *dev, u32 id) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id); 12628c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci desc.its_vmapti_cmd.vpe = map->vpe; 12658c2ecf20Sopenharmony_ci desc.its_vmapti_cmd.dev = dev; 12668c2ecf20Sopenharmony_ci desc.its_vmapti_cmd.virt_id = map->vintid; 12678c2ecf20Sopenharmony_ci desc.its_vmapti_cmd.event_id = id; 12688c2ecf20Sopenharmony_ci desc.its_vmapti_cmd.db_enabled = map->db_enabled; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci its_send_single_vcommand(dev->its, its_build_vmapti_cmd, &desc); 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void its_send_vmovi(struct its_device *dev, u32 id) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct its_vlpi_map *map = dev_event_to_vlpi_map(dev, id); 12768c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci desc.its_vmovi_cmd.vpe = map->vpe; 12798c2ecf20Sopenharmony_ci desc.its_vmovi_cmd.dev = dev; 12808c2ecf20Sopenharmony_ci desc.its_vmovi_cmd.event_id = id; 12818c2ecf20Sopenharmony_ci desc.its_vmovi_cmd.db_enabled = map->db_enabled; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci its_send_single_vcommand(dev->its, its_build_vmovi_cmd, &desc); 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic void its_send_vmapp(struct its_node *its, 12878c2ecf20Sopenharmony_ci struct its_vpe *vpe, bool valid) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci desc.its_vmapp_cmd.vpe = vpe; 12928c2ecf20Sopenharmony_ci desc.its_vmapp_cmd.valid = valid; 12938c2ecf20Sopenharmony_ci desc.its_vmapp_cmd.col = &its->collections[vpe->col_idx]; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci its_send_single_vcommand(its, its_build_vmapp_cmd, &desc); 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic void its_send_vmovp(struct its_vpe *vpe) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct its_cmd_desc desc = {}; 13018c2ecf20Sopenharmony_ci struct its_node *its; 13028c2ecf20Sopenharmony_ci unsigned long flags; 13038c2ecf20Sopenharmony_ci int col_id = vpe->col_idx; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci desc.its_vmovp_cmd.vpe = vpe; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci if (!its_list_map) { 13088c2ecf20Sopenharmony_ci its = list_first_entry(&its_nodes, struct its_node, entry); 13098c2ecf20Sopenharmony_ci desc.its_vmovp_cmd.col = &its->collections[col_id]; 13108c2ecf20Sopenharmony_ci its_send_single_vcommand(its, its_build_vmovp_cmd, &desc); 13118c2ecf20Sopenharmony_ci return; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* 13158c2ecf20Sopenharmony_ci * Yet another marvel of the architecture. If using the 13168c2ecf20Sopenharmony_ci * its_list "feature", we need to make sure that all ITSs 13178c2ecf20Sopenharmony_ci * receive all VMOVP commands in the same order. The only way 13188c2ecf20Sopenharmony_ci * to guarantee this is to make vmovp a serialization point. 13198c2ecf20Sopenharmony_ci * 13208c2ecf20Sopenharmony_ci * Wall <-- Head. 13218c2ecf20Sopenharmony_ci */ 13228c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vmovp_lock, flags); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci desc.its_vmovp_cmd.seq_num = vmovp_seq_num++; 13258c2ecf20Sopenharmony_ci desc.its_vmovp_cmd.its_list = get_its_list(vpe->its_vm); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* Emit VMOVPs */ 13288c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 13298c2ecf20Sopenharmony_ci if (!is_v4(its)) 13308c2ecf20Sopenharmony_ci continue; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci if (!require_its_list_vmovp(vpe->its_vm, its)) 13338c2ecf20Sopenharmony_ci continue; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci desc.its_vmovp_cmd.col = &its->collections[col_id]; 13368c2ecf20Sopenharmony_ci its_send_single_vcommand(its, its_build_vmovp_cmd, &desc); 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vmovp_lock, flags); 13408c2ecf20Sopenharmony_ci} 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic void its_send_vinvall(struct its_node *its, struct its_vpe *vpe) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci desc.its_vinvall_cmd.vpe = vpe; 13478c2ecf20Sopenharmony_ci its_send_single_vcommand(its, its_build_vinvall_cmd, &desc); 13488c2ecf20Sopenharmony_ci} 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistatic void its_send_vinv(struct its_device *dev, u32 event_id) 13518c2ecf20Sopenharmony_ci{ 13528c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* 13558c2ecf20Sopenharmony_ci * There is no real VINV command. This is just a normal INV, 13568c2ecf20Sopenharmony_ci * with a VSYNC instead of a SYNC. 13578c2ecf20Sopenharmony_ci */ 13588c2ecf20Sopenharmony_ci desc.its_inv_cmd.dev = dev; 13598c2ecf20Sopenharmony_ci desc.its_inv_cmd.event_id = event_id; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci its_send_single_vcommand(dev->its, its_build_vinv_cmd, &desc); 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic void its_send_vint(struct its_device *dev, u32 event_id) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci /* 13698c2ecf20Sopenharmony_ci * There is no real VINT command. This is just a normal INT, 13708c2ecf20Sopenharmony_ci * with a VSYNC instead of a SYNC. 13718c2ecf20Sopenharmony_ci */ 13728c2ecf20Sopenharmony_ci desc.its_int_cmd.dev = dev; 13738c2ecf20Sopenharmony_ci desc.its_int_cmd.event_id = event_id; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci its_send_single_vcommand(dev->its, its_build_vint_cmd, &desc); 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic void its_send_vclear(struct its_device *dev, u32 event_id) 13798c2ecf20Sopenharmony_ci{ 13808c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* 13838c2ecf20Sopenharmony_ci * There is no real VCLEAR command. This is just a normal CLEAR, 13848c2ecf20Sopenharmony_ci * with a VSYNC instead of a SYNC. 13858c2ecf20Sopenharmony_ci */ 13868c2ecf20Sopenharmony_ci desc.its_clear_cmd.dev = dev; 13878c2ecf20Sopenharmony_ci desc.its_clear_cmd.event_id = event_id; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci its_send_single_vcommand(dev->its, its_build_vclear_cmd, &desc); 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_cistatic void its_send_invdb(struct its_node *its, struct its_vpe *vpe) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci desc.its_invdb_cmd.vpe = vpe; 13978c2ecf20Sopenharmony_ci its_send_single_vcommand(its, its_build_invdb_cmd, &desc); 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci/* 14018c2ecf20Sopenharmony_ci * irqchip functions - assumes MSI, mostly. 14028c2ecf20Sopenharmony_ci */ 14038c2ecf20Sopenharmony_cistatic void lpi_write_config(struct irq_data *d, u8 clr, u8 set) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci struct its_vlpi_map *map = get_vlpi_map(d); 14068c2ecf20Sopenharmony_ci irq_hw_number_t hwirq; 14078c2ecf20Sopenharmony_ci void *va; 14088c2ecf20Sopenharmony_ci u8 *cfg; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci if (map) { 14118c2ecf20Sopenharmony_ci va = page_address(map->vm->vprop_page); 14128c2ecf20Sopenharmony_ci hwirq = map->vintid; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci /* Remember the updated property */ 14158c2ecf20Sopenharmony_ci map->properties &= ~clr; 14168c2ecf20Sopenharmony_ci map->properties |= set | LPI_PROP_GROUP1; 14178c2ecf20Sopenharmony_ci } else { 14188c2ecf20Sopenharmony_ci va = gic_rdists->prop_table_va; 14198c2ecf20Sopenharmony_ci hwirq = d->hwirq; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci cfg = va + hwirq - 8192; 14238c2ecf20Sopenharmony_ci *cfg &= ~clr; 14248c2ecf20Sopenharmony_ci *cfg |= set | LPI_PROP_GROUP1; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* 14278c2ecf20Sopenharmony_ci * Make the above write visible to the redistributors. 14288c2ecf20Sopenharmony_ci * And yes, we're flushing exactly: One. Single. Byte. 14298c2ecf20Sopenharmony_ci * Humpf... 14308c2ecf20Sopenharmony_ci */ 14318c2ecf20Sopenharmony_ci if (gic_rdists->flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING) 14328c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(cfg, sizeof(*cfg)); 14338c2ecf20Sopenharmony_ci else 14348c2ecf20Sopenharmony_ci dsb(ishst); 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic void wait_for_syncr(void __iomem *rdbase) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci while (readl_relaxed(rdbase + GICR_SYNCR) & 1) 14408c2ecf20Sopenharmony_ci cpu_relax(); 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic void __direct_lpi_inv(struct irq_data *d, u64 val) 14448c2ecf20Sopenharmony_ci{ 14458c2ecf20Sopenharmony_ci void __iomem *rdbase; 14468c2ecf20Sopenharmony_ci unsigned long flags; 14478c2ecf20Sopenharmony_ci int cpu; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* Target the redistributor this LPI is currently routed to */ 14508c2ecf20Sopenharmony_ci cpu = irq_to_cpuid_lock(d, &flags); 14518c2ecf20Sopenharmony_ci raw_spin_lock(&gic_data_rdist_cpu(cpu)->rd_lock); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci rdbase = per_cpu_ptr(gic_rdists->rdist, cpu)->rd_base; 14548c2ecf20Sopenharmony_ci gic_write_lpir(val, rdbase + GICR_INVLPIR); 14558c2ecf20Sopenharmony_ci wait_for_syncr(rdbase); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci raw_spin_unlock(&gic_data_rdist_cpu(cpu)->rd_lock); 14588c2ecf20Sopenharmony_ci irq_to_cpuid_unlock(d, flags); 14598c2ecf20Sopenharmony_ci} 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cistatic void direct_lpi_inv(struct irq_data *d) 14628c2ecf20Sopenharmony_ci{ 14638c2ecf20Sopenharmony_ci struct its_vlpi_map *map = get_vlpi_map(d); 14648c2ecf20Sopenharmony_ci u64 val; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (map) { 14678c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci WARN_ON(!is_v4_1(its_dev->its)); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci val = GICR_INVLPIR_V; 14728c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_INVLPIR_VPEID, map->vpe->vpe_id); 14738c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_INVLPIR_INTID, map->vintid); 14748c2ecf20Sopenharmony_ci } else { 14758c2ecf20Sopenharmony_ci val = d->hwirq; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci __direct_lpi_inv(d, val); 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic void lpi_update_config(struct irq_data *d, u8 clr, u8 set) 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci lpi_write_config(d, clr, set); 14868c2ecf20Sopenharmony_ci if (gic_rdists->has_direct_lpi && 14878c2ecf20Sopenharmony_ci (is_v4_1(its_dev->its) || !irqd_is_forwarded_to_vcpu(d))) 14888c2ecf20Sopenharmony_ci direct_lpi_inv(d); 14898c2ecf20Sopenharmony_ci else if (!irqd_is_forwarded_to_vcpu(d)) 14908c2ecf20Sopenharmony_ci its_send_inv(its_dev, its_get_event_id(d)); 14918c2ecf20Sopenharmony_ci else 14928c2ecf20Sopenharmony_ci its_send_vinv(its_dev, its_get_event_id(d)); 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic void its_vlpi_set_doorbell(struct irq_data *d, bool enable) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 14988c2ecf20Sopenharmony_ci u32 event = its_get_event_id(d); 14998c2ecf20Sopenharmony_ci struct its_vlpi_map *map; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci /* 15028c2ecf20Sopenharmony_ci * GICv4.1 does away with the per-LPI nonsense, nothing to do 15038c2ecf20Sopenharmony_ci * here. 15048c2ecf20Sopenharmony_ci */ 15058c2ecf20Sopenharmony_ci if (is_v4_1(its_dev->its)) 15068c2ecf20Sopenharmony_ci return; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci map = dev_event_to_vlpi_map(its_dev, event); 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (map->db_enabled == enable) 15118c2ecf20Sopenharmony_ci return; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci map->db_enabled = enable; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci /* 15168c2ecf20Sopenharmony_ci * More fun with the architecture: 15178c2ecf20Sopenharmony_ci * 15188c2ecf20Sopenharmony_ci * Ideally, we'd issue a VMAPTI to set the doorbell to its LPI 15198c2ecf20Sopenharmony_ci * value or to 1023, depending on the enable bit. But that 15208c2ecf20Sopenharmony_ci * would be issuing a mapping for an /existing/ DevID+EventID 15218c2ecf20Sopenharmony_ci * pair, which is UNPREDICTABLE. Instead, let's issue a VMOVI 15228c2ecf20Sopenharmony_ci * to the /same/ vPE, using this opportunity to adjust the 15238c2ecf20Sopenharmony_ci * doorbell. Mouahahahaha. We loves it, Precious. 15248c2ecf20Sopenharmony_ci */ 15258c2ecf20Sopenharmony_ci its_send_vmovi(its_dev, event); 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_cistatic void its_mask_irq(struct irq_data *d) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) 15318c2ecf20Sopenharmony_ci its_vlpi_set_doorbell(d, false); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci lpi_update_config(d, LPI_PROP_ENABLED, 0); 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_cistatic void its_unmask_irq(struct irq_data *d) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) 15398c2ecf20Sopenharmony_ci its_vlpi_set_doorbell(d, true); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci lpi_update_config(d, 0, LPI_PROP_ENABLED); 15428c2ecf20Sopenharmony_ci} 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_cistatic __maybe_unused u32 its_read_lpi_count(struct irq_data *d, int cpu) 15458c2ecf20Sopenharmony_ci{ 15468c2ecf20Sopenharmony_ci if (irqd_affinity_is_managed(d)) 15478c2ecf20Sopenharmony_ci return atomic_read(&per_cpu_ptr(&cpu_lpi_count, cpu)->managed); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci return atomic_read(&per_cpu_ptr(&cpu_lpi_count, cpu)->unmanaged); 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_cistatic void its_inc_lpi_count(struct irq_data *d, int cpu) 15538c2ecf20Sopenharmony_ci{ 15548c2ecf20Sopenharmony_ci if (irqd_affinity_is_managed(d)) 15558c2ecf20Sopenharmony_ci atomic_inc(&per_cpu_ptr(&cpu_lpi_count, cpu)->managed); 15568c2ecf20Sopenharmony_ci else 15578c2ecf20Sopenharmony_ci atomic_inc(&per_cpu_ptr(&cpu_lpi_count, cpu)->unmanaged); 15588c2ecf20Sopenharmony_ci} 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_cistatic void its_dec_lpi_count(struct irq_data *d, int cpu) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci if (irqd_affinity_is_managed(d)) 15638c2ecf20Sopenharmony_ci atomic_dec(&per_cpu_ptr(&cpu_lpi_count, cpu)->managed); 15648c2ecf20Sopenharmony_ci else 15658c2ecf20Sopenharmony_ci atomic_dec(&per_cpu_ptr(&cpu_lpi_count, cpu)->unmanaged); 15668c2ecf20Sopenharmony_ci} 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_cistatic unsigned int cpumask_pick_least_loaded(struct irq_data *d, 15698c2ecf20Sopenharmony_ci const struct cpumask *cpu_mask) 15708c2ecf20Sopenharmony_ci{ 15718c2ecf20Sopenharmony_ci unsigned int cpu = nr_cpu_ids, tmp; 15728c2ecf20Sopenharmony_ci int count = S32_MAX; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci for_each_cpu(tmp, cpu_mask) { 15758c2ecf20Sopenharmony_ci int this_count = its_read_lpi_count(d, tmp); 15768c2ecf20Sopenharmony_ci if (this_count < count) { 15778c2ecf20Sopenharmony_ci cpu = tmp; 15788c2ecf20Sopenharmony_ci count = this_count; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci return cpu; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci/* 15868c2ecf20Sopenharmony_ci * As suggested by Thomas Gleixner in: 15878c2ecf20Sopenharmony_ci * https://lore.kernel.org/r/87h80q2aoc.fsf@nanos.tec.linutronix.de 15888c2ecf20Sopenharmony_ci */ 15898c2ecf20Sopenharmony_cistatic int its_select_cpu(struct irq_data *d, 15908c2ecf20Sopenharmony_ci const struct cpumask *aff_mask) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 15938c2ecf20Sopenharmony_ci cpumask_var_t tmpmask; 15948c2ecf20Sopenharmony_ci int cpu, node; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (!alloc_cpumask_var(&tmpmask, GFP_ATOMIC)) 15978c2ecf20Sopenharmony_ci return -ENOMEM; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci node = its_dev->its->numa_node; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (!irqd_affinity_is_managed(d)) { 16028c2ecf20Sopenharmony_ci /* First try the NUMA node */ 16038c2ecf20Sopenharmony_ci if (node != NUMA_NO_NODE) { 16048c2ecf20Sopenharmony_ci /* 16058c2ecf20Sopenharmony_ci * Try the intersection of the affinity mask and the 16068c2ecf20Sopenharmony_ci * node mask (and the online mask, just to be safe). 16078c2ecf20Sopenharmony_ci */ 16088c2ecf20Sopenharmony_ci cpumask_and(tmpmask, cpumask_of_node(node), aff_mask); 16098c2ecf20Sopenharmony_ci cpumask_and(tmpmask, tmpmask, cpu_online_mask); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci /* 16128c2ecf20Sopenharmony_ci * Ideally, we would check if the mask is empty, and 16138c2ecf20Sopenharmony_ci * try again on the full node here. 16148c2ecf20Sopenharmony_ci * 16158c2ecf20Sopenharmony_ci * But it turns out that the way ACPI describes the 16168c2ecf20Sopenharmony_ci * affinity for ITSs only deals about memory, and 16178c2ecf20Sopenharmony_ci * not target CPUs, so it cannot describe a single 16188c2ecf20Sopenharmony_ci * ITS placed next to two NUMA nodes. 16198c2ecf20Sopenharmony_ci * 16208c2ecf20Sopenharmony_ci * Instead, just fallback on the online mask. This 16218c2ecf20Sopenharmony_ci * diverges from Thomas' suggestion above. 16228c2ecf20Sopenharmony_ci */ 16238c2ecf20Sopenharmony_ci cpu = cpumask_pick_least_loaded(d, tmpmask); 16248c2ecf20Sopenharmony_ci if (cpu < nr_cpu_ids) 16258c2ecf20Sopenharmony_ci goto out; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci /* If we can't cross sockets, give up */ 16288c2ecf20Sopenharmony_ci if ((its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144)) 16298c2ecf20Sopenharmony_ci goto out; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci /* If the above failed, expand the search */ 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* Try the intersection of the affinity and online masks */ 16358c2ecf20Sopenharmony_ci cpumask_and(tmpmask, aff_mask, cpu_online_mask); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci /* If that doesn't fly, the online mask is the last resort */ 16388c2ecf20Sopenharmony_ci if (cpumask_empty(tmpmask)) 16398c2ecf20Sopenharmony_ci cpumask_copy(tmpmask, cpu_online_mask); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci cpu = cpumask_pick_least_loaded(d, tmpmask); 16428c2ecf20Sopenharmony_ci } else { 16438c2ecf20Sopenharmony_ci cpumask_copy(tmpmask, aff_mask); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci /* If we cannot cross sockets, limit the search to that node */ 16468c2ecf20Sopenharmony_ci if ((its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) && 16478c2ecf20Sopenharmony_ci node != NUMA_NO_NODE) 16488c2ecf20Sopenharmony_ci cpumask_and(tmpmask, tmpmask, cpumask_of_node(node)); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci cpu = cpumask_pick_least_loaded(d, tmpmask); 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ciout: 16538c2ecf20Sopenharmony_ci free_cpumask_var(tmpmask); 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci pr_debug("IRQ%d -> %*pbl CPU%d\n", d->irq, cpumask_pr_args(aff_mask), cpu); 16568c2ecf20Sopenharmony_ci return cpu; 16578c2ecf20Sopenharmony_ci} 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cistatic int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val, 16608c2ecf20Sopenharmony_ci bool force) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 16638c2ecf20Sopenharmony_ci struct its_collection *target_col; 16648c2ecf20Sopenharmony_ci u32 id = its_get_event_id(d); 16658c2ecf20Sopenharmony_ci int cpu, prev_cpu; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci /* A forwarded interrupt should use irq_set_vcpu_affinity */ 16688c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) 16698c2ecf20Sopenharmony_ci return -EINVAL; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci prev_cpu = its_dev->event_map.col_map[id]; 16728c2ecf20Sopenharmony_ci its_dec_lpi_count(d, prev_cpu); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (!force) 16758c2ecf20Sopenharmony_ci cpu = its_select_cpu(d, mask_val); 16768c2ecf20Sopenharmony_ci else 16778c2ecf20Sopenharmony_ci cpu = cpumask_pick_least_loaded(d, mask_val); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci if (cpu < 0 || cpu >= nr_cpu_ids) 16808c2ecf20Sopenharmony_ci goto err; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci /* don't set the affinity when the target cpu is same as current one */ 16838c2ecf20Sopenharmony_ci if (cpu != prev_cpu) { 16848c2ecf20Sopenharmony_ci target_col = &its_dev->its->collections[cpu]; 16858c2ecf20Sopenharmony_ci its_send_movi(its_dev, target_col, id); 16868c2ecf20Sopenharmony_ci its_dev->event_map.col_map[id] = cpu; 16878c2ecf20Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(cpu)); 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci its_inc_lpi_count(d, cpu); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK_DONE; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cierr: 16958c2ecf20Sopenharmony_ci its_inc_lpi_count(d, prev_cpu); 16968c2ecf20Sopenharmony_ci return -EINVAL; 16978c2ecf20Sopenharmony_ci} 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_cistatic u64 its_irq_get_msi_base(struct its_device *its_dev) 17008c2ecf20Sopenharmony_ci{ 17018c2ecf20Sopenharmony_ci struct its_node *its = its_dev->its; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci return its->phys_base + GITS_TRANSLATER; 17048c2ecf20Sopenharmony_ci} 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_cistatic void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) 17078c2ecf20Sopenharmony_ci{ 17088c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 17098c2ecf20Sopenharmony_ci struct its_node *its; 17108c2ecf20Sopenharmony_ci u64 addr; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci its = its_dev->its; 17138c2ecf20Sopenharmony_ci addr = its->get_msi_base(its_dev); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci msg->address_lo = lower_32_bits(addr); 17168c2ecf20Sopenharmony_ci msg->address_hi = upper_32_bits(addr); 17178c2ecf20Sopenharmony_ci msg->data = its_get_event_id(d); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg); 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_cistatic int its_irq_set_irqchip_state(struct irq_data *d, 17238c2ecf20Sopenharmony_ci enum irqchip_irq_state which, 17248c2ecf20Sopenharmony_ci bool state) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 17278c2ecf20Sopenharmony_ci u32 event = its_get_event_id(d); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci if (which != IRQCHIP_STATE_PENDING) 17308c2ecf20Sopenharmony_ci return -EINVAL; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) { 17338c2ecf20Sopenharmony_ci if (state) 17348c2ecf20Sopenharmony_ci its_send_vint(its_dev, event); 17358c2ecf20Sopenharmony_ci else 17368c2ecf20Sopenharmony_ci its_send_vclear(its_dev, event); 17378c2ecf20Sopenharmony_ci } else { 17388c2ecf20Sopenharmony_ci if (state) 17398c2ecf20Sopenharmony_ci its_send_int(its_dev, event); 17408c2ecf20Sopenharmony_ci else 17418c2ecf20Sopenharmony_ci its_send_clear(its_dev, event); 17428c2ecf20Sopenharmony_ci } 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return 0; 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int its_irq_retrigger(struct irq_data *d) 17488c2ecf20Sopenharmony_ci{ 17498c2ecf20Sopenharmony_ci return !its_irq_set_irqchip_state(d, IRQCHIP_STATE_PENDING, true); 17508c2ecf20Sopenharmony_ci} 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci/* 17538c2ecf20Sopenharmony_ci * Two favourable cases: 17548c2ecf20Sopenharmony_ci * 17558c2ecf20Sopenharmony_ci * (a) Either we have a GICv4.1, and all vPEs have to be mapped at all times 17568c2ecf20Sopenharmony_ci * for vSGI delivery 17578c2ecf20Sopenharmony_ci * 17588c2ecf20Sopenharmony_ci * (b) Or the ITSs do not use a list map, meaning that VMOVP is cheap enough 17598c2ecf20Sopenharmony_ci * and we're better off mapping all VPEs always 17608c2ecf20Sopenharmony_ci * 17618c2ecf20Sopenharmony_ci * If neither (a) nor (b) is true, then we map vPEs on demand. 17628c2ecf20Sopenharmony_ci * 17638c2ecf20Sopenharmony_ci */ 17648c2ecf20Sopenharmony_cistatic bool gic_requires_eager_mapping(void) 17658c2ecf20Sopenharmony_ci{ 17668c2ecf20Sopenharmony_ci if (!its_list_map || gic_rdists->has_rvpeid) 17678c2ecf20Sopenharmony_ci return true; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci return false; 17708c2ecf20Sopenharmony_ci} 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_cistatic void its_map_vm(struct its_node *its, struct its_vm *vm) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci unsigned long flags; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci if (gic_requires_eager_mapping()) 17778c2ecf20Sopenharmony_ci return; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vmovp_lock, flags); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci /* 17828c2ecf20Sopenharmony_ci * If the VM wasn't mapped yet, iterate over the vpes and get 17838c2ecf20Sopenharmony_ci * them mapped now. 17848c2ecf20Sopenharmony_ci */ 17858c2ecf20Sopenharmony_ci vm->vlpi_count[its->list_nr]++; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci if (vm->vlpi_count[its->list_nr] == 1) { 17888c2ecf20Sopenharmony_ci int i; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci for (i = 0; i < vm->nr_vpes; i++) { 17918c2ecf20Sopenharmony_ci struct its_vpe *vpe = vm->vpes[i]; 17928c2ecf20Sopenharmony_ci struct irq_data *d = irq_get_irq_data(vpe->irq); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci /* Map the VPE to the first possible CPU */ 17958c2ecf20Sopenharmony_ci vpe->col_idx = cpumask_first(cpu_online_mask); 17968c2ecf20Sopenharmony_ci its_send_vmapp(its, vpe, true); 17978c2ecf20Sopenharmony_ci its_send_vinvall(its, vpe); 17988c2ecf20Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vmovp_lock, flags); 18038c2ecf20Sopenharmony_ci} 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_cistatic void its_unmap_vm(struct its_node *its, struct its_vm *vm) 18068c2ecf20Sopenharmony_ci{ 18078c2ecf20Sopenharmony_ci unsigned long flags; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci /* Not using the ITS list? Everything is always mapped. */ 18108c2ecf20Sopenharmony_ci if (gic_requires_eager_mapping()) 18118c2ecf20Sopenharmony_ci return; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vmovp_lock, flags); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (!--vm->vlpi_count[its->list_nr]) { 18168c2ecf20Sopenharmony_ci int i; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci for (i = 0; i < vm->nr_vpes; i++) 18198c2ecf20Sopenharmony_ci its_send_vmapp(its, vm->vpes[i], false); 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vmovp_lock, flags); 18238c2ecf20Sopenharmony_ci} 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_cistatic int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) 18268c2ecf20Sopenharmony_ci{ 18278c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 18288c2ecf20Sopenharmony_ci u32 event = its_get_event_id(d); 18298c2ecf20Sopenharmony_ci int ret = 0; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci if (!info->map) 18328c2ecf20Sopenharmony_ci return -EINVAL; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci raw_spin_lock(&its_dev->event_map.vlpi_lock); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (!its_dev->event_map.vm) { 18378c2ecf20Sopenharmony_ci struct its_vlpi_map *maps; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci maps = kcalloc(its_dev->event_map.nr_lpis, sizeof(*maps), 18408c2ecf20Sopenharmony_ci GFP_ATOMIC); 18418c2ecf20Sopenharmony_ci if (!maps) { 18428c2ecf20Sopenharmony_ci ret = -ENOMEM; 18438c2ecf20Sopenharmony_ci goto out; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci its_dev->event_map.vm = info->map->vm; 18478c2ecf20Sopenharmony_ci its_dev->event_map.vlpi_maps = maps; 18488c2ecf20Sopenharmony_ci } else if (its_dev->event_map.vm != info->map->vm) { 18498c2ecf20Sopenharmony_ci ret = -EINVAL; 18508c2ecf20Sopenharmony_ci goto out; 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci /* Get our private copy of the mapping information */ 18548c2ecf20Sopenharmony_ci its_dev->event_map.vlpi_maps[event] = *info->map; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci if (irqd_is_forwarded_to_vcpu(d)) { 18578c2ecf20Sopenharmony_ci /* Already mapped, move it around */ 18588c2ecf20Sopenharmony_ci its_send_vmovi(its_dev, event); 18598c2ecf20Sopenharmony_ci } else { 18608c2ecf20Sopenharmony_ci /* Ensure all the VPEs are mapped on this ITS */ 18618c2ecf20Sopenharmony_ci its_map_vm(its_dev->its, info->map->vm); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* 18648c2ecf20Sopenharmony_ci * Flag the interrupt as forwarded so that we can 18658c2ecf20Sopenharmony_ci * start poking the virtual property table. 18668c2ecf20Sopenharmony_ci */ 18678c2ecf20Sopenharmony_ci irqd_set_forwarded_to_vcpu(d); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci /* Write out the property to the prop table */ 18708c2ecf20Sopenharmony_ci lpi_write_config(d, 0xff, info->map->properties); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci /* Drop the physical mapping */ 18738c2ecf20Sopenharmony_ci its_send_discard(its_dev, event); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci /* and install the virtual one */ 18768c2ecf20Sopenharmony_ci its_send_vmapti(its_dev, event); 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci /* Increment the number of VLPIs */ 18798c2ecf20Sopenharmony_ci its_dev->event_map.nr_vlpis++; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ciout: 18838c2ecf20Sopenharmony_ci raw_spin_unlock(&its_dev->event_map.vlpi_lock); 18848c2ecf20Sopenharmony_ci return ret; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic int its_vlpi_get(struct irq_data *d, struct its_cmd_info *info) 18888c2ecf20Sopenharmony_ci{ 18898c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 18908c2ecf20Sopenharmony_ci struct its_vlpi_map *map; 18918c2ecf20Sopenharmony_ci int ret = 0; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci raw_spin_lock(&its_dev->event_map.vlpi_lock); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci map = get_vlpi_map(d); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci if (!its_dev->event_map.vm || !map) { 18988c2ecf20Sopenharmony_ci ret = -EINVAL; 18998c2ecf20Sopenharmony_ci goto out; 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci /* Copy our mapping information to the incoming request */ 19038c2ecf20Sopenharmony_ci *info->map = *map; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ciout: 19068c2ecf20Sopenharmony_ci raw_spin_unlock(&its_dev->event_map.vlpi_lock); 19078c2ecf20Sopenharmony_ci return ret; 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic int its_vlpi_unmap(struct irq_data *d) 19118c2ecf20Sopenharmony_ci{ 19128c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 19138c2ecf20Sopenharmony_ci u32 event = its_get_event_id(d); 19148c2ecf20Sopenharmony_ci int ret = 0; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci raw_spin_lock(&its_dev->event_map.vlpi_lock); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) { 19198c2ecf20Sopenharmony_ci ret = -EINVAL; 19208c2ecf20Sopenharmony_ci goto out; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci /* Drop the virtual mapping */ 19248c2ecf20Sopenharmony_ci its_send_discard(its_dev, event); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci /* and restore the physical one */ 19278c2ecf20Sopenharmony_ci irqd_clr_forwarded_to_vcpu(d); 19288c2ecf20Sopenharmony_ci its_send_mapti(its_dev, d->hwirq, event); 19298c2ecf20Sopenharmony_ci lpi_update_config(d, 0xff, (LPI_PROP_DEFAULT_PRIO | 19308c2ecf20Sopenharmony_ci LPI_PROP_ENABLED | 19318c2ecf20Sopenharmony_ci LPI_PROP_GROUP1)); 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci /* Potentially unmap the VM from this ITS */ 19348c2ecf20Sopenharmony_ci its_unmap_vm(its_dev->its, its_dev->event_map.vm); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci /* 19378c2ecf20Sopenharmony_ci * Drop the refcount and make the device available again if 19388c2ecf20Sopenharmony_ci * this was the last VLPI. 19398c2ecf20Sopenharmony_ci */ 19408c2ecf20Sopenharmony_ci if (!--its_dev->event_map.nr_vlpis) { 19418c2ecf20Sopenharmony_ci its_dev->event_map.vm = NULL; 19428c2ecf20Sopenharmony_ci kfree(its_dev->event_map.vlpi_maps); 19438c2ecf20Sopenharmony_ci } 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ciout: 19468c2ecf20Sopenharmony_ci raw_spin_unlock(&its_dev->event_map.vlpi_lock); 19478c2ecf20Sopenharmony_ci return ret; 19488c2ecf20Sopenharmony_ci} 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_cistatic int its_vlpi_prop_update(struct irq_data *d, struct its_cmd_info *info) 19518c2ecf20Sopenharmony_ci{ 19528c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) 19558c2ecf20Sopenharmony_ci return -EINVAL; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci if (info->cmd_type == PROP_UPDATE_AND_INV_VLPI) 19588c2ecf20Sopenharmony_ci lpi_update_config(d, 0xff, info->config); 19598c2ecf20Sopenharmony_ci else 19608c2ecf20Sopenharmony_ci lpi_write_config(d, 0xff, info->config); 19618c2ecf20Sopenharmony_ci its_vlpi_set_doorbell(d, !!(info->config & LPI_PROP_ENABLED)); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci return 0; 19648c2ecf20Sopenharmony_ci} 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_cistatic int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) 19678c2ecf20Sopenharmony_ci{ 19688c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 19698c2ecf20Sopenharmony_ci struct its_cmd_info *info = vcpu_info; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci /* Need a v4 ITS */ 19728c2ecf20Sopenharmony_ci if (!is_v4(its_dev->its)) 19738c2ecf20Sopenharmony_ci return -EINVAL; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* Unmap request? */ 19768c2ecf20Sopenharmony_ci if (!info) 19778c2ecf20Sopenharmony_ci return its_vlpi_unmap(d); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci switch (info->cmd_type) { 19808c2ecf20Sopenharmony_ci case MAP_VLPI: 19818c2ecf20Sopenharmony_ci return its_vlpi_map(d, info); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci case GET_VLPI: 19848c2ecf20Sopenharmony_ci return its_vlpi_get(d, info); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci case PROP_UPDATE_VLPI: 19878c2ecf20Sopenharmony_ci case PROP_UPDATE_AND_INV_VLPI: 19888c2ecf20Sopenharmony_ci return its_vlpi_prop_update(d, info); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci default: 19918c2ecf20Sopenharmony_ci return -EINVAL; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci} 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_cistatic struct irq_chip its_irq_chip = { 19968c2ecf20Sopenharmony_ci .name = "ITS", 19978c2ecf20Sopenharmony_ci .irq_mask = its_mask_irq, 19988c2ecf20Sopenharmony_ci .irq_unmask = its_unmask_irq, 19998c2ecf20Sopenharmony_ci .irq_eoi = irq_chip_eoi_parent, 20008c2ecf20Sopenharmony_ci .irq_set_affinity = its_set_affinity, 20018c2ecf20Sopenharmony_ci .irq_compose_msi_msg = its_irq_compose_msi_msg, 20028c2ecf20Sopenharmony_ci .irq_set_irqchip_state = its_irq_set_irqchip_state, 20038c2ecf20Sopenharmony_ci .irq_retrigger = its_irq_retrigger, 20048c2ecf20Sopenharmony_ci .irq_set_vcpu_affinity = its_irq_set_vcpu_affinity, 20058c2ecf20Sopenharmony_ci}; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci/* 20098c2ecf20Sopenharmony_ci * How we allocate LPIs: 20108c2ecf20Sopenharmony_ci * 20118c2ecf20Sopenharmony_ci * lpi_range_list contains ranges of LPIs that are to available to 20128c2ecf20Sopenharmony_ci * allocate from. To allocate LPIs, just pick the first range that 20138c2ecf20Sopenharmony_ci * fits the required allocation, and reduce it by the required 20148c2ecf20Sopenharmony_ci * amount. Once empty, remove the range from the list. 20158c2ecf20Sopenharmony_ci * 20168c2ecf20Sopenharmony_ci * To free a range of LPIs, add a free range to the list, sort it and 20178c2ecf20Sopenharmony_ci * merge the result if the new range happens to be adjacent to an 20188c2ecf20Sopenharmony_ci * already free block. 20198c2ecf20Sopenharmony_ci * 20208c2ecf20Sopenharmony_ci * The consequence of the above is that allocation is cost is low, but 20218c2ecf20Sopenharmony_ci * freeing is expensive. We assumes that freeing rarely occurs. 20228c2ecf20Sopenharmony_ci */ 20238c2ecf20Sopenharmony_ci#define ITS_MAX_LPI_NRBITS 16 /* 64K LPIs */ 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(lpi_range_lock); 20268c2ecf20Sopenharmony_cistatic LIST_HEAD(lpi_range_list); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_cistruct lpi_range { 20298c2ecf20Sopenharmony_ci struct list_head entry; 20308c2ecf20Sopenharmony_ci u32 base_id; 20318c2ecf20Sopenharmony_ci u32 span; 20328c2ecf20Sopenharmony_ci}; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_cistatic struct lpi_range *mk_lpi_range(u32 base, u32 span) 20358c2ecf20Sopenharmony_ci{ 20368c2ecf20Sopenharmony_ci struct lpi_range *range; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci range = kmalloc(sizeof(*range), GFP_KERNEL); 20398c2ecf20Sopenharmony_ci if (range) { 20408c2ecf20Sopenharmony_ci range->base_id = base; 20418c2ecf20Sopenharmony_ci range->span = span; 20428c2ecf20Sopenharmony_ci } 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci return range; 20458c2ecf20Sopenharmony_ci} 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_cistatic int alloc_lpi_range(u32 nr_lpis, u32 *base) 20488c2ecf20Sopenharmony_ci{ 20498c2ecf20Sopenharmony_ci struct lpi_range *range, *tmp; 20508c2ecf20Sopenharmony_ci int err = -ENOSPC; 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_ci mutex_lock(&lpi_range_lock); 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) { 20558c2ecf20Sopenharmony_ci if (range->span >= nr_lpis) { 20568c2ecf20Sopenharmony_ci *base = range->base_id; 20578c2ecf20Sopenharmony_ci range->base_id += nr_lpis; 20588c2ecf20Sopenharmony_ci range->span -= nr_lpis; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (range->span == 0) { 20618c2ecf20Sopenharmony_ci list_del(&range->entry); 20628c2ecf20Sopenharmony_ci kfree(range); 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci err = 0; 20668c2ecf20Sopenharmony_ci break; 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci mutex_unlock(&lpi_range_lock); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci pr_debug("ITS: alloc %u:%u\n", *base, nr_lpis); 20738c2ecf20Sopenharmony_ci return err; 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_cistatic void merge_lpi_ranges(struct lpi_range *a, struct lpi_range *b) 20778c2ecf20Sopenharmony_ci{ 20788c2ecf20Sopenharmony_ci if (&a->entry == &lpi_range_list || &b->entry == &lpi_range_list) 20798c2ecf20Sopenharmony_ci return; 20808c2ecf20Sopenharmony_ci if (a->base_id + a->span != b->base_id) 20818c2ecf20Sopenharmony_ci return; 20828c2ecf20Sopenharmony_ci b->base_id = a->base_id; 20838c2ecf20Sopenharmony_ci b->span += a->span; 20848c2ecf20Sopenharmony_ci list_del(&a->entry); 20858c2ecf20Sopenharmony_ci kfree(a); 20868c2ecf20Sopenharmony_ci} 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_cistatic int free_lpi_range(u32 base, u32 nr_lpis) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci struct lpi_range *new, *old; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci new = mk_lpi_range(base, nr_lpis); 20938c2ecf20Sopenharmony_ci if (!new) 20948c2ecf20Sopenharmony_ci return -ENOMEM; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci mutex_lock(&lpi_range_lock); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci list_for_each_entry_reverse(old, &lpi_range_list, entry) { 20998c2ecf20Sopenharmony_ci if (old->base_id < base) 21008c2ecf20Sopenharmony_ci break; 21018c2ecf20Sopenharmony_ci } 21028c2ecf20Sopenharmony_ci /* 21038c2ecf20Sopenharmony_ci * old is the last element with ->base_id smaller than base, 21048c2ecf20Sopenharmony_ci * so new goes right after it. If there are no elements with 21058c2ecf20Sopenharmony_ci * ->base_id smaller than base, &old->entry ends up pointing 21068c2ecf20Sopenharmony_ci * at the head of the list, and inserting new it the start of 21078c2ecf20Sopenharmony_ci * the list is the right thing to do in that case as well. 21088c2ecf20Sopenharmony_ci */ 21098c2ecf20Sopenharmony_ci list_add(&new->entry, &old->entry); 21108c2ecf20Sopenharmony_ci /* 21118c2ecf20Sopenharmony_ci * Now check if we can merge with the preceding and/or 21128c2ecf20Sopenharmony_ci * following ranges. 21138c2ecf20Sopenharmony_ci */ 21148c2ecf20Sopenharmony_ci merge_lpi_ranges(old, new); 21158c2ecf20Sopenharmony_ci merge_lpi_ranges(new, list_next_entry(new, entry)); 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci mutex_unlock(&lpi_range_lock); 21188c2ecf20Sopenharmony_ci return 0; 21198c2ecf20Sopenharmony_ci} 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_cistatic int __init its_lpi_init(u32 id_bits) 21228c2ecf20Sopenharmony_ci{ 21238c2ecf20Sopenharmony_ci u32 lpis = (1UL << id_bits) - 8192; 21248c2ecf20Sopenharmony_ci u32 numlpis; 21258c2ecf20Sopenharmony_ci int err; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci numlpis = 1UL << GICD_TYPER_NUM_LPIS(gic_rdists->gicd_typer); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci if (numlpis > 2 && !WARN_ON(numlpis > lpis)) { 21308c2ecf20Sopenharmony_ci lpis = numlpis; 21318c2ecf20Sopenharmony_ci pr_info("ITS: Using hypervisor restricted LPI range [%u]\n", 21328c2ecf20Sopenharmony_ci lpis); 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci /* 21368c2ecf20Sopenharmony_ci * Initializing the allocator is just the same as freeing the 21378c2ecf20Sopenharmony_ci * full range of LPIs. 21388c2ecf20Sopenharmony_ci */ 21398c2ecf20Sopenharmony_ci err = free_lpi_range(8192, lpis); 21408c2ecf20Sopenharmony_ci pr_debug("ITS: Allocator initialized for %u LPIs\n", lpis); 21418c2ecf20Sopenharmony_ci return err; 21428c2ecf20Sopenharmony_ci} 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_cistatic unsigned long *its_lpi_alloc(int nr_irqs, u32 *base, int *nr_ids) 21458c2ecf20Sopenharmony_ci{ 21468c2ecf20Sopenharmony_ci unsigned long *bitmap = NULL; 21478c2ecf20Sopenharmony_ci int err = 0; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci do { 21508c2ecf20Sopenharmony_ci err = alloc_lpi_range(nr_irqs, base); 21518c2ecf20Sopenharmony_ci if (!err) 21528c2ecf20Sopenharmony_ci break; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci nr_irqs /= 2; 21558c2ecf20Sopenharmony_ci } while (nr_irqs > 0); 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (!nr_irqs) 21588c2ecf20Sopenharmony_ci err = -ENOSPC; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci if (err) 21618c2ecf20Sopenharmony_ci goto out; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci bitmap = kcalloc(BITS_TO_LONGS(nr_irqs), sizeof (long), GFP_ATOMIC); 21648c2ecf20Sopenharmony_ci if (!bitmap) 21658c2ecf20Sopenharmony_ci goto out; 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci *nr_ids = nr_irqs; 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ciout: 21708c2ecf20Sopenharmony_ci if (!bitmap) 21718c2ecf20Sopenharmony_ci *base = *nr_ids = 0; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci return bitmap; 21748c2ecf20Sopenharmony_ci} 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_cistatic void its_lpi_free(unsigned long *bitmap, u32 base, u32 nr_ids) 21778c2ecf20Sopenharmony_ci{ 21788c2ecf20Sopenharmony_ci WARN_ON(free_lpi_range(base, nr_ids)); 21798c2ecf20Sopenharmony_ci kfree(bitmap); 21808c2ecf20Sopenharmony_ci} 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_cistatic void gic_reset_prop_table(void *va) 21838c2ecf20Sopenharmony_ci{ 21848c2ecf20Sopenharmony_ci /* Priority 0xa0, Group-1, disabled */ 21858c2ecf20Sopenharmony_ci memset(va, LPI_PROP_DEFAULT_PRIO | LPI_PROP_GROUP1, LPI_PROPBASE_SZ); 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci /* Make sure the GIC will observe the written configuration */ 21888c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(va, LPI_PROPBASE_SZ); 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_cistatic struct page *its_allocate_prop_table(gfp_t gfp_flags) 21928c2ecf20Sopenharmony_ci{ 21938c2ecf20Sopenharmony_ci struct page *prop_page; 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci prop_page = alloc_pages(gfp_flags, get_order(LPI_PROPBASE_SZ)); 21968c2ecf20Sopenharmony_ci if (!prop_page) 21978c2ecf20Sopenharmony_ci return NULL; 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci gic_reset_prop_table(page_address(prop_page)); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci return prop_page; 22028c2ecf20Sopenharmony_ci} 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_cistatic void its_free_prop_table(struct page *prop_page) 22058c2ecf20Sopenharmony_ci{ 22068c2ecf20Sopenharmony_ci free_pages((unsigned long)page_address(prop_page), 22078c2ecf20Sopenharmony_ci get_order(LPI_PROPBASE_SZ)); 22088c2ecf20Sopenharmony_ci} 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_cistatic bool gic_check_reserved_range(phys_addr_t addr, unsigned long size) 22118c2ecf20Sopenharmony_ci{ 22128c2ecf20Sopenharmony_ci phys_addr_t start, end, addr_end; 22138c2ecf20Sopenharmony_ci u64 i; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci /* 22168c2ecf20Sopenharmony_ci * We don't bother checking for a kdump kernel as by 22178c2ecf20Sopenharmony_ci * construction, the LPI tables are out of this kernel's 22188c2ecf20Sopenharmony_ci * memory map. 22198c2ecf20Sopenharmony_ci */ 22208c2ecf20Sopenharmony_ci if (is_kdump_kernel()) 22218c2ecf20Sopenharmony_ci return true; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci addr_end = addr + size - 1; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci for_each_reserved_mem_range(i, &start, &end) { 22268c2ecf20Sopenharmony_ci if (addr >= start && addr_end <= end) 22278c2ecf20Sopenharmony_ci return true; 22288c2ecf20Sopenharmony_ci } 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci /* Not found, not a good sign... */ 22318c2ecf20Sopenharmony_ci pr_warn("GICv3: Expected reserved range [%pa:%pa], not found\n", 22328c2ecf20Sopenharmony_ci &addr, &addr_end); 22338c2ecf20Sopenharmony_ci add_taint(TAINT_CRAP, LOCKDEP_STILL_OK); 22348c2ecf20Sopenharmony_ci return false; 22358c2ecf20Sopenharmony_ci} 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_cistatic int gic_reserve_range(phys_addr_t addr, unsigned long size) 22388c2ecf20Sopenharmony_ci{ 22398c2ecf20Sopenharmony_ci if (efi_enabled(EFI_CONFIG_TABLES)) 22408c2ecf20Sopenharmony_ci return efi_mem_reserve_persistent(addr, size); 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci return 0; 22438c2ecf20Sopenharmony_ci} 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_cistatic int __init its_setup_lpi_prop_table(void) 22468c2ecf20Sopenharmony_ci{ 22478c2ecf20Sopenharmony_ci if (gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) { 22488c2ecf20Sopenharmony_ci u64 val; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci val = gicr_read_propbaser(gic_data_rdist_rd_base() + GICR_PROPBASER); 22518c2ecf20Sopenharmony_ci lpi_id_bits = (val & GICR_PROPBASER_IDBITS_MASK) + 1; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci gic_rdists->prop_table_pa = val & GENMASK_ULL(51, 12); 22548c2ecf20Sopenharmony_ci gic_rdists->prop_table_va = memremap(gic_rdists->prop_table_pa, 22558c2ecf20Sopenharmony_ci LPI_PROPBASE_SZ, 22568c2ecf20Sopenharmony_ci MEMREMAP_WB); 22578c2ecf20Sopenharmony_ci gic_reset_prop_table(gic_rdists->prop_table_va); 22588c2ecf20Sopenharmony_ci } else { 22598c2ecf20Sopenharmony_ci struct page *page; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci lpi_id_bits = min_t(u32, 22628c2ecf20Sopenharmony_ci GICD_TYPER_ID_BITS(gic_rdists->gicd_typer), 22638c2ecf20Sopenharmony_ci ITS_MAX_LPI_NRBITS); 22648c2ecf20Sopenharmony_ci page = its_allocate_prop_table(GFP_NOWAIT); 22658c2ecf20Sopenharmony_ci if (!page) { 22668c2ecf20Sopenharmony_ci pr_err("Failed to allocate PROPBASE\n"); 22678c2ecf20Sopenharmony_ci return -ENOMEM; 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci gic_rdists->prop_table_pa = page_to_phys(page); 22718c2ecf20Sopenharmony_ci gic_rdists->prop_table_va = page_address(page); 22728c2ecf20Sopenharmony_ci WARN_ON(gic_reserve_range(gic_rdists->prop_table_pa, 22738c2ecf20Sopenharmony_ci LPI_PROPBASE_SZ)); 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci pr_info("GICv3: using LPI property table @%pa\n", 22778c2ecf20Sopenharmony_ci &gic_rdists->prop_table_pa); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci return its_lpi_init(lpi_id_bits); 22808c2ecf20Sopenharmony_ci} 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_cistatic const char *its_base_type_string[] = { 22838c2ecf20Sopenharmony_ci [GITS_BASER_TYPE_DEVICE] = "Devices", 22848c2ecf20Sopenharmony_ci [GITS_BASER_TYPE_VCPU] = "Virtual CPUs", 22858c2ecf20Sopenharmony_ci [GITS_BASER_TYPE_RESERVED3] = "Reserved (3)", 22868c2ecf20Sopenharmony_ci [GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections", 22878c2ecf20Sopenharmony_ci [GITS_BASER_TYPE_RESERVED5] = "Reserved (5)", 22888c2ecf20Sopenharmony_ci [GITS_BASER_TYPE_RESERVED6] = "Reserved (6)", 22898c2ecf20Sopenharmony_ci [GITS_BASER_TYPE_RESERVED7] = "Reserved (7)", 22908c2ecf20Sopenharmony_ci}; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_cistatic u64 its_read_baser(struct its_node *its, struct its_baser *baser) 22938c2ecf20Sopenharmony_ci{ 22948c2ecf20Sopenharmony_ci u32 idx = baser - its->tables; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci return gits_read_baser(its->base + GITS_BASER + (idx << 3)); 22978c2ecf20Sopenharmony_ci} 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_cistatic void its_write_baser(struct its_node *its, struct its_baser *baser, 23008c2ecf20Sopenharmony_ci u64 val) 23018c2ecf20Sopenharmony_ci{ 23028c2ecf20Sopenharmony_ci u32 idx = baser - its->tables; 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci gits_write_baser(val, its->base + GITS_BASER + (idx << 3)); 23058c2ecf20Sopenharmony_ci baser->val = its_read_baser(its, baser); 23068c2ecf20Sopenharmony_ci} 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_cistatic int its_setup_baser(struct its_node *its, struct its_baser *baser, 23098c2ecf20Sopenharmony_ci u64 cache, u64 shr, u32 order, bool indirect) 23108c2ecf20Sopenharmony_ci{ 23118c2ecf20Sopenharmony_ci u64 val = its_read_baser(its, baser); 23128c2ecf20Sopenharmony_ci u64 esz = GITS_BASER_ENTRY_SIZE(val); 23138c2ecf20Sopenharmony_ci u64 type = GITS_BASER_TYPE(val); 23148c2ecf20Sopenharmony_ci u64 baser_phys, tmp; 23158c2ecf20Sopenharmony_ci u32 alloc_pages, psz; 23168c2ecf20Sopenharmony_ci struct page *page; 23178c2ecf20Sopenharmony_ci void *base; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci psz = baser->psz; 23208c2ecf20Sopenharmony_ci alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz); 23218c2ecf20Sopenharmony_ci if (alloc_pages > GITS_BASER_PAGES_MAX) { 23228c2ecf20Sopenharmony_ci pr_warn("ITS@%pa: %s too large, reduce ITS pages %u->%u\n", 23238c2ecf20Sopenharmony_ci &its->phys_base, its_base_type_string[type], 23248c2ecf20Sopenharmony_ci alloc_pages, GITS_BASER_PAGES_MAX); 23258c2ecf20Sopenharmony_ci alloc_pages = GITS_BASER_PAGES_MAX; 23268c2ecf20Sopenharmony_ci order = get_order(GITS_BASER_PAGES_MAX * psz); 23278c2ecf20Sopenharmony_ci } 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO, order); 23308c2ecf20Sopenharmony_ci if (!page) 23318c2ecf20Sopenharmony_ci return -ENOMEM; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci base = (void *)page_address(page); 23348c2ecf20Sopenharmony_ci baser_phys = virt_to_phys(base); 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci /* Check if the physical address of the memory is above 48bits */ 23378c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM64_64K_PAGES) && (baser_phys >> 48)) { 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci /* 52bit PA is supported only when PageSize=64K */ 23408c2ecf20Sopenharmony_ci if (psz != SZ_64K) { 23418c2ecf20Sopenharmony_ci pr_err("ITS: no 52bit PA support when psz=%d\n", psz); 23428c2ecf20Sopenharmony_ci free_pages((unsigned long)base, order); 23438c2ecf20Sopenharmony_ci return -ENXIO; 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci /* Convert 52bit PA to 48bit field */ 23478c2ecf20Sopenharmony_ci baser_phys = GITS_BASER_PHYS_52_to_48(baser_phys); 23488c2ecf20Sopenharmony_ci } 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ciretry_baser: 23518c2ecf20Sopenharmony_ci val = (baser_phys | 23528c2ecf20Sopenharmony_ci (type << GITS_BASER_TYPE_SHIFT) | 23538c2ecf20Sopenharmony_ci ((esz - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | 23548c2ecf20Sopenharmony_ci ((alloc_pages - 1) << GITS_BASER_PAGES_SHIFT) | 23558c2ecf20Sopenharmony_ci cache | 23568c2ecf20Sopenharmony_ci shr | 23578c2ecf20Sopenharmony_ci GITS_BASER_VALID); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci val |= indirect ? GITS_BASER_INDIRECT : 0x0; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci switch (psz) { 23628c2ecf20Sopenharmony_ci case SZ_4K: 23638c2ecf20Sopenharmony_ci val |= GITS_BASER_PAGE_SIZE_4K; 23648c2ecf20Sopenharmony_ci break; 23658c2ecf20Sopenharmony_ci case SZ_16K: 23668c2ecf20Sopenharmony_ci val |= GITS_BASER_PAGE_SIZE_16K; 23678c2ecf20Sopenharmony_ci break; 23688c2ecf20Sopenharmony_ci case SZ_64K: 23698c2ecf20Sopenharmony_ci val |= GITS_BASER_PAGE_SIZE_64K; 23708c2ecf20Sopenharmony_ci break; 23718c2ecf20Sopenharmony_ci } 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci its_write_baser(its, baser, val); 23748c2ecf20Sopenharmony_ci tmp = baser->val; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { 23778c2ecf20Sopenharmony_ci /* 23788c2ecf20Sopenharmony_ci * Shareability didn't stick. Just use 23798c2ecf20Sopenharmony_ci * whatever the read reported, which is likely 23808c2ecf20Sopenharmony_ci * to be the only thing this redistributor 23818c2ecf20Sopenharmony_ci * supports. If that's zero, make it 23828c2ecf20Sopenharmony_ci * non-cacheable as well. 23838c2ecf20Sopenharmony_ci */ 23848c2ecf20Sopenharmony_ci shr = tmp & GITS_BASER_SHAREABILITY_MASK; 23858c2ecf20Sopenharmony_ci if (!shr) { 23868c2ecf20Sopenharmony_ci cache = GITS_BASER_nC; 23878c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(base, PAGE_ORDER_TO_SIZE(order)); 23888c2ecf20Sopenharmony_ci } 23898c2ecf20Sopenharmony_ci goto retry_baser; 23908c2ecf20Sopenharmony_ci } 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (val != tmp) { 23938c2ecf20Sopenharmony_ci pr_err("ITS@%pa: %s doesn't stick: %llx %llx\n", 23948c2ecf20Sopenharmony_ci &its->phys_base, its_base_type_string[type], 23958c2ecf20Sopenharmony_ci val, tmp); 23968c2ecf20Sopenharmony_ci free_pages((unsigned long)base, order); 23978c2ecf20Sopenharmony_ci return -ENXIO; 23988c2ecf20Sopenharmony_ci } 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci baser->order = order; 24018c2ecf20Sopenharmony_ci baser->base = base; 24028c2ecf20Sopenharmony_ci baser->psz = psz; 24038c2ecf20Sopenharmony_ci tmp = indirect ? GITS_LVL1_ENTRY_SIZE : esz; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci pr_info("ITS@%pa: allocated %d %s @%lx (%s, esz %d, psz %dK, shr %d)\n", 24068c2ecf20Sopenharmony_ci &its->phys_base, (int)(PAGE_ORDER_TO_SIZE(order) / (int)tmp), 24078c2ecf20Sopenharmony_ci its_base_type_string[type], 24088c2ecf20Sopenharmony_ci (unsigned long)virt_to_phys(base), 24098c2ecf20Sopenharmony_ci indirect ? "indirect" : "flat", (int)esz, 24108c2ecf20Sopenharmony_ci psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci return 0; 24138c2ecf20Sopenharmony_ci} 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cistatic bool its_parse_indirect_baser(struct its_node *its, 24168c2ecf20Sopenharmony_ci struct its_baser *baser, 24178c2ecf20Sopenharmony_ci u32 *order, u32 ids) 24188c2ecf20Sopenharmony_ci{ 24198c2ecf20Sopenharmony_ci u64 tmp = its_read_baser(its, baser); 24208c2ecf20Sopenharmony_ci u64 type = GITS_BASER_TYPE(tmp); 24218c2ecf20Sopenharmony_ci u64 esz = GITS_BASER_ENTRY_SIZE(tmp); 24228c2ecf20Sopenharmony_ci u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb; 24238c2ecf20Sopenharmony_ci u32 new_order = *order; 24248c2ecf20Sopenharmony_ci u32 psz = baser->psz; 24258c2ecf20Sopenharmony_ci bool indirect = false; 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_ci /* No need to enable Indirection if memory requirement < (psz*2)bytes */ 24288c2ecf20Sopenharmony_ci if ((esz << ids) > (psz * 2)) { 24298c2ecf20Sopenharmony_ci /* 24308c2ecf20Sopenharmony_ci * Find out whether hw supports a single or two-level table by 24318c2ecf20Sopenharmony_ci * table by reading bit at offset '62' after writing '1' to it. 24328c2ecf20Sopenharmony_ci */ 24338c2ecf20Sopenharmony_ci its_write_baser(its, baser, val | GITS_BASER_INDIRECT); 24348c2ecf20Sopenharmony_ci indirect = !!(baser->val & GITS_BASER_INDIRECT); 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci if (indirect) { 24378c2ecf20Sopenharmony_ci /* 24388c2ecf20Sopenharmony_ci * The size of the lvl2 table is equal to ITS page size 24398c2ecf20Sopenharmony_ci * which is 'psz'. For computing lvl1 table size, 24408c2ecf20Sopenharmony_ci * subtract ID bits that sparse lvl2 table from 'ids' 24418c2ecf20Sopenharmony_ci * which is reported by ITS hardware times lvl1 table 24428c2ecf20Sopenharmony_ci * entry size. 24438c2ecf20Sopenharmony_ci */ 24448c2ecf20Sopenharmony_ci ids -= ilog2(psz / (int)esz); 24458c2ecf20Sopenharmony_ci esz = GITS_LVL1_ENTRY_SIZE; 24468c2ecf20Sopenharmony_ci } 24478c2ecf20Sopenharmony_ci } 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci /* 24508c2ecf20Sopenharmony_ci * Allocate as many entries as required to fit the 24518c2ecf20Sopenharmony_ci * range of device IDs that the ITS can grok... The ID 24528c2ecf20Sopenharmony_ci * space being incredibly sparse, this results in a 24538c2ecf20Sopenharmony_ci * massive waste of memory if two-level device table 24548c2ecf20Sopenharmony_ci * feature is not supported by hardware. 24558c2ecf20Sopenharmony_ci */ 24568c2ecf20Sopenharmony_ci new_order = max_t(u32, get_order(esz << ids), new_order); 24578c2ecf20Sopenharmony_ci if (new_order >= MAX_ORDER) { 24588c2ecf20Sopenharmony_ci new_order = MAX_ORDER - 1; 24598c2ecf20Sopenharmony_ci ids = ilog2(PAGE_ORDER_TO_SIZE(new_order) / (int)esz); 24608c2ecf20Sopenharmony_ci pr_warn("ITS@%pa: %s Table too large, reduce ids %llu->%u\n", 24618c2ecf20Sopenharmony_ci &its->phys_base, its_base_type_string[type], 24628c2ecf20Sopenharmony_ci device_ids(its), ids); 24638c2ecf20Sopenharmony_ci } 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci *order = new_order; 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci return indirect; 24688c2ecf20Sopenharmony_ci} 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_cistatic u32 compute_common_aff(u64 val) 24718c2ecf20Sopenharmony_ci{ 24728c2ecf20Sopenharmony_ci u32 aff, clpiaff; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci aff = FIELD_GET(GICR_TYPER_AFFINITY, val); 24758c2ecf20Sopenharmony_ci clpiaff = FIELD_GET(GICR_TYPER_COMMON_LPI_AFF, val); 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci return aff & ~(GENMASK(31, 0) >> (clpiaff * 8)); 24788c2ecf20Sopenharmony_ci} 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_cistatic u32 compute_its_aff(struct its_node *its) 24818c2ecf20Sopenharmony_ci{ 24828c2ecf20Sopenharmony_ci u64 val; 24838c2ecf20Sopenharmony_ci u32 svpet; 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci /* 24868c2ecf20Sopenharmony_ci * Reencode the ITS SVPET and MPIDR as a GICR_TYPER, and compute 24878c2ecf20Sopenharmony_ci * the resulting affinity. We then use that to see if this match 24888c2ecf20Sopenharmony_ci * our own affinity. 24898c2ecf20Sopenharmony_ci */ 24908c2ecf20Sopenharmony_ci svpet = FIELD_GET(GITS_TYPER_SVPET, its->typer); 24918c2ecf20Sopenharmony_ci val = FIELD_PREP(GICR_TYPER_COMMON_LPI_AFF, svpet); 24928c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_TYPER_AFFINITY, its->mpidr); 24938c2ecf20Sopenharmony_ci return compute_common_aff(val); 24948c2ecf20Sopenharmony_ci} 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_cistatic struct its_node *find_sibling_its(struct its_node *cur_its) 24978c2ecf20Sopenharmony_ci{ 24988c2ecf20Sopenharmony_ci struct its_node *its; 24998c2ecf20Sopenharmony_ci u32 aff; 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci if (!FIELD_GET(GITS_TYPER_SVPET, cur_its->typer)) 25028c2ecf20Sopenharmony_ci return NULL; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci aff = compute_its_aff(cur_its); 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 25078c2ecf20Sopenharmony_ci u64 baser; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci if (!is_v4_1(its) || its == cur_its) 25108c2ecf20Sopenharmony_ci continue; 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci if (!FIELD_GET(GITS_TYPER_SVPET, its->typer)) 25138c2ecf20Sopenharmony_ci continue; 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci if (aff != compute_its_aff(its)) 25168c2ecf20Sopenharmony_ci continue; 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_ci /* GICv4.1 guarantees that the vPE table is GITS_BASER2 */ 25198c2ecf20Sopenharmony_ci baser = its->tables[2].val; 25208c2ecf20Sopenharmony_ci if (!(baser & GITS_BASER_VALID)) 25218c2ecf20Sopenharmony_ci continue; 25228c2ecf20Sopenharmony_ci 25238c2ecf20Sopenharmony_ci return its; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci return NULL; 25278c2ecf20Sopenharmony_ci} 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_cistatic void its_free_tables(struct its_node *its) 25308c2ecf20Sopenharmony_ci{ 25318c2ecf20Sopenharmony_ci int i; 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci for (i = 0; i < GITS_BASER_NR_REGS; i++) { 25348c2ecf20Sopenharmony_ci if (its->tables[i].base) { 25358c2ecf20Sopenharmony_ci free_pages((unsigned long)its->tables[i].base, 25368c2ecf20Sopenharmony_ci its->tables[i].order); 25378c2ecf20Sopenharmony_ci its->tables[i].base = NULL; 25388c2ecf20Sopenharmony_ci } 25398c2ecf20Sopenharmony_ci } 25408c2ecf20Sopenharmony_ci} 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_cistatic int its_probe_baser_psz(struct its_node *its, struct its_baser *baser) 25438c2ecf20Sopenharmony_ci{ 25448c2ecf20Sopenharmony_ci u64 psz = SZ_64K; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci while (psz) { 25478c2ecf20Sopenharmony_ci u64 val, gpsz; 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci val = its_read_baser(its, baser); 25508c2ecf20Sopenharmony_ci val &= ~GITS_BASER_PAGE_SIZE_MASK; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci switch (psz) { 25538c2ecf20Sopenharmony_ci case SZ_64K: 25548c2ecf20Sopenharmony_ci gpsz = GITS_BASER_PAGE_SIZE_64K; 25558c2ecf20Sopenharmony_ci break; 25568c2ecf20Sopenharmony_ci case SZ_16K: 25578c2ecf20Sopenharmony_ci gpsz = GITS_BASER_PAGE_SIZE_16K; 25588c2ecf20Sopenharmony_ci break; 25598c2ecf20Sopenharmony_ci case SZ_4K: 25608c2ecf20Sopenharmony_ci default: 25618c2ecf20Sopenharmony_ci gpsz = GITS_BASER_PAGE_SIZE_4K; 25628c2ecf20Sopenharmony_ci break; 25638c2ecf20Sopenharmony_ci } 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci gpsz >>= GITS_BASER_PAGE_SIZE_SHIFT; 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci val |= FIELD_PREP(GITS_BASER_PAGE_SIZE_MASK, gpsz); 25688c2ecf20Sopenharmony_ci its_write_baser(its, baser, val); 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci if (FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser->val) == gpsz) 25718c2ecf20Sopenharmony_ci break; 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci switch (psz) { 25748c2ecf20Sopenharmony_ci case SZ_64K: 25758c2ecf20Sopenharmony_ci psz = SZ_16K; 25768c2ecf20Sopenharmony_ci break; 25778c2ecf20Sopenharmony_ci case SZ_16K: 25788c2ecf20Sopenharmony_ci psz = SZ_4K; 25798c2ecf20Sopenharmony_ci break; 25808c2ecf20Sopenharmony_ci case SZ_4K: 25818c2ecf20Sopenharmony_ci default: 25828c2ecf20Sopenharmony_ci return -1; 25838c2ecf20Sopenharmony_ci } 25848c2ecf20Sopenharmony_ci } 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci baser->psz = psz; 25878c2ecf20Sopenharmony_ci return 0; 25888c2ecf20Sopenharmony_ci} 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_cistatic int its_alloc_tables(struct its_node *its) 25918c2ecf20Sopenharmony_ci{ 25928c2ecf20Sopenharmony_ci u64 shr = GITS_BASER_InnerShareable; 25938c2ecf20Sopenharmony_ci u64 cache = GITS_BASER_RaWaWb; 25948c2ecf20Sopenharmony_ci int err, i; 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) 25978c2ecf20Sopenharmony_ci /* erratum 24313: ignore memory access type */ 25988c2ecf20Sopenharmony_ci cache = GITS_BASER_nCnB; 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci for (i = 0; i < GITS_BASER_NR_REGS; i++) { 26018c2ecf20Sopenharmony_ci struct its_baser *baser = its->tables + i; 26028c2ecf20Sopenharmony_ci u64 val = its_read_baser(its, baser); 26038c2ecf20Sopenharmony_ci u64 type = GITS_BASER_TYPE(val); 26048c2ecf20Sopenharmony_ci bool indirect = false; 26058c2ecf20Sopenharmony_ci u32 order; 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci if (type == GITS_BASER_TYPE_NONE) 26088c2ecf20Sopenharmony_ci continue; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci if (its_probe_baser_psz(its, baser)) { 26118c2ecf20Sopenharmony_ci its_free_tables(its); 26128c2ecf20Sopenharmony_ci return -ENXIO; 26138c2ecf20Sopenharmony_ci } 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci order = get_order(baser->psz); 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci switch (type) { 26188c2ecf20Sopenharmony_ci case GITS_BASER_TYPE_DEVICE: 26198c2ecf20Sopenharmony_ci indirect = its_parse_indirect_baser(its, baser, &order, 26208c2ecf20Sopenharmony_ci device_ids(its)); 26218c2ecf20Sopenharmony_ci break; 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci case GITS_BASER_TYPE_VCPU: 26248c2ecf20Sopenharmony_ci if (is_v4_1(its)) { 26258c2ecf20Sopenharmony_ci struct its_node *sibling; 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci WARN_ON(i != 2); 26288c2ecf20Sopenharmony_ci if ((sibling = find_sibling_its(its))) { 26298c2ecf20Sopenharmony_ci *baser = sibling->tables[2]; 26308c2ecf20Sopenharmony_ci its_write_baser(its, baser, baser->val); 26318c2ecf20Sopenharmony_ci continue; 26328c2ecf20Sopenharmony_ci } 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci indirect = its_parse_indirect_baser(its, baser, &order, 26368c2ecf20Sopenharmony_ci ITS_MAX_VPEID_BITS); 26378c2ecf20Sopenharmony_ci break; 26388c2ecf20Sopenharmony_ci } 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci err = its_setup_baser(its, baser, cache, shr, order, indirect); 26418c2ecf20Sopenharmony_ci if (err < 0) { 26428c2ecf20Sopenharmony_ci its_free_tables(its); 26438c2ecf20Sopenharmony_ci return err; 26448c2ecf20Sopenharmony_ci } 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci /* Update settings which will be used for next BASERn */ 26478c2ecf20Sopenharmony_ci cache = baser->val & GITS_BASER_CACHEABILITY_MASK; 26488c2ecf20Sopenharmony_ci shr = baser->val & GITS_BASER_SHAREABILITY_MASK; 26498c2ecf20Sopenharmony_ci } 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci return 0; 26528c2ecf20Sopenharmony_ci} 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_cistatic u64 inherit_vpe_l1_table_from_its(void) 26558c2ecf20Sopenharmony_ci{ 26568c2ecf20Sopenharmony_ci struct its_node *its; 26578c2ecf20Sopenharmony_ci u64 val; 26588c2ecf20Sopenharmony_ci u32 aff; 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci val = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER); 26618c2ecf20Sopenharmony_ci aff = compute_common_aff(val); 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 26648c2ecf20Sopenharmony_ci u64 baser, addr; 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci if (!is_v4_1(its)) 26678c2ecf20Sopenharmony_ci continue; 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_ci if (!FIELD_GET(GITS_TYPER_SVPET, its->typer)) 26708c2ecf20Sopenharmony_ci continue; 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci if (aff != compute_its_aff(its)) 26738c2ecf20Sopenharmony_ci continue; 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci /* GICv4.1 guarantees that the vPE table is GITS_BASER2 */ 26768c2ecf20Sopenharmony_ci baser = its->tables[2].val; 26778c2ecf20Sopenharmony_ci if (!(baser & GITS_BASER_VALID)) 26788c2ecf20Sopenharmony_ci continue; 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci /* We have a winner! */ 26818c2ecf20Sopenharmony_ci gic_data_rdist()->vpe_l1_base = its->tables[2].base; 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci val = GICR_VPROPBASER_4_1_VALID; 26848c2ecf20Sopenharmony_ci if (baser & GITS_BASER_INDIRECT) 26858c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_4_1_INDIRECT; 26868c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE, 26878c2ecf20Sopenharmony_ci FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser)); 26888c2ecf20Sopenharmony_ci switch (FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, baser)) { 26898c2ecf20Sopenharmony_ci case GIC_PAGE_SIZE_64K: 26908c2ecf20Sopenharmony_ci addr = GITS_BASER_ADDR_48_to_52(baser); 26918c2ecf20Sopenharmony_ci break; 26928c2ecf20Sopenharmony_ci default: 26938c2ecf20Sopenharmony_ci addr = baser & GENMASK_ULL(47, 12); 26948c2ecf20Sopenharmony_ci break; 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR, addr >> 12); 26978c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_SHAREABILITY_MASK, 26988c2ecf20Sopenharmony_ci FIELD_GET(GITS_BASER_SHAREABILITY_MASK, baser)); 26998c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_INNER_CACHEABILITY_MASK, 27008c2ecf20Sopenharmony_ci FIELD_GET(GITS_BASER_INNER_CACHEABILITY_MASK, baser)); 27018c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, GITS_BASER_NR_PAGES(baser) - 1); 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci return val; 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci return 0; 27078c2ecf20Sopenharmony_ci} 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_cistatic u64 inherit_vpe_l1_table_from_rd(cpumask_t **mask) 27108c2ecf20Sopenharmony_ci{ 27118c2ecf20Sopenharmony_ci u32 aff; 27128c2ecf20Sopenharmony_ci u64 val; 27138c2ecf20Sopenharmony_ci int cpu; 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci val = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER); 27168c2ecf20Sopenharmony_ci aff = compute_common_aff(val); 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 27198c2ecf20Sopenharmony_ci void __iomem *base = gic_data_rdist_cpu(cpu)->rd_base; 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci if (!base || cpu == smp_processor_id()) 27228c2ecf20Sopenharmony_ci continue; 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci val = gic_read_typer(base + GICR_TYPER); 27258c2ecf20Sopenharmony_ci if (aff != compute_common_aff(val)) 27268c2ecf20Sopenharmony_ci continue; 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci /* 27298c2ecf20Sopenharmony_ci * At this point, we have a victim. This particular CPU 27308c2ecf20Sopenharmony_ci * has already booted, and has an affinity that matches 27318c2ecf20Sopenharmony_ci * ours wrt CommonLPIAff. Let's use its own VPROPBASER. 27328c2ecf20Sopenharmony_ci * Make sure we don't write the Z bit in that case. 27338c2ecf20Sopenharmony_ci */ 27348c2ecf20Sopenharmony_ci val = gicr_read_vpropbaser(base + SZ_128K + GICR_VPROPBASER); 27358c2ecf20Sopenharmony_ci val &= ~GICR_VPROPBASER_4_1_Z; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci gic_data_rdist()->vpe_l1_base = gic_data_rdist_cpu(cpu)->vpe_l1_base; 27388c2ecf20Sopenharmony_ci *mask = gic_data_rdist_cpu(cpu)->vpe_table_mask; 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci return val; 27418c2ecf20Sopenharmony_ci } 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci return 0; 27448c2ecf20Sopenharmony_ci} 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_cistatic bool allocate_vpe_l2_table(int cpu, u32 id) 27478c2ecf20Sopenharmony_ci{ 27488c2ecf20Sopenharmony_ci void __iomem *base = gic_data_rdist_cpu(cpu)->rd_base; 27498c2ecf20Sopenharmony_ci unsigned int psz, esz, idx, npg, gpsz; 27508c2ecf20Sopenharmony_ci u64 val; 27518c2ecf20Sopenharmony_ci struct page *page; 27528c2ecf20Sopenharmony_ci __le64 *table; 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci if (!gic_rdists->has_rvpeid) 27558c2ecf20Sopenharmony_ci return true; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci /* Skip non-present CPUs */ 27588c2ecf20Sopenharmony_ci if (!base) 27598c2ecf20Sopenharmony_ci return true; 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci val = gicr_read_vpropbaser(base + SZ_128K + GICR_VPROPBASER); 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci esz = FIELD_GET(GICR_VPROPBASER_4_1_ENTRY_SIZE, val) + 1; 27648c2ecf20Sopenharmony_ci gpsz = FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val); 27658c2ecf20Sopenharmony_ci npg = FIELD_GET(GICR_VPROPBASER_4_1_SIZE, val) + 1; 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci switch (gpsz) { 27688c2ecf20Sopenharmony_ci default: 27698c2ecf20Sopenharmony_ci WARN_ON(1); 27708c2ecf20Sopenharmony_ci fallthrough; 27718c2ecf20Sopenharmony_ci case GIC_PAGE_SIZE_4K: 27728c2ecf20Sopenharmony_ci psz = SZ_4K; 27738c2ecf20Sopenharmony_ci break; 27748c2ecf20Sopenharmony_ci case GIC_PAGE_SIZE_16K: 27758c2ecf20Sopenharmony_ci psz = SZ_16K; 27768c2ecf20Sopenharmony_ci break; 27778c2ecf20Sopenharmony_ci case GIC_PAGE_SIZE_64K: 27788c2ecf20Sopenharmony_ci psz = SZ_64K; 27798c2ecf20Sopenharmony_ci break; 27808c2ecf20Sopenharmony_ci } 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci /* Don't allow vpe_id that exceeds single, flat table limit */ 27838c2ecf20Sopenharmony_ci if (!(val & GICR_VPROPBASER_4_1_INDIRECT)) 27848c2ecf20Sopenharmony_ci return (id < (npg * psz / (esz * SZ_8))); 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci /* Compute 1st level table index & check if that exceeds table limit */ 27878c2ecf20Sopenharmony_ci idx = id >> ilog2(psz / (esz * SZ_8)); 27888c2ecf20Sopenharmony_ci if (idx >= (npg * psz / GITS_LVL1_ENTRY_SIZE)) 27898c2ecf20Sopenharmony_ci return false; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci table = gic_data_rdist_cpu(cpu)->vpe_l1_base; 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci /* Allocate memory for 2nd level table */ 27948c2ecf20Sopenharmony_ci if (!table[idx]) { 27958c2ecf20Sopenharmony_ci page = alloc_pages(GFP_KERNEL | __GFP_ZERO, get_order(psz)); 27968c2ecf20Sopenharmony_ci if (!page) 27978c2ecf20Sopenharmony_ci return false; 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci /* Flush Lvl2 table to PoC if hw doesn't support coherency */ 28008c2ecf20Sopenharmony_ci if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK)) 28018c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(page_address(page), psz); 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID); 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci /* Flush Lvl1 entry to PoC if hw doesn't support coherency */ 28068c2ecf20Sopenharmony_ci if (!(val & GICR_VPROPBASER_SHAREABILITY_MASK)) 28078c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(table + idx, GITS_LVL1_ENTRY_SIZE); 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci /* Ensure updated table contents are visible to RD hardware */ 28108c2ecf20Sopenharmony_ci dsb(sy); 28118c2ecf20Sopenharmony_ci } 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci return true; 28148c2ecf20Sopenharmony_ci} 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_cistatic int allocate_vpe_l1_table(void) 28178c2ecf20Sopenharmony_ci{ 28188c2ecf20Sopenharmony_ci void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 28198c2ecf20Sopenharmony_ci u64 val, gpsz, npg, pa; 28208c2ecf20Sopenharmony_ci unsigned int psz = SZ_64K; 28218c2ecf20Sopenharmony_ci unsigned int np, epp, esz; 28228c2ecf20Sopenharmony_ci struct page *page; 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci if (!gic_rdists->has_rvpeid) 28258c2ecf20Sopenharmony_ci return 0; 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_ci /* 28288c2ecf20Sopenharmony_ci * if VPENDBASER.Valid is set, disable any previously programmed 28298c2ecf20Sopenharmony_ci * VPE by setting PendingLast while clearing Valid. This has the 28308c2ecf20Sopenharmony_ci * effect of making sure no doorbell will be generated and we can 28318c2ecf20Sopenharmony_ci * then safely clear VPROPBASER.Valid. 28328c2ecf20Sopenharmony_ci */ 28338c2ecf20Sopenharmony_ci if (gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER) & GICR_VPENDBASER_Valid) 28348c2ecf20Sopenharmony_ci gicr_write_vpendbaser(GICR_VPENDBASER_PendingLast, 28358c2ecf20Sopenharmony_ci vlpi_base + GICR_VPENDBASER); 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci /* 28388c2ecf20Sopenharmony_ci * If we can inherit the configuration from another RD, let's do 28398c2ecf20Sopenharmony_ci * so. Otherwise, we have to go through the allocation process. We 28408c2ecf20Sopenharmony_ci * assume that all RDs have the exact same requirements, as 28418c2ecf20Sopenharmony_ci * nothing will work otherwise. 28428c2ecf20Sopenharmony_ci */ 28438c2ecf20Sopenharmony_ci val = inherit_vpe_l1_table_from_rd(&gic_data_rdist()->vpe_table_mask); 28448c2ecf20Sopenharmony_ci if (val & GICR_VPROPBASER_4_1_VALID) 28458c2ecf20Sopenharmony_ci goto out; 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci gic_data_rdist()->vpe_table_mask = kzalloc(sizeof(cpumask_t), GFP_ATOMIC); 28488c2ecf20Sopenharmony_ci if (!gic_data_rdist()->vpe_table_mask) 28498c2ecf20Sopenharmony_ci return -ENOMEM; 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci val = inherit_vpe_l1_table_from_its(); 28528c2ecf20Sopenharmony_ci if (val & GICR_VPROPBASER_4_1_VALID) 28538c2ecf20Sopenharmony_ci goto out; 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci /* First probe the page size */ 28568c2ecf20Sopenharmony_ci val = FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE, GIC_PAGE_SIZE_64K); 28578c2ecf20Sopenharmony_ci gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER); 28588c2ecf20Sopenharmony_ci val = gicr_read_vpropbaser(vlpi_base + GICR_VPROPBASER); 28598c2ecf20Sopenharmony_ci gpsz = FIELD_GET(GICR_VPROPBASER_4_1_PAGE_SIZE, val); 28608c2ecf20Sopenharmony_ci esz = FIELD_GET(GICR_VPROPBASER_4_1_ENTRY_SIZE, val); 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci switch (gpsz) { 28638c2ecf20Sopenharmony_ci default: 28648c2ecf20Sopenharmony_ci gpsz = GIC_PAGE_SIZE_4K; 28658c2ecf20Sopenharmony_ci fallthrough; 28668c2ecf20Sopenharmony_ci case GIC_PAGE_SIZE_4K: 28678c2ecf20Sopenharmony_ci psz = SZ_4K; 28688c2ecf20Sopenharmony_ci break; 28698c2ecf20Sopenharmony_ci case GIC_PAGE_SIZE_16K: 28708c2ecf20Sopenharmony_ci psz = SZ_16K; 28718c2ecf20Sopenharmony_ci break; 28728c2ecf20Sopenharmony_ci case GIC_PAGE_SIZE_64K: 28738c2ecf20Sopenharmony_ci psz = SZ_64K; 28748c2ecf20Sopenharmony_ci break; 28758c2ecf20Sopenharmony_ci } 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci /* 28788c2ecf20Sopenharmony_ci * Start populating the register from scratch, including RO fields 28798c2ecf20Sopenharmony_ci * (which we want to print in debug cases...) 28808c2ecf20Sopenharmony_ci */ 28818c2ecf20Sopenharmony_ci val = 0; 28828c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_4_1_PAGE_SIZE, gpsz); 28838c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_4_1_ENTRY_SIZE, esz); 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci /* How many entries per GIC page? */ 28868c2ecf20Sopenharmony_ci esz++; 28878c2ecf20Sopenharmony_ci epp = psz / (esz * SZ_8); 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci /* 28908c2ecf20Sopenharmony_ci * If we need more than just a single L1 page, flag the table 28918c2ecf20Sopenharmony_ci * as indirect and compute the number of required L1 pages. 28928c2ecf20Sopenharmony_ci */ 28938c2ecf20Sopenharmony_ci if (epp < ITS_MAX_VPEID) { 28948c2ecf20Sopenharmony_ci int nl2; 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_4_1_INDIRECT; 28978c2ecf20Sopenharmony_ci 28988c2ecf20Sopenharmony_ci /* Number of L2 pages required to cover the VPEID space */ 28998c2ecf20Sopenharmony_ci nl2 = DIV_ROUND_UP(ITS_MAX_VPEID, epp); 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci /* Number of L1 pages to point to the L2 pages */ 29028c2ecf20Sopenharmony_ci npg = DIV_ROUND_UP(nl2 * SZ_8, psz); 29038c2ecf20Sopenharmony_ci } else { 29048c2ecf20Sopenharmony_ci npg = 1; 29058c2ecf20Sopenharmony_ci } 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_4_1_SIZE, npg - 1); 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci /* Right, that's the number of CPU pages we need for L1 */ 29108c2ecf20Sopenharmony_ci np = DIV_ROUND_UP(npg * psz, PAGE_SIZE); 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci pr_debug("np = %d, npg = %lld, psz = %d, epp = %d, esz = %d\n", 29138c2ecf20Sopenharmony_ci np, npg, psz, epp, esz); 29148c2ecf20Sopenharmony_ci page = alloc_pages(GFP_ATOMIC | __GFP_ZERO, get_order(np * PAGE_SIZE)); 29158c2ecf20Sopenharmony_ci if (!page) 29168c2ecf20Sopenharmony_ci return -ENOMEM; 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci gic_data_rdist()->vpe_l1_base = page_address(page); 29198c2ecf20Sopenharmony_ci pa = virt_to_phys(page_address(page)); 29208c2ecf20Sopenharmony_ci WARN_ON(!IS_ALIGNED(pa, psz)); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPROPBASER_4_1_ADDR, pa >> 12); 29238c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_RaWb; 29248c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_InnerShareable; 29258c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_4_1_Z; 29268c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_4_1_VALID; 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ciout: 29298c2ecf20Sopenharmony_ci gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER); 29308c2ecf20Sopenharmony_ci cpumask_set_cpu(smp_processor_id(), gic_data_rdist()->vpe_table_mask); 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci pr_debug("CPU%d: VPROPBASER = %llx %*pbl\n", 29338c2ecf20Sopenharmony_ci smp_processor_id(), val, 29348c2ecf20Sopenharmony_ci cpumask_pr_args(gic_data_rdist()->vpe_table_mask)); 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci return 0; 29378c2ecf20Sopenharmony_ci} 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_cistatic int its_alloc_collections(struct its_node *its) 29408c2ecf20Sopenharmony_ci{ 29418c2ecf20Sopenharmony_ci int i; 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_ci its->collections = kcalloc(nr_cpu_ids, sizeof(*its->collections), 29448c2ecf20Sopenharmony_ci GFP_KERNEL); 29458c2ecf20Sopenharmony_ci if (!its->collections) 29468c2ecf20Sopenharmony_ci return -ENOMEM; 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpu_ids; i++) 29498c2ecf20Sopenharmony_ci its->collections[i].target_address = ~0ULL; 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci return 0; 29528c2ecf20Sopenharmony_ci} 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_cistatic struct page *its_allocate_pending_table(gfp_t gfp_flags) 29558c2ecf20Sopenharmony_ci{ 29568c2ecf20Sopenharmony_ci struct page *pend_page; 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci pend_page = alloc_pages(gfp_flags | __GFP_ZERO, 29598c2ecf20Sopenharmony_ci get_order(LPI_PENDBASE_SZ)); 29608c2ecf20Sopenharmony_ci if (!pend_page) 29618c2ecf20Sopenharmony_ci return NULL; 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_ci /* Make sure the GIC will observe the zero-ed page */ 29648c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(page_address(pend_page), LPI_PENDBASE_SZ); 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci return pend_page; 29678c2ecf20Sopenharmony_ci} 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_cistatic void its_free_pending_table(struct page *pt) 29708c2ecf20Sopenharmony_ci{ 29718c2ecf20Sopenharmony_ci free_pages((unsigned long)page_address(pt), get_order(LPI_PENDBASE_SZ)); 29728c2ecf20Sopenharmony_ci} 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci/* 29758c2ecf20Sopenharmony_ci * Booting with kdump and LPIs enabled is generally fine. Any other 29768c2ecf20Sopenharmony_ci * case is wrong in the absence of firmware/EFI support. 29778c2ecf20Sopenharmony_ci */ 29788c2ecf20Sopenharmony_cistatic bool enabled_lpis_allowed(void) 29798c2ecf20Sopenharmony_ci{ 29808c2ecf20Sopenharmony_ci phys_addr_t addr; 29818c2ecf20Sopenharmony_ci u64 val; 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci /* Check whether the property table is in a reserved region */ 29848c2ecf20Sopenharmony_ci val = gicr_read_propbaser(gic_data_rdist_rd_base() + GICR_PROPBASER); 29858c2ecf20Sopenharmony_ci addr = val & GENMASK_ULL(51, 12); 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci return gic_check_reserved_range(addr, LPI_PROPBASE_SZ); 29888c2ecf20Sopenharmony_ci} 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_cistatic int __init allocate_lpi_tables(void) 29918c2ecf20Sopenharmony_ci{ 29928c2ecf20Sopenharmony_ci u64 val; 29938c2ecf20Sopenharmony_ci int err, cpu; 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci /* 29968c2ecf20Sopenharmony_ci * If LPIs are enabled while we run this from the boot CPU, 29978c2ecf20Sopenharmony_ci * flag the RD tables as pre-allocated if the stars do align. 29988c2ecf20Sopenharmony_ci */ 29998c2ecf20Sopenharmony_ci val = readl_relaxed(gic_data_rdist_rd_base() + GICR_CTLR); 30008c2ecf20Sopenharmony_ci if ((val & GICR_CTLR_ENABLE_LPIS) && enabled_lpis_allowed()) { 30018c2ecf20Sopenharmony_ci gic_rdists->flags |= (RDIST_FLAGS_RD_TABLES_PREALLOCATED | 30028c2ecf20Sopenharmony_ci RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING); 30038c2ecf20Sopenharmony_ci pr_info("GICv3: Using preallocated redistributor tables\n"); 30048c2ecf20Sopenharmony_ci } 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci err = its_setup_lpi_prop_table(); 30078c2ecf20Sopenharmony_ci if (err) 30088c2ecf20Sopenharmony_ci return err; 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci /* 30118c2ecf20Sopenharmony_ci * We allocate all the pending tables anyway, as we may have a 30128c2ecf20Sopenharmony_ci * mix of RDs that have had LPIs enabled, and some that 30138c2ecf20Sopenharmony_ci * don't. We'll free the unused ones as each CPU comes online. 30148c2ecf20Sopenharmony_ci */ 30158c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 30168c2ecf20Sopenharmony_ci struct page *pend_page; 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci pend_page = its_allocate_pending_table(GFP_NOWAIT); 30198c2ecf20Sopenharmony_ci if (!pend_page) { 30208c2ecf20Sopenharmony_ci pr_err("Failed to allocate PENDBASE for CPU%d\n", cpu); 30218c2ecf20Sopenharmony_ci return -ENOMEM; 30228c2ecf20Sopenharmony_ci } 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci gic_data_rdist_cpu(cpu)->pend_page = pend_page; 30258c2ecf20Sopenharmony_ci } 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci return 0; 30288c2ecf20Sopenharmony_ci} 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_cistatic u64 its_clear_vpend_valid(void __iomem *vlpi_base, u64 clr, u64 set) 30318c2ecf20Sopenharmony_ci{ 30328c2ecf20Sopenharmony_ci u32 count = 1000000; /* 1s! */ 30338c2ecf20Sopenharmony_ci bool clean; 30348c2ecf20Sopenharmony_ci u64 val; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci val = gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER); 30378c2ecf20Sopenharmony_ci val &= ~GICR_VPENDBASER_Valid; 30388c2ecf20Sopenharmony_ci val &= ~clr; 30398c2ecf20Sopenharmony_ci val |= set; 30408c2ecf20Sopenharmony_ci gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci do { 30438c2ecf20Sopenharmony_ci val = gicr_read_vpendbaser(vlpi_base + GICR_VPENDBASER); 30448c2ecf20Sopenharmony_ci clean = !(val & GICR_VPENDBASER_Dirty); 30458c2ecf20Sopenharmony_ci if (!clean) { 30468c2ecf20Sopenharmony_ci count--; 30478c2ecf20Sopenharmony_ci cpu_relax(); 30488c2ecf20Sopenharmony_ci udelay(1); 30498c2ecf20Sopenharmony_ci } 30508c2ecf20Sopenharmony_ci } while (!clean && count); 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci if (unlikely(val & GICR_VPENDBASER_Dirty)) { 30538c2ecf20Sopenharmony_ci pr_err_ratelimited("ITS virtual pending table not cleaning\n"); 30548c2ecf20Sopenharmony_ci val |= GICR_VPENDBASER_PendingLast; 30558c2ecf20Sopenharmony_ci } 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci return val; 30588c2ecf20Sopenharmony_ci} 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_cistatic void its_cpu_init_lpis(void) 30618c2ecf20Sopenharmony_ci{ 30628c2ecf20Sopenharmony_ci void __iomem *rbase = gic_data_rdist_rd_base(); 30638c2ecf20Sopenharmony_ci struct page *pend_page; 30648c2ecf20Sopenharmony_ci phys_addr_t paddr; 30658c2ecf20Sopenharmony_ci u64 val, tmp; 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci if (gic_data_rdist()->lpi_enabled) 30688c2ecf20Sopenharmony_ci return; 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci val = readl_relaxed(rbase + GICR_CTLR); 30718c2ecf20Sopenharmony_ci if ((gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) && 30728c2ecf20Sopenharmony_ci (val & GICR_CTLR_ENABLE_LPIS)) { 30738c2ecf20Sopenharmony_ci /* 30748c2ecf20Sopenharmony_ci * Check that we get the same property table on all 30758c2ecf20Sopenharmony_ci * RDs. If we don't, this is hopeless. 30768c2ecf20Sopenharmony_ci */ 30778c2ecf20Sopenharmony_ci paddr = gicr_read_propbaser(rbase + GICR_PROPBASER); 30788c2ecf20Sopenharmony_ci paddr &= GENMASK_ULL(51, 12); 30798c2ecf20Sopenharmony_ci if (WARN_ON(gic_rdists->prop_table_pa != paddr)) 30808c2ecf20Sopenharmony_ci add_taint(TAINT_CRAP, LOCKDEP_STILL_OK); 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci paddr = gicr_read_pendbaser(rbase + GICR_PENDBASER); 30838c2ecf20Sopenharmony_ci paddr &= GENMASK_ULL(51, 16); 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci WARN_ON(!gic_check_reserved_range(paddr, LPI_PENDBASE_SZ)); 30868c2ecf20Sopenharmony_ci its_free_pending_table(gic_data_rdist()->pend_page); 30878c2ecf20Sopenharmony_ci gic_data_rdist()->pend_page = NULL; 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci goto out; 30908c2ecf20Sopenharmony_ci } 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_ci pend_page = gic_data_rdist()->pend_page; 30938c2ecf20Sopenharmony_ci paddr = page_to_phys(pend_page); 30948c2ecf20Sopenharmony_ci WARN_ON(gic_reserve_range(paddr, LPI_PENDBASE_SZ)); 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci /* set PROPBASE */ 30978c2ecf20Sopenharmony_ci val = (gic_rdists->prop_table_pa | 30988c2ecf20Sopenharmony_ci GICR_PROPBASER_InnerShareable | 30998c2ecf20Sopenharmony_ci GICR_PROPBASER_RaWaWb | 31008c2ecf20Sopenharmony_ci ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci gicr_write_propbaser(val, rbase + GICR_PROPBASER); 31038c2ecf20Sopenharmony_ci tmp = gicr_read_propbaser(rbase + GICR_PROPBASER); 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) { 31068c2ecf20Sopenharmony_ci if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) { 31078c2ecf20Sopenharmony_ci /* 31088c2ecf20Sopenharmony_ci * The HW reports non-shareable, we must 31098c2ecf20Sopenharmony_ci * remove the cacheability attributes as 31108c2ecf20Sopenharmony_ci * well. 31118c2ecf20Sopenharmony_ci */ 31128c2ecf20Sopenharmony_ci val &= ~(GICR_PROPBASER_SHAREABILITY_MASK | 31138c2ecf20Sopenharmony_ci GICR_PROPBASER_CACHEABILITY_MASK); 31148c2ecf20Sopenharmony_ci val |= GICR_PROPBASER_nC; 31158c2ecf20Sopenharmony_ci gicr_write_propbaser(val, rbase + GICR_PROPBASER); 31168c2ecf20Sopenharmony_ci } 31178c2ecf20Sopenharmony_ci pr_info_once("GIC: using cache flushing for LPI property table\n"); 31188c2ecf20Sopenharmony_ci gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; 31198c2ecf20Sopenharmony_ci } 31208c2ecf20Sopenharmony_ci 31218c2ecf20Sopenharmony_ci /* set PENDBASE */ 31228c2ecf20Sopenharmony_ci val = (page_to_phys(pend_page) | 31238c2ecf20Sopenharmony_ci GICR_PENDBASER_InnerShareable | 31248c2ecf20Sopenharmony_ci GICR_PENDBASER_RaWaWb); 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci gicr_write_pendbaser(val, rbase + GICR_PENDBASER); 31278c2ecf20Sopenharmony_ci tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER); 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) { 31308c2ecf20Sopenharmony_ci /* 31318c2ecf20Sopenharmony_ci * The HW reports non-shareable, we must remove the 31328c2ecf20Sopenharmony_ci * cacheability attributes as well. 31338c2ecf20Sopenharmony_ci */ 31348c2ecf20Sopenharmony_ci val &= ~(GICR_PENDBASER_SHAREABILITY_MASK | 31358c2ecf20Sopenharmony_ci GICR_PENDBASER_CACHEABILITY_MASK); 31368c2ecf20Sopenharmony_ci val |= GICR_PENDBASER_nC; 31378c2ecf20Sopenharmony_ci gicr_write_pendbaser(val, rbase + GICR_PENDBASER); 31388c2ecf20Sopenharmony_ci } 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_ci /* Enable LPIs */ 31418c2ecf20Sopenharmony_ci val = readl_relaxed(rbase + GICR_CTLR); 31428c2ecf20Sopenharmony_ci val |= GICR_CTLR_ENABLE_LPIS; 31438c2ecf20Sopenharmony_ci writel_relaxed(val, rbase + GICR_CTLR); 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci if (gic_rdists->has_vlpis && !gic_rdists->has_rvpeid) { 31468c2ecf20Sopenharmony_ci void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_ci /* 31498c2ecf20Sopenharmony_ci * It's possible for CPU to receive VLPIs before it is 31508c2ecf20Sopenharmony_ci * scheduled as a vPE, especially for the first CPU, and the 31518c2ecf20Sopenharmony_ci * VLPI with INTID larger than 2^(IDbits+1) will be considered 31528c2ecf20Sopenharmony_ci * as out of range and dropped by GIC. 31538c2ecf20Sopenharmony_ci * So we initialize IDbits to known value to avoid VLPI drop. 31548c2ecf20Sopenharmony_ci */ 31558c2ecf20Sopenharmony_ci val = (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK; 31568c2ecf20Sopenharmony_ci pr_debug("GICv4: CPU%d: Init IDbits to 0x%llx for GICR_VPROPBASER\n", 31578c2ecf20Sopenharmony_ci smp_processor_id(), val); 31588c2ecf20Sopenharmony_ci gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER); 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci /* 31618c2ecf20Sopenharmony_ci * Also clear Valid bit of GICR_VPENDBASER, in case some 31628c2ecf20Sopenharmony_ci * ancient programming gets left in and has possibility of 31638c2ecf20Sopenharmony_ci * corrupting memory. 31648c2ecf20Sopenharmony_ci */ 31658c2ecf20Sopenharmony_ci val = its_clear_vpend_valid(vlpi_base, 0, 0); 31668c2ecf20Sopenharmony_ci } 31678c2ecf20Sopenharmony_ci 31688c2ecf20Sopenharmony_ci if (allocate_vpe_l1_table()) { 31698c2ecf20Sopenharmony_ci /* 31708c2ecf20Sopenharmony_ci * If the allocation has failed, we're in massive trouble. 31718c2ecf20Sopenharmony_ci * Disable direct injection, and pray that no VM was 31728c2ecf20Sopenharmony_ci * already running... 31738c2ecf20Sopenharmony_ci */ 31748c2ecf20Sopenharmony_ci gic_rdists->has_rvpeid = false; 31758c2ecf20Sopenharmony_ci gic_rdists->has_vlpis = false; 31768c2ecf20Sopenharmony_ci } 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci /* Make sure the GIC has seen the above */ 31798c2ecf20Sopenharmony_ci dsb(sy); 31808c2ecf20Sopenharmony_ciout: 31818c2ecf20Sopenharmony_ci gic_data_rdist()->lpi_enabled = true; 31828c2ecf20Sopenharmony_ci pr_info("GICv3: CPU%d: using %s LPI pending table @%pa\n", 31838c2ecf20Sopenharmony_ci smp_processor_id(), 31848c2ecf20Sopenharmony_ci gic_data_rdist()->pend_page ? "allocated" : "reserved", 31858c2ecf20Sopenharmony_ci &paddr); 31868c2ecf20Sopenharmony_ci} 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_cistatic void its_cpu_init_collection(struct its_node *its) 31898c2ecf20Sopenharmony_ci{ 31908c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 31918c2ecf20Sopenharmony_ci u64 target; 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci /* avoid cross node collections and its mapping */ 31948c2ecf20Sopenharmony_ci if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { 31958c2ecf20Sopenharmony_ci struct device_node *cpu_node; 31968c2ecf20Sopenharmony_ci 31978c2ecf20Sopenharmony_ci cpu_node = of_get_cpu_node(cpu, NULL); 31988c2ecf20Sopenharmony_ci if (its->numa_node != NUMA_NO_NODE && 31998c2ecf20Sopenharmony_ci its->numa_node != of_node_to_nid(cpu_node)) 32008c2ecf20Sopenharmony_ci return; 32018c2ecf20Sopenharmony_ci } 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci /* 32048c2ecf20Sopenharmony_ci * We now have to bind each collection to its target 32058c2ecf20Sopenharmony_ci * redistributor. 32068c2ecf20Sopenharmony_ci */ 32078c2ecf20Sopenharmony_ci if (gic_read_typer(its->base + GITS_TYPER) & GITS_TYPER_PTA) { 32088c2ecf20Sopenharmony_ci /* 32098c2ecf20Sopenharmony_ci * This ITS wants the physical address of the 32108c2ecf20Sopenharmony_ci * redistributor. 32118c2ecf20Sopenharmony_ci */ 32128c2ecf20Sopenharmony_ci target = gic_data_rdist()->phys_base; 32138c2ecf20Sopenharmony_ci } else { 32148c2ecf20Sopenharmony_ci /* This ITS wants a linear CPU number. */ 32158c2ecf20Sopenharmony_ci target = gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER); 32168c2ecf20Sopenharmony_ci target = GICR_TYPER_CPU_NUMBER(target) << 16; 32178c2ecf20Sopenharmony_ci } 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_ci /* Perform collection mapping */ 32208c2ecf20Sopenharmony_ci its->collections[cpu].target_address = target; 32218c2ecf20Sopenharmony_ci its->collections[cpu].col_id = cpu; 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci its_send_mapc(its, &its->collections[cpu], 1); 32248c2ecf20Sopenharmony_ci its_send_invall(its, &its->collections[cpu]); 32258c2ecf20Sopenharmony_ci} 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_cistatic void its_cpu_init_collections(void) 32288c2ecf20Sopenharmony_ci{ 32298c2ecf20Sopenharmony_ci struct its_node *its; 32308c2ecf20Sopenharmony_ci 32318c2ecf20Sopenharmony_ci raw_spin_lock(&its_lock); 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) 32348c2ecf20Sopenharmony_ci its_cpu_init_collection(its); 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci raw_spin_unlock(&its_lock); 32378c2ecf20Sopenharmony_ci} 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_cistatic struct its_device *its_find_device(struct its_node *its, u32 dev_id) 32408c2ecf20Sopenharmony_ci{ 32418c2ecf20Sopenharmony_ci struct its_device *its_dev = NULL, *tmp; 32428c2ecf20Sopenharmony_ci unsigned long flags; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&its->lock, flags); 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &its->its_device_list, entry) { 32478c2ecf20Sopenharmony_ci if (tmp->device_id == dev_id) { 32488c2ecf20Sopenharmony_ci its_dev = tmp; 32498c2ecf20Sopenharmony_ci break; 32508c2ecf20Sopenharmony_ci } 32518c2ecf20Sopenharmony_ci } 32528c2ecf20Sopenharmony_ci 32538c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&its->lock, flags); 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_ci return its_dev; 32568c2ecf20Sopenharmony_ci} 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_cistatic struct its_baser *its_get_baser(struct its_node *its, u32 type) 32598c2ecf20Sopenharmony_ci{ 32608c2ecf20Sopenharmony_ci int i; 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci for (i = 0; i < GITS_BASER_NR_REGS; i++) { 32638c2ecf20Sopenharmony_ci if (GITS_BASER_TYPE(its->tables[i].val) == type) 32648c2ecf20Sopenharmony_ci return &its->tables[i]; 32658c2ecf20Sopenharmony_ci } 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci return NULL; 32688c2ecf20Sopenharmony_ci} 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_cistatic bool its_alloc_table_entry(struct its_node *its, 32718c2ecf20Sopenharmony_ci struct its_baser *baser, u32 id) 32728c2ecf20Sopenharmony_ci{ 32738c2ecf20Sopenharmony_ci struct page *page; 32748c2ecf20Sopenharmony_ci u32 esz, idx; 32758c2ecf20Sopenharmony_ci __le64 *table; 32768c2ecf20Sopenharmony_ci 32778c2ecf20Sopenharmony_ci /* Don't allow device id that exceeds single, flat table limit */ 32788c2ecf20Sopenharmony_ci esz = GITS_BASER_ENTRY_SIZE(baser->val); 32798c2ecf20Sopenharmony_ci if (!(baser->val & GITS_BASER_INDIRECT)) 32808c2ecf20Sopenharmony_ci return (id < (PAGE_ORDER_TO_SIZE(baser->order) / esz)); 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci /* Compute 1st level table index & check if that exceeds table limit */ 32838c2ecf20Sopenharmony_ci idx = id >> ilog2(baser->psz / esz); 32848c2ecf20Sopenharmony_ci if (idx >= (PAGE_ORDER_TO_SIZE(baser->order) / GITS_LVL1_ENTRY_SIZE)) 32858c2ecf20Sopenharmony_ci return false; 32868c2ecf20Sopenharmony_ci 32878c2ecf20Sopenharmony_ci table = baser->base; 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci /* Allocate memory for 2nd level table */ 32908c2ecf20Sopenharmony_ci if (!table[idx]) { 32918c2ecf20Sopenharmony_ci page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO, 32928c2ecf20Sopenharmony_ci get_order(baser->psz)); 32938c2ecf20Sopenharmony_ci if (!page) 32948c2ecf20Sopenharmony_ci return false; 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci /* Flush Lvl2 table to PoC if hw doesn't support coherency */ 32978c2ecf20Sopenharmony_ci if (!(baser->val & GITS_BASER_SHAREABILITY_MASK)) 32988c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(page_address(page), baser->psz); 32998c2ecf20Sopenharmony_ci 33008c2ecf20Sopenharmony_ci table[idx] = cpu_to_le64(page_to_phys(page) | GITS_BASER_VALID); 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci /* Flush Lvl1 entry to PoC if hw doesn't support coherency */ 33038c2ecf20Sopenharmony_ci if (!(baser->val & GITS_BASER_SHAREABILITY_MASK)) 33048c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(table + idx, GITS_LVL1_ENTRY_SIZE); 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci /* Ensure updated table contents are visible to ITS hardware */ 33078c2ecf20Sopenharmony_ci dsb(sy); 33088c2ecf20Sopenharmony_ci } 33098c2ecf20Sopenharmony_ci 33108c2ecf20Sopenharmony_ci return true; 33118c2ecf20Sopenharmony_ci} 33128c2ecf20Sopenharmony_ci 33138c2ecf20Sopenharmony_cistatic bool its_alloc_device_table(struct its_node *its, u32 dev_id) 33148c2ecf20Sopenharmony_ci{ 33158c2ecf20Sopenharmony_ci struct its_baser *baser; 33168c2ecf20Sopenharmony_ci 33178c2ecf20Sopenharmony_ci baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE); 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci /* Don't allow device id that exceeds ITS hardware limit */ 33208c2ecf20Sopenharmony_ci if (!baser) 33218c2ecf20Sopenharmony_ci return (ilog2(dev_id) < device_ids(its)); 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci return its_alloc_table_entry(its, baser, dev_id); 33248c2ecf20Sopenharmony_ci} 33258c2ecf20Sopenharmony_ci 33268c2ecf20Sopenharmony_cistatic bool its_alloc_vpe_table(u32 vpe_id) 33278c2ecf20Sopenharmony_ci{ 33288c2ecf20Sopenharmony_ci struct its_node *its; 33298c2ecf20Sopenharmony_ci int cpu; 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci /* 33328c2ecf20Sopenharmony_ci * Make sure the L2 tables are allocated on *all* v4 ITSs. We 33338c2ecf20Sopenharmony_ci * could try and only do it on ITSs corresponding to devices 33348c2ecf20Sopenharmony_ci * that have interrupts targeted at this VPE, but the 33358c2ecf20Sopenharmony_ci * complexity becomes crazy (and you have tons of memory 33368c2ecf20Sopenharmony_ci * anyway, right?). 33378c2ecf20Sopenharmony_ci */ 33388c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 33398c2ecf20Sopenharmony_ci struct its_baser *baser; 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci if (!is_v4(its)) 33428c2ecf20Sopenharmony_ci continue; 33438c2ecf20Sopenharmony_ci 33448c2ecf20Sopenharmony_ci baser = its_get_baser(its, GITS_BASER_TYPE_VCPU); 33458c2ecf20Sopenharmony_ci if (!baser) 33468c2ecf20Sopenharmony_ci return false; 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci if (!its_alloc_table_entry(its, baser, vpe_id)) 33498c2ecf20Sopenharmony_ci return false; 33508c2ecf20Sopenharmony_ci } 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci /* Non v4.1? No need to iterate RDs and go back early. */ 33538c2ecf20Sopenharmony_ci if (!gic_rdists->has_rvpeid) 33548c2ecf20Sopenharmony_ci return true; 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci /* 33578c2ecf20Sopenharmony_ci * Make sure the L2 tables are allocated for all copies of 33588c2ecf20Sopenharmony_ci * the L1 table on *all* v4.1 RDs. 33598c2ecf20Sopenharmony_ci */ 33608c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 33618c2ecf20Sopenharmony_ci if (!allocate_vpe_l2_table(cpu, vpe_id)) 33628c2ecf20Sopenharmony_ci return false; 33638c2ecf20Sopenharmony_ci } 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci return true; 33668c2ecf20Sopenharmony_ci} 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_cistatic struct its_device *its_create_device(struct its_node *its, u32 dev_id, 33698c2ecf20Sopenharmony_ci int nvecs, bool alloc_lpis) 33708c2ecf20Sopenharmony_ci{ 33718c2ecf20Sopenharmony_ci struct its_device *dev; 33728c2ecf20Sopenharmony_ci unsigned long *lpi_map = NULL; 33738c2ecf20Sopenharmony_ci unsigned long flags; 33748c2ecf20Sopenharmony_ci u16 *col_map = NULL; 33758c2ecf20Sopenharmony_ci void *itt; 33768c2ecf20Sopenharmony_ci int lpi_base; 33778c2ecf20Sopenharmony_ci int nr_lpis; 33788c2ecf20Sopenharmony_ci int nr_ites; 33798c2ecf20Sopenharmony_ci int sz; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci if (!its_alloc_device_table(its, dev_id)) 33828c2ecf20Sopenharmony_ci return NULL; 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci if (WARN_ON(!is_power_of_2(nvecs))) 33858c2ecf20Sopenharmony_ci nvecs = roundup_pow_of_two(nvecs); 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 33888c2ecf20Sopenharmony_ci /* 33898c2ecf20Sopenharmony_ci * Even if the device wants a single LPI, the ITT must be 33908c2ecf20Sopenharmony_ci * sized as a power of two (and you need at least one bit...). 33918c2ecf20Sopenharmony_ci */ 33928c2ecf20Sopenharmony_ci nr_ites = max(2, nvecs); 33938c2ecf20Sopenharmony_ci sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1); 33948c2ecf20Sopenharmony_ci sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; 33958c2ecf20Sopenharmony_ci itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node); 33968c2ecf20Sopenharmony_ci if (alloc_lpis) { 33978c2ecf20Sopenharmony_ci lpi_map = its_lpi_alloc(nvecs, &lpi_base, &nr_lpis); 33988c2ecf20Sopenharmony_ci if (lpi_map) 33998c2ecf20Sopenharmony_ci col_map = kcalloc(nr_lpis, sizeof(*col_map), 34008c2ecf20Sopenharmony_ci GFP_KERNEL); 34018c2ecf20Sopenharmony_ci } else { 34028c2ecf20Sopenharmony_ci col_map = kcalloc(nr_ites, sizeof(*col_map), GFP_KERNEL); 34038c2ecf20Sopenharmony_ci nr_lpis = 0; 34048c2ecf20Sopenharmony_ci lpi_base = 0; 34058c2ecf20Sopenharmony_ci } 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_ci if (!dev || !itt || !col_map || (!lpi_map && alloc_lpis)) { 34088c2ecf20Sopenharmony_ci kfree(dev); 34098c2ecf20Sopenharmony_ci kfree(itt); 34108c2ecf20Sopenharmony_ci kfree(lpi_map); 34118c2ecf20Sopenharmony_ci kfree(col_map); 34128c2ecf20Sopenharmony_ci return NULL; 34138c2ecf20Sopenharmony_ci } 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci gic_flush_dcache_to_poc(itt, sz); 34168c2ecf20Sopenharmony_ci 34178c2ecf20Sopenharmony_ci dev->its = its; 34188c2ecf20Sopenharmony_ci dev->itt = itt; 34198c2ecf20Sopenharmony_ci dev->nr_ites = nr_ites; 34208c2ecf20Sopenharmony_ci dev->event_map.lpi_map = lpi_map; 34218c2ecf20Sopenharmony_ci dev->event_map.col_map = col_map; 34228c2ecf20Sopenharmony_ci dev->event_map.lpi_base = lpi_base; 34238c2ecf20Sopenharmony_ci dev->event_map.nr_lpis = nr_lpis; 34248c2ecf20Sopenharmony_ci raw_spin_lock_init(&dev->event_map.vlpi_lock); 34258c2ecf20Sopenharmony_ci dev->device_id = dev_id; 34268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->entry); 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&its->lock, flags); 34298c2ecf20Sopenharmony_ci list_add(&dev->entry, &its->its_device_list); 34308c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&its->lock, flags); 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci /* Map device to its ITT */ 34338c2ecf20Sopenharmony_ci its_send_mapd(dev, 1); 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci return dev; 34368c2ecf20Sopenharmony_ci} 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_cistatic void its_free_device(struct its_device *its_dev) 34398c2ecf20Sopenharmony_ci{ 34408c2ecf20Sopenharmony_ci unsigned long flags; 34418c2ecf20Sopenharmony_ci 34428c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&its_dev->its->lock, flags); 34438c2ecf20Sopenharmony_ci list_del(&its_dev->entry); 34448c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&its_dev->its->lock, flags); 34458c2ecf20Sopenharmony_ci kfree(its_dev->event_map.col_map); 34468c2ecf20Sopenharmony_ci kfree(its_dev->itt); 34478c2ecf20Sopenharmony_ci kfree(its_dev); 34488c2ecf20Sopenharmony_ci} 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_cistatic int its_alloc_device_irq(struct its_device *dev, int nvecs, irq_hw_number_t *hwirq) 34518c2ecf20Sopenharmony_ci{ 34528c2ecf20Sopenharmony_ci int idx; 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci /* Find a free LPI region in lpi_map and allocate them. */ 34558c2ecf20Sopenharmony_ci idx = bitmap_find_free_region(dev->event_map.lpi_map, 34568c2ecf20Sopenharmony_ci dev->event_map.nr_lpis, 34578c2ecf20Sopenharmony_ci get_count_order(nvecs)); 34588c2ecf20Sopenharmony_ci if (idx < 0) 34598c2ecf20Sopenharmony_ci return -ENOSPC; 34608c2ecf20Sopenharmony_ci 34618c2ecf20Sopenharmony_ci *hwirq = dev->event_map.lpi_base + idx; 34628c2ecf20Sopenharmony_ci 34638c2ecf20Sopenharmony_ci return 0; 34648c2ecf20Sopenharmony_ci} 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_cistatic int its_msi_prepare(struct irq_domain *domain, struct device *dev, 34678c2ecf20Sopenharmony_ci int nvec, msi_alloc_info_t *info) 34688c2ecf20Sopenharmony_ci{ 34698c2ecf20Sopenharmony_ci struct its_node *its; 34708c2ecf20Sopenharmony_ci struct its_device *its_dev; 34718c2ecf20Sopenharmony_ci struct msi_domain_info *msi_info; 34728c2ecf20Sopenharmony_ci u32 dev_id; 34738c2ecf20Sopenharmony_ci int err = 0; 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci /* 34768c2ecf20Sopenharmony_ci * We ignore "dev" entirely, and rely on the dev_id that has 34778c2ecf20Sopenharmony_ci * been passed via the scratchpad. This limits this domain's 34788c2ecf20Sopenharmony_ci * usefulness to upper layers that definitely know that they 34798c2ecf20Sopenharmony_ci * are built on top of the ITS. 34808c2ecf20Sopenharmony_ci */ 34818c2ecf20Sopenharmony_ci dev_id = info->scratchpad[0].ul; 34828c2ecf20Sopenharmony_ci 34838c2ecf20Sopenharmony_ci msi_info = msi_get_domain_info(domain); 34848c2ecf20Sopenharmony_ci its = msi_info->data; 34858c2ecf20Sopenharmony_ci 34868c2ecf20Sopenharmony_ci if (!gic_rdists->has_direct_lpi && 34878c2ecf20Sopenharmony_ci vpe_proxy.dev && 34888c2ecf20Sopenharmony_ci vpe_proxy.dev->its == its && 34898c2ecf20Sopenharmony_ci dev_id == vpe_proxy.dev->device_id) { 34908c2ecf20Sopenharmony_ci /* Bad luck. Get yourself a better implementation */ 34918c2ecf20Sopenharmony_ci WARN_ONCE(1, "DevId %x clashes with GICv4 VPE proxy device\n", 34928c2ecf20Sopenharmony_ci dev_id); 34938c2ecf20Sopenharmony_ci return -EINVAL; 34948c2ecf20Sopenharmony_ci } 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci mutex_lock(&its->dev_alloc_lock); 34978c2ecf20Sopenharmony_ci its_dev = its_find_device(its, dev_id); 34988c2ecf20Sopenharmony_ci if (its_dev) { 34998c2ecf20Sopenharmony_ci /* 35008c2ecf20Sopenharmony_ci * We already have seen this ID, probably through 35018c2ecf20Sopenharmony_ci * another alias (PCI bridge of some sort). No need to 35028c2ecf20Sopenharmony_ci * create the device. 35038c2ecf20Sopenharmony_ci */ 35048c2ecf20Sopenharmony_ci its_dev->shared = true; 35058c2ecf20Sopenharmony_ci pr_debug("Reusing ITT for devID %x\n", dev_id); 35068c2ecf20Sopenharmony_ci goto out; 35078c2ecf20Sopenharmony_ci } 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci its_dev = its_create_device(its, dev_id, nvec, true); 35108c2ecf20Sopenharmony_ci if (!its_dev) { 35118c2ecf20Sopenharmony_ci err = -ENOMEM; 35128c2ecf20Sopenharmony_ci goto out; 35138c2ecf20Sopenharmony_ci } 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec)); 35168c2ecf20Sopenharmony_ciout: 35178c2ecf20Sopenharmony_ci mutex_unlock(&its->dev_alloc_lock); 35188c2ecf20Sopenharmony_ci info->scratchpad[0].ptr = its_dev; 35198c2ecf20Sopenharmony_ci return err; 35208c2ecf20Sopenharmony_ci} 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_cistatic struct msi_domain_ops its_msi_domain_ops = { 35238c2ecf20Sopenharmony_ci .msi_prepare = its_msi_prepare, 35248c2ecf20Sopenharmony_ci}; 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_cistatic int its_irq_gic_domain_alloc(struct irq_domain *domain, 35278c2ecf20Sopenharmony_ci unsigned int virq, 35288c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 35298c2ecf20Sopenharmony_ci{ 35308c2ecf20Sopenharmony_ci struct irq_fwspec fwspec; 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci if (irq_domain_get_of_node(domain->parent)) { 35338c2ecf20Sopenharmony_ci fwspec.fwnode = domain->parent->fwnode; 35348c2ecf20Sopenharmony_ci fwspec.param_count = 3; 35358c2ecf20Sopenharmony_ci fwspec.param[0] = GIC_IRQ_TYPE_LPI; 35368c2ecf20Sopenharmony_ci fwspec.param[1] = hwirq; 35378c2ecf20Sopenharmony_ci fwspec.param[2] = IRQ_TYPE_EDGE_RISING; 35388c2ecf20Sopenharmony_ci } else if (is_fwnode_irqchip(domain->parent->fwnode)) { 35398c2ecf20Sopenharmony_ci fwspec.fwnode = domain->parent->fwnode; 35408c2ecf20Sopenharmony_ci fwspec.param_count = 2; 35418c2ecf20Sopenharmony_ci fwspec.param[0] = hwirq; 35428c2ecf20Sopenharmony_ci fwspec.param[1] = IRQ_TYPE_EDGE_RISING; 35438c2ecf20Sopenharmony_ci } else { 35448c2ecf20Sopenharmony_ci return -EINVAL; 35458c2ecf20Sopenharmony_ci } 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); 35488c2ecf20Sopenharmony_ci} 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_cistatic int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 35518c2ecf20Sopenharmony_ci unsigned int nr_irqs, void *args) 35528c2ecf20Sopenharmony_ci{ 35538c2ecf20Sopenharmony_ci msi_alloc_info_t *info = args; 35548c2ecf20Sopenharmony_ci struct its_device *its_dev = info->scratchpad[0].ptr; 35558c2ecf20Sopenharmony_ci struct its_node *its = its_dev->its; 35568c2ecf20Sopenharmony_ci struct irq_data *irqd; 35578c2ecf20Sopenharmony_ci irq_hw_number_t hwirq; 35588c2ecf20Sopenharmony_ci int err; 35598c2ecf20Sopenharmony_ci int i; 35608c2ecf20Sopenharmony_ci 35618c2ecf20Sopenharmony_ci err = its_alloc_device_irq(its_dev, nr_irqs, &hwirq); 35628c2ecf20Sopenharmony_ci if (err) 35638c2ecf20Sopenharmony_ci return err; 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev)); 35668c2ecf20Sopenharmony_ci if (err) 35678c2ecf20Sopenharmony_ci return err; 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) { 35708c2ecf20Sopenharmony_ci err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i); 35718c2ecf20Sopenharmony_ci if (err) 35728c2ecf20Sopenharmony_ci return err; 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci irq_domain_set_hwirq_and_chip(domain, virq + i, 35758c2ecf20Sopenharmony_ci hwirq + i, &its_irq_chip, its_dev); 35768c2ecf20Sopenharmony_ci irqd = irq_get_irq_data(virq + i); 35778c2ecf20Sopenharmony_ci irqd_set_single_target(irqd); 35788c2ecf20Sopenharmony_ci irqd_set_affinity_on_activate(irqd); 35798c2ecf20Sopenharmony_ci pr_debug("ID:%d pID:%d vID:%d\n", 35808c2ecf20Sopenharmony_ci (int)(hwirq + i - its_dev->event_map.lpi_base), 35818c2ecf20Sopenharmony_ci (int)(hwirq + i), virq + i); 35828c2ecf20Sopenharmony_ci } 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci return 0; 35858c2ecf20Sopenharmony_ci} 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_cistatic int its_irq_domain_activate(struct irq_domain *domain, 35888c2ecf20Sopenharmony_ci struct irq_data *d, bool reserve) 35898c2ecf20Sopenharmony_ci{ 35908c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 35918c2ecf20Sopenharmony_ci u32 event = its_get_event_id(d); 35928c2ecf20Sopenharmony_ci int cpu; 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_ci cpu = its_select_cpu(d, cpu_online_mask); 35958c2ecf20Sopenharmony_ci if (cpu < 0 || cpu >= nr_cpu_ids) 35968c2ecf20Sopenharmony_ci return -EINVAL; 35978c2ecf20Sopenharmony_ci 35988c2ecf20Sopenharmony_ci its_inc_lpi_count(d, cpu); 35998c2ecf20Sopenharmony_ci its_dev->event_map.col_map[event] = cpu; 36008c2ecf20Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(cpu)); 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci /* Map the GIC IRQ and event to the device */ 36038c2ecf20Sopenharmony_ci its_send_mapti(its_dev, d->hwirq, event); 36048c2ecf20Sopenharmony_ci return 0; 36058c2ecf20Sopenharmony_ci} 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_cistatic void its_irq_domain_deactivate(struct irq_domain *domain, 36088c2ecf20Sopenharmony_ci struct irq_data *d) 36098c2ecf20Sopenharmony_ci{ 36108c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 36118c2ecf20Sopenharmony_ci u32 event = its_get_event_id(d); 36128c2ecf20Sopenharmony_ci 36138c2ecf20Sopenharmony_ci its_dec_lpi_count(d, its_dev->event_map.col_map[event]); 36148c2ecf20Sopenharmony_ci /* Stop the delivery of interrupts */ 36158c2ecf20Sopenharmony_ci its_send_discard(its_dev, event); 36168c2ecf20Sopenharmony_ci} 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_cistatic void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, 36198c2ecf20Sopenharmony_ci unsigned int nr_irqs) 36208c2ecf20Sopenharmony_ci{ 36218c2ecf20Sopenharmony_ci struct irq_data *d = irq_domain_get_irq_data(domain, virq); 36228c2ecf20Sopenharmony_ci struct its_device *its_dev = irq_data_get_irq_chip_data(d); 36238c2ecf20Sopenharmony_ci struct its_node *its = its_dev->its; 36248c2ecf20Sopenharmony_ci int i; 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci bitmap_release_region(its_dev->event_map.lpi_map, 36278c2ecf20Sopenharmony_ci its_get_event_id(irq_domain_get_irq_data(domain, virq)), 36288c2ecf20Sopenharmony_ci get_count_order(nr_irqs)); 36298c2ecf20Sopenharmony_ci 36308c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) { 36318c2ecf20Sopenharmony_ci struct irq_data *data = irq_domain_get_irq_data(domain, 36328c2ecf20Sopenharmony_ci virq + i); 36338c2ecf20Sopenharmony_ci /* Nuke the entry in the domain */ 36348c2ecf20Sopenharmony_ci irq_domain_reset_irq_data(data); 36358c2ecf20Sopenharmony_ci } 36368c2ecf20Sopenharmony_ci 36378c2ecf20Sopenharmony_ci mutex_lock(&its->dev_alloc_lock); 36388c2ecf20Sopenharmony_ci 36398c2ecf20Sopenharmony_ci /* 36408c2ecf20Sopenharmony_ci * If all interrupts have been freed, start mopping the 36418c2ecf20Sopenharmony_ci * floor. This is conditioned on the device not being shared. 36428c2ecf20Sopenharmony_ci */ 36438c2ecf20Sopenharmony_ci if (!its_dev->shared && 36448c2ecf20Sopenharmony_ci bitmap_empty(its_dev->event_map.lpi_map, 36458c2ecf20Sopenharmony_ci its_dev->event_map.nr_lpis)) { 36468c2ecf20Sopenharmony_ci its_lpi_free(its_dev->event_map.lpi_map, 36478c2ecf20Sopenharmony_ci its_dev->event_map.lpi_base, 36488c2ecf20Sopenharmony_ci its_dev->event_map.nr_lpis); 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci /* Unmap device/itt */ 36518c2ecf20Sopenharmony_ci its_send_mapd(its_dev, 0); 36528c2ecf20Sopenharmony_ci its_free_device(its_dev); 36538c2ecf20Sopenharmony_ci } 36548c2ecf20Sopenharmony_ci 36558c2ecf20Sopenharmony_ci mutex_unlock(&its->dev_alloc_lock); 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci irq_domain_free_irqs_parent(domain, virq, nr_irqs); 36588c2ecf20Sopenharmony_ci} 36598c2ecf20Sopenharmony_ci 36608c2ecf20Sopenharmony_cistatic const struct irq_domain_ops its_domain_ops = { 36618c2ecf20Sopenharmony_ci .alloc = its_irq_domain_alloc, 36628c2ecf20Sopenharmony_ci .free = its_irq_domain_free, 36638c2ecf20Sopenharmony_ci .activate = its_irq_domain_activate, 36648c2ecf20Sopenharmony_ci .deactivate = its_irq_domain_deactivate, 36658c2ecf20Sopenharmony_ci}; 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci/* 36688c2ecf20Sopenharmony_ci * This is insane. 36698c2ecf20Sopenharmony_ci * 36708c2ecf20Sopenharmony_ci * If a GICv4.0 doesn't implement Direct LPIs (which is extremely 36718c2ecf20Sopenharmony_ci * likely), the only way to perform an invalidate is to use a fake 36728c2ecf20Sopenharmony_ci * device to issue an INV command, implying that the LPI has first 36738c2ecf20Sopenharmony_ci * been mapped to some event on that device. Since this is not exactly 36748c2ecf20Sopenharmony_ci * cheap, we try to keep that mapping around as long as possible, and 36758c2ecf20Sopenharmony_ci * only issue an UNMAP if we're short on available slots. 36768c2ecf20Sopenharmony_ci * 36778c2ecf20Sopenharmony_ci * Broken by design(tm). 36788c2ecf20Sopenharmony_ci * 36798c2ecf20Sopenharmony_ci * GICv4.1, on the other hand, mandates that we're able to invalidate 36808c2ecf20Sopenharmony_ci * by writing to a MMIO register. It doesn't implement the whole of 36818c2ecf20Sopenharmony_ci * DirectLPI, but that's good enough. And most of the time, we don't 36828c2ecf20Sopenharmony_ci * even have to invalidate anything, as the redistributor can be told 36838c2ecf20Sopenharmony_ci * whether to generate a doorbell or not (we thus leave it enabled, 36848c2ecf20Sopenharmony_ci * always). 36858c2ecf20Sopenharmony_ci */ 36868c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_unmap_locked(struct its_vpe *vpe) 36878c2ecf20Sopenharmony_ci{ 36888c2ecf20Sopenharmony_ci /* GICv4.1 doesn't use a proxy, so nothing to do here */ 36898c2ecf20Sopenharmony_ci if (gic_rdists->has_rvpeid) 36908c2ecf20Sopenharmony_ci return; 36918c2ecf20Sopenharmony_ci 36928c2ecf20Sopenharmony_ci /* Already unmapped? */ 36938c2ecf20Sopenharmony_ci if (vpe->vpe_proxy_event == -1) 36948c2ecf20Sopenharmony_ci return; 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci its_send_discard(vpe_proxy.dev, vpe->vpe_proxy_event); 36978c2ecf20Sopenharmony_ci vpe_proxy.vpes[vpe->vpe_proxy_event] = NULL; 36988c2ecf20Sopenharmony_ci 36998c2ecf20Sopenharmony_ci /* 37008c2ecf20Sopenharmony_ci * We don't track empty slots at all, so let's move the 37018c2ecf20Sopenharmony_ci * next_victim pointer if we can quickly reuse that slot 37028c2ecf20Sopenharmony_ci * instead of nuking an existing entry. Not clear that this is 37038c2ecf20Sopenharmony_ci * always a win though, and this might just generate a ripple 37048c2ecf20Sopenharmony_ci * effect... Let's just hope VPEs don't migrate too often. 37058c2ecf20Sopenharmony_ci */ 37068c2ecf20Sopenharmony_ci if (vpe_proxy.vpes[vpe_proxy.next_victim]) 37078c2ecf20Sopenharmony_ci vpe_proxy.next_victim = vpe->vpe_proxy_event; 37088c2ecf20Sopenharmony_ci 37098c2ecf20Sopenharmony_ci vpe->vpe_proxy_event = -1; 37108c2ecf20Sopenharmony_ci} 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_unmap(struct its_vpe *vpe) 37138c2ecf20Sopenharmony_ci{ 37148c2ecf20Sopenharmony_ci /* GICv4.1 doesn't use a proxy, so nothing to do here */ 37158c2ecf20Sopenharmony_ci if (gic_rdists->has_rvpeid) 37168c2ecf20Sopenharmony_ci return; 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_ci if (!gic_rdists->has_direct_lpi) { 37198c2ecf20Sopenharmony_ci unsigned long flags; 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vpe_proxy.lock, flags); 37228c2ecf20Sopenharmony_ci its_vpe_db_proxy_unmap_locked(vpe); 37238c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vpe_proxy.lock, flags); 37248c2ecf20Sopenharmony_ci } 37258c2ecf20Sopenharmony_ci} 37268c2ecf20Sopenharmony_ci 37278c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_map_locked(struct its_vpe *vpe) 37288c2ecf20Sopenharmony_ci{ 37298c2ecf20Sopenharmony_ci /* GICv4.1 doesn't use a proxy, so nothing to do here */ 37308c2ecf20Sopenharmony_ci if (gic_rdists->has_rvpeid) 37318c2ecf20Sopenharmony_ci return; 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci /* Already mapped? */ 37348c2ecf20Sopenharmony_ci if (vpe->vpe_proxy_event != -1) 37358c2ecf20Sopenharmony_ci return; 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci /* This slot was already allocated. Kick the other VPE out. */ 37388c2ecf20Sopenharmony_ci if (vpe_proxy.vpes[vpe_proxy.next_victim]) 37398c2ecf20Sopenharmony_ci its_vpe_db_proxy_unmap_locked(vpe_proxy.vpes[vpe_proxy.next_victim]); 37408c2ecf20Sopenharmony_ci 37418c2ecf20Sopenharmony_ci /* Map the new VPE instead */ 37428c2ecf20Sopenharmony_ci vpe_proxy.vpes[vpe_proxy.next_victim] = vpe; 37438c2ecf20Sopenharmony_ci vpe->vpe_proxy_event = vpe_proxy.next_victim; 37448c2ecf20Sopenharmony_ci vpe_proxy.next_victim = (vpe_proxy.next_victim + 1) % vpe_proxy.dev->nr_ites; 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci vpe_proxy.dev->event_map.col_map[vpe->vpe_proxy_event] = vpe->col_idx; 37478c2ecf20Sopenharmony_ci its_send_mapti(vpe_proxy.dev, vpe->vpe_db_lpi, vpe->vpe_proxy_event); 37488c2ecf20Sopenharmony_ci} 37498c2ecf20Sopenharmony_ci 37508c2ecf20Sopenharmony_cistatic void its_vpe_db_proxy_move(struct its_vpe *vpe, int from, int to) 37518c2ecf20Sopenharmony_ci{ 37528c2ecf20Sopenharmony_ci unsigned long flags; 37538c2ecf20Sopenharmony_ci struct its_collection *target_col; 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_ci /* GICv4.1 doesn't use a proxy, so nothing to do here */ 37568c2ecf20Sopenharmony_ci if (gic_rdists->has_rvpeid) 37578c2ecf20Sopenharmony_ci return; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci if (gic_rdists->has_direct_lpi) { 37608c2ecf20Sopenharmony_ci void __iomem *rdbase; 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci rdbase = per_cpu_ptr(gic_rdists->rdist, from)->rd_base; 37638c2ecf20Sopenharmony_ci gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR); 37648c2ecf20Sopenharmony_ci wait_for_syncr(rdbase); 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci return; 37678c2ecf20Sopenharmony_ci } 37688c2ecf20Sopenharmony_ci 37698c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vpe_proxy.lock, flags); 37708c2ecf20Sopenharmony_ci 37718c2ecf20Sopenharmony_ci its_vpe_db_proxy_map_locked(vpe); 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci target_col = &vpe_proxy.dev->its->collections[to]; 37748c2ecf20Sopenharmony_ci its_send_movi(vpe_proxy.dev, target_col, vpe->vpe_proxy_event); 37758c2ecf20Sopenharmony_ci vpe_proxy.dev->event_map.col_map[vpe->vpe_proxy_event] = to; 37768c2ecf20Sopenharmony_ci 37778c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vpe_proxy.lock, flags); 37788c2ecf20Sopenharmony_ci} 37798c2ecf20Sopenharmony_ci 37808c2ecf20Sopenharmony_cistatic int its_vpe_set_affinity(struct irq_data *d, 37818c2ecf20Sopenharmony_ci const struct cpumask *mask_val, 37828c2ecf20Sopenharmony_ci bool force) 37838c2ecf20Sopenharmony_ci{ 37848c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 37858c2ecf20Sopenharmony_ci struct cpumask common, *table_mask; 37868c2ecf20Sopenharmony_ci unsigned long flags; 37878c2ecf20Sopenharmony_ci int from, cpu; 37888c2ecf20Sopenharmony_ci 37898c2ecf20Sopenharmony_ci /* 37908c2ecf20Sopenharmony_ci * Changing affinity is mega expensive, so let's be as lazy as 37918c2ecf20Sopenharmony_ci * we can and only do it if we really have to. Also, if mapped 37928c2ecf20Sopenharmony_ci * into the proxy device, we need to move the doorbell 37938c2ecf20Sopenharmony_ci * interrupt to its new location. 37948c2ecf20Sopenharmony_ci * 37958c2ecf20Sopenharmony_ci * Another thing is that changing the affinity of a vPE affects 37968c2ecf20Sopenharmony_ci * *other interrupts* such as all the vLPIs that are routed to 37978c2ecf20Sopenharmony_ci * this vPE. This means that the irq_desc lock is not enough to 37988c2ecf20Sopenharmony_ci * protect us, and that we must ensure nobody samples vpe->col_idx 37998c2ecf20Sopenharmony_ci * during the update, hence the lock below which must also be 38008c2ecf20Sopenharmony_ci * taken on any vLPI handling path that evaluates vpe->col_idx. 38018c2ecf20Sopenharmony_ci */ 38028c2ecf20Sopenharmony_ci from = vpe_to_cpuid_lock(vpe, &flags); 38038c2ecf20Sopenharmony_ci table_mask = gic_data_rdist_cpu(from)->vpe_table_mask; 38048c2ecf20Sopenharmony_ci 38058c2ecf20Sopenharmony_ci /* 38068c2ecf20Sopenharmony_ci * If we are offered another CPU in the same GICv4.1 ITS 38078c2ecf20Sopenharmony_ci * affinity, pick this one. Otherwise, any CPU will do. 38088c2ecf20Sopenharmony_ci */ 38098c2ecf20Sopenharmony_ci if (table_mask && cpumask_and(&common, mask_val, table_mask)) 38108c2ecf20Sopenharmony_ci cpu = cpumask_test_cpu(from, &common) ? from : cpumask_first(&common); 38118c2ecf20Sopenharmony_ci else 38128c2ecf20Sopenharmony_ci cpu = cpumask_first(mask_val); 38138c2ecf20Sopenharmony_ci 38148c2ecf20Sopenharmony_ci if (from == cpu) 38158c2ecf20Sopenharmony_ci goto out; 38168c2ecf20Sopenharmony_ci 38178c2ecf20Sopenharmony_ci vpe->col_idx = cpu; 38188c2ecf20Sopenharmony_ci 38198c2ecf20Sopenharmony_ci its_send_vmovp(vpe); 38208c2ecf20Sopenharmony_ci its_vpe_db_proxy_move(vpe, from, cpu); 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_ciout: 38238c2ecf20Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(cpu)); 38248c2ecf20Sopenharmony_ci vpe_to_cpuid_unlock(vpe, flags); 38258c2ecf20Sopenharmony_ci 38268c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK_DONE; 38278c2ecf20Sopenharmony_ci} 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_cistatic void its_wait_vpt_parse_complete(void) 38308c2ecf20Sopenharmony_ci{ 38318c2ecf20Sopenharmony_ci void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 38328c2ecf20Sopenharmony_ci u64 val; 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_ci if (!gic_rdists->has_vpend_valid_dirty) 38358c2ecf20Sopenharmony_ci return; 38368c2ecf20Sopenharmony_ci 38378c2ecf20Sopenharmony_ci WARN_ON_ONCE(readq_relaxed_poll_timeout_atomic(vlpi_base + GICR_VPENDBASER, 38388c2ecf20Sopenharmony_ci val, 38398c2ecf20Sopenharmony_ci !(val & GICR_VPENDBASER_Dirty), 38408c2ecf20Sopenharmony_ci 10, 500)); 38418c2ecf20Sopenharmony_ci} 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_cistatic void its_vpe_schedule(struct its_vpe *vpe) 38448c2ecf20Sopenharmony_ci{ 38458c2ecf20Sopenharmony_ci void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 38468c2ecf20Sopenharmony_ci u64 val; 38478c2ecf20Sopenharmony_ci 38488c2ecf20Sopenharmony_ci /* Schedule the VPE */ 38498c2ecf20Sopenharmony_ci val = virt_to_phys(page_address(vpe->its_vm->vprop_page)) & 38508c2ecf20Sopenharmony_ci GENMASK_ULL(51, 12); 38518c2ecf20Sopenharmony_ci val |= (LPI_NRBITS - 1) & GICR_VPROPBASER_IDBITS_MASK; 38528c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_RaWb; 38538c2ecf20Sopenharmony_ci val |= GICR_VPROPBASER_InnerShareable; 38548c2ecf20Sopenharmony_ci gicr_write_vpropbaser(val, vlpi_base + GICR_VPROPBASER); 38558c2ecf20Sopenharmony_ci 38568c2ecf20Sopenharmony_ci val = virt_to_phys(page_address(vpe->vpt_page)) & 38578c2ecf20Sopenharmony_ci GENMASK_ULL(51, 16); 38588c2ecf20Sopenharmony_ci val |= GICR_VPENDBASER_RaWaWb; 38598c2ecf20Sopenharmony_ci val |= GICR_VPENDBASER_InnerShareable; 38608c2ecf20Sopenharmony_ci /* 38618c2ecf20Sopenharmony_ci * There is no good way of finding out if the pending table is 38628c2ecf20Sopenharmony_ci * empty as we can race against the doorbell interrupt very 38638c2ecf20Sopenharmony_ci * easily. So in the end, vpe->pending_last is only an 38648c2ecf20Sopenharmony_ci * indication that the vcpu has something pending, not one 38658c2ecf20Sopenharmony_ci * that the pending table is empty. A good implementation 38668c2ecf20Sopenharmony_ci * would be able to read its coarse map pretty quickly anyway, 38678c2ecf20Sopenharmony_ci * making this a tolerable issue. 38688c2ecf20Sopenharmony_ci */ 38698c2ecf20Sopenharmony_ci val |= GICR_VPENDBASER_PendingLast; 38708c2ecf20Sopenharmony_ci val |= vpe->idai ? GICR_VPENDBASER_IDAI : 0; 38718c2ecf20Sopenharmony_ci val |= GICR_VPENDBASER_Valid; 38728c2ecf20Sopenharmony_ci gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); 38738c2ecf20Sopenharmony_ci 38748c2ecf20Sopenharmony_ci its_wait_vpt_parse_complete(); 38758c2ecf20Sopenharmony_ci} 38768c2ecf20Sopenharmony_ci 38778c2ecf20Sopenharmony_cistatic void its_vpe_deschedule(struct its_vpe *vpe) 38788c2ecf20Sopenharmony_ci{ 38798c2ecf20Sopenharmony_ci void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 38808c2ecf20Sopenharmony_ci u64 val; 38818c2ecf20Sopenharmony_ci 38828c2ecf20Sopenharmony_ci val = its_clear_vpend_valid(vlpi_base, 0, 0); 38838c2ecf20Sopenharmony_ci 38848c2ecf20Sopenharmony_ci vpe->idai = !!(val & GICR_VPENDBASER_IDAI); 38858c2ecf20Sopenharmony_ci vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast); 38868c2ecf20Sopenharmony_ci} 38878c2ecf20Sopenharmony_ci 38888c2ecf20Sopenharmony_cistatic void its_vpe_invall(struct its_vpe *vpe) 38898c2ecf20Sopenharmony_ci{ 38908c2ecf20Sopenharmony_ci struct its_node *its; 38918c2ecf20Sopenharmony_ci 38928c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 38938c2ecf20Sopenharmony_ci if (!is_v4(its)) 38948c2ecf20Sopenharmony_ci continue; 38958c2ecf20Sopenharmony_ci 38968c2ecf20Sopenharmony_ci if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr]) 38978c2ecf20Sopenharmony_ci continue; 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci /* 39008c2ecf20Sopenharmony_ci * Sending a VINVALL to a single ITS is enough, as all 39018c2ecf20Sopenharmony_ci * we need is to reach the redistributors. 39028c2ecf20Sopenharmony_ci */ 39038c2ecf20Sopenharmony_ci its_send_vinvall(its, vpe); 39048c2ecf20Sopenharmony_ci return; 39058c2ecf20Sopenharmony_ci } 39068c2ecf20Sopenharmony_ci} 39078c2ecf20Sopenharmony_ci 39088c2ecf20Sopenharmony_cistatic int its_vpe_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) 39098c2ecf20Sopenharmony_ci{ 39108c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 39118c2ecf20Sopenharmony_ci struct its_cmd_info *info = vcpu_info; 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci switch (info->cmd_type) { 39148c2ecf20Sopenharmony_ci case SCHEDULE_VPE: 39158c2ecf20Sopenharmony_ci its_vpe_schedule(vpe); 39168c2ecf20Sopenharmony_ci return 0; 39178c2ecf20Sopenharmony_ci 39188c2ecf20Sopenharmony_ci case DESCHEDULE_VPE: 39198c2ecf20Sopenharmony_ci its_vpe_deschedule(vpe); 39208c2ecf20Sopenharmony_ci return 0; 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci case INVALL_VPE: 39238c2ecf20Sopenharmony_ci its_vpe_invall(vpe); 39248c2ecf20Sopenharmony_ci return 0; 39258c2ecf20Sopenharmony_ci 39268c2ecf20Sopenharmony_ci default: 39278c2ecf20Sopenharmony_ci return -EINVAL; 39288c2ecf20Sopenharmony_ci } 39298c2ecf20Sopenharmony_ci} 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_cistatic void its_vpe_send_cmd(struct its_vpe *vpe, 39328c2ecf20Sopenharmony_ci void (*cmd)(struct its_device *, u32)) 39338c2ecf20Sopenharmony_ci{ 39348c2ecf20Sopenharmony_ci unsigned long flags; 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vpe_proxy.lock, flags); 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ci its_vpe_db_proxy_map_locked(vpe); 39398c2ecf20Sopenharmony_ci cmd(vpe_proxy.dev, vpe->vpe_proxy_event); 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vpe_proxy.lock, flags); 39428c2ecf20Sopenharmony_ci} 39438c2ecf20Sopenharmony_ci 39448c2ecf20Sopenharmony_cistatic void its_vpe_send_inv(struct irq_data *d) 39458c2ecf20Sopenharmony_ci{ 39468c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 39478c2ecf20Sopenharmony_ci 39488c2ecf20Sopenharmony_ci if (gic_rdists->has_direct_lpi) 39498c2ecf20Sopenharmony_ci __direct_lpi_inv(d, d->parent_data->hwirq); 39508c2ecf20Sopenharmony_ci else 39518c2ecf20Sopenharmony_ci its_vpe_send_cmd(vpe, its_send_inv); 39528c2ecf20Sopenharmony_ci} 39538c2ecf20Sopenharmony_ci 39548c2ecf20Sopenharmony_cistatic void its_vpe_mask_irq(struct irq_data *d) 39558c2ecf20Sopenharmony_ci{ 39568c2ecf20Sopenharmony_ci /* 39578c2ecf20Sopenharmony_ci * We need to unmask the LPI, which is described by the parent 39588c2ecf20Sopenharmony_ci * irq_data. Instead of calling into the parent (which won't 39598c2ecf20Sopenharmony_ci * exactly do the right thing, let's simply use the 39608c2ecf20Sopenharmony_ci * parent_data pointer. Yes, I'm naughty. 39618c2ecf20Sopenharmony_ci */ 39628c2ecf20Sopenharmony_ci lpi_write_config(d->parent_data, LPI_PROP_ENABLED, 0); 39638c2ecf20Sopenharmony_ci its_vpe_send_inv(d); 39648c2ecf20Sopenharmony_ci} 39658c2ecf20Sopenharmony_ci 39668c2ecf20Sopenharmony_cistatic void its_vpe_unmask_irq(struct irq_data *d) 39678c2ecf20Sopenharmony_ci{ 39688c2ecf20Sopenharmony_ci /* Same hack as above... */ 39698c2ecf20Sopenharmony_ci lpi_write_config(d->parent_data, 0, LPI_PROP_ENABLED); 39708c2ecf20Sopenharmony_ci its_vpe_send_inv(d); 39718c2ecf20Sopenharmony_ci} 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_cistatic int its_vpe_set_irqchip_state(struct irq_data *d, 39748c2ecf20Sopenharmony_ci enum irqchip_irq_state which, 39758c2ecf20Sopenharmony_ci bool state) 39768c2ecf20Sopenharmony_ci{ 39778c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci if (which != IRQCHIP_STATE_PENDING) 39808c2ecf20Sopenharmony_ci return -EINVAL; 39818c2ecf20Sopenharmony_ci 39828c2ecf20Sopenharmony_ci if (gic_rdists->has_direct_lpi) { 39838c2ecf20Sopenharmony_ci void __iomem *rdbase; 39848c2ecf20Sopenharmony_ci 39858c2ecf20Sopenharmony_ci rdbase = per_cpu_ptr(gic_rdists->rdist, vpe->col_idx)->rd_base; 39868c2ecf20Sopenharmony_ci if (state) { 39878c2ecf20Sopenharmony_ci gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_SETLPIR); 39888c2ecf20Sopenharmony_ci } else { 39898c2ecf20Sopenharmony_ci gic_write_lpir(vpe->vpe_db_lpi, rdbase + GICR_CLRLPIR); 39908c2ecf20Sopenharmony_ci wait_for_syncr(rdbase); 39918c2ecf20Sopenharmony_ci } 39928c2ecf20Sopenharmony_ci } else { 39938c2ecf20Sopenharmony_ci if (state) 39948c2ecf20Sopenharmony_ci its_vpe_send_cmd(vpe, its_send_int); 39958c2ecf20Sopenharmony_ci else 39968c2ecf20Sopenharmony_ci its_vpe_send_cmd(vpe, its_send_clear); 39978c2ecf20Sopenharmony_ci } 39988c2ecf20Sopenharmony_ci 39998c2ecf20Sopenharmony_ci return 0; 40008c2ecf20Sopenharmony_ci} 40018c2ecf20Sopenharmony_ci 40028c2ecf20Sopenharmony_cistatic int its_vpe_retrigger(struct irq_data *d) 40038c2ecf20Sopenharmony_ci{ 40048c2ecf20Sopenharmony_ci return !its_vpe_set_irqchip_state(d, IRQCHIP_STATE_PENDING, true); 40058c2ecf20Sopenharmony_ci} 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_cistatic struct irq_chip its_vpe_irq_chip = { 40088c2ecf20Sopenharmony_ci .name = "GICv4-vpe", 40098c2ecf20Sopenharmony_ci .irq_mask = its_vpe_mask_irq, 40108c2ecf20Sopenharmony_ci .irq_unmask = its_vpe_unmask_irq, 40118c2ecf20Sopenharmony_ci .irq_eoi = irq_chip_eoi_parent, 40128c2ecf20Sopenharmony_ci .irq_set_affinity = its_vpe_set_affinity, 40138c2ecf20Sopenharmony_ci .irq_retrigger = its_vpe_retrigger, 40148c2ecf20Sopenharmony_ci .irq_set_irqchip_state = its_vpe_set_irqchip_state, 40158c2ecf20Sopenharmony_ci .irq_set_vcpu_affinity = its_vpe_set_vcpu_affinity, 40168c2ecf20Sopenharmony_ci}; 40178c2ecf20Sopenharmony_ci 40188c2ecf20Sopenharmony_cistatic struct its_node *find_4_1_its(void) 40198c2ecf20Sopenharmony_ci{ 40208c2ecf20Sopenharmony_ci static struct its_node *its = NULL; 40218c2ecf20Sopenharmony_ci 40228c2ecf20Sopenharmony_ci if (!its) { 40238c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 40248c2ecf20Sopenharmony_ci if (is_v4_1(its)) 40258c2ecf20Sopenharmony_ci return its; 40268c2ecf20Sopenharmony_ci } 40278c2ecf20Sopenharmony_ci 40288c2ecf20Sopenharmony_ci /* Oops? */ 40298c2ecf20Sopenharmony_ci its = NULL; 40308c2ecf20Sopenharmony_ci } 40318c2ecf20Sopenharmony_ci 40328c2ecf20Sopenharmony_ci return its; 40338c2ecf20Sopenharmony_ci} 40348c2ecf20Sopenharmony_ci 40358c2ecf20Sopenharmony_cistatic void its_vpe_4_1_send_inv(struct irq_data *d) 40368c2ecf20Sopenharmony_ci{ 40378c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 40388c2ecf20Sopenharmony_ci struct its_node *its; 40398c2ecf20Sopenharmony_ci 40408c2ecf20Sopenharmony_ci /* 40418c2ecf20Sopenharmony_ci * GICv4.1 wants doorbells to be invalidated using the 40428c2ecf20Sopenharmony_ci * INVDB command in order to be broadcast to all RDs. Send 40438c2ecf20Sopenharmony_ci * it to the first valid ITS, and let the HW do its magic. 40448c2ecf20Sopenharmony_ci */ 40458c2ecf20Sopenharmony_ci its = find_4_1_its(); 40468c2ecf20Sopenharmony_ci if (its) 40478c2ecf20Sopenharmony_ci its_send_invdb(its, vpe); 40488c2ecf20Sopenharmony_ci} 40498c2ecf20Sopenharmony_ci 40508c2ecf20Sopenharmony_cistatic void its_vpe_4_1_mask_irq(struct irq_data *d) 40518c2ecf20Sopenharmony_ci{ 40528c2ecf20Sopenharmony_ci lpi_write_config(d->parent_data, LPI_PROP_ENABLED, 0); 40538c2ecf20Sopenharmony_ci its_vpe_4_1_send_inv(d); 40548c2ecf20Sopenharmony_ci} 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_cistatic void its_vpe_4_1_unmask_irq(struct irq_data *d) 40578c2ecf20Sopenharmony_ci{ 40588c2ecf20Sopenharmony_ci lpi_write_config(d->parent_data, 0, LPI_PROP_ENABLED); 40598c2ecf20Sopenharmony_ci its_vpe_4_1_send_inv(d); 40608c2ecf20Sopenharmony_ci} 40618c2ecf20Sopenharmony_ci 40628c2ecf20Sopenharmony_cistatic void its_vpe_4_1_schedule(struct its_vpe *vpe, 40638c2ecf20Sopenharmony_ci struct its_cmd_info *info) 40648c2ecf20Sopenharmony_ci{ 40658c2ecf20Sopenharmony_ci void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 40668c2ecf20Sopenharmony_ci u64 val = 0; 40678c2ecf20Sopenharmony_ci 40688c2ecf20Sopenharmony_ci /* Schedule the VPE */ 40698c2ecf20Sopenharmony_ci val |= GICR_VPENDBASER_Valid; 40708c2ecf20Sopenharmony_ci val |= info->g0en ? GICR_VPENDBASER_4_1_VGRP0EN : 0; 40718c2ecf20Sopenharmony_ci val |= info->g1en ? GICR_VPENDBASER_4_1_VGRP1EN : 0; 40728c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_VPENDBASER_4_1_VPEID, vpe->vpe_id); 40738c2ecf20Sopenharmony_ci 40748c2ecf20Sopenharmony_ci gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER); 40758c2ecf20Sopenharmony_ci 40768c2ecf20Sopenharmony_ci its_wait_vpt_parse_complete(); 40778c2ecf20Sopenharmony_ci} 40788c2ecf20Sopenharmony_ci 40798c2ecf20Sopenharmony_cistatic void its_vpe_4_1_deschedule(struct its_vpe *vpe, 40808c2ecf20Sopenharmony_ci struct its_cmd_info *info) 40818c2ecf20Sopenharmony_ci{ 40828c2ecf20Sopenharmony_ci void __iomem *vlpi_base = gic_data_rdist_vlpi_base(); 40838c2ecf20Sopenharmony_ci u64 val; 40848c2ecf20Sopenharmony_ci 40858c2ecf20Sopenharmony_ci if (info->req_db) { 40868c2ecf20Sopenharmony_ci unsigned long flags; 40878c2ecf20Sopenharmony_ci 40888c2ecf20Sopenharmony_ci /* 40898c2ecf20Sopenharmony_ci * vPE is going to block: make the vPE non-resident with 40908c2ecf20Sopenharmony_ci * PendingLast clear and DB set. The GIC guarantees that if 40918c2ecf20Sopenharmony_ci * we read-back PendingLast clear, then a doorbell will be 40928c2ecf20Sopenharmony_ci * delivered when an interrupt comes. 40938c2ecf20Sopenharmony_ci * 40948c2ecf20Sopenharmony_ci * Note the locking to deal with the concurrent update of 40958c2ecf20Sopenharmony_ci * pending_last from the doorbell interrupt handler that can 40968c2ecf20Sopenharmony_ci * run concurrently. 40978c2ecf20Sopenharmony_ci */ 40988c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&vpe->vpe_lock, flags); 40998c2ecf20Sopenharmony_ci val = its_clear_vpend_valid(vlpi_base, 41008c2ecf20Sopenharmony_ci GICR_VPENDBASER_PendingLast, 41018c2ecf20Sopenharmony_ci GICR_VPENDBASER_4_1_DB); 41028c2ecf20Sopenharmony_ci vpe->pending_last = !!(val & GICR_VPENDBASER_PendingLast); 41038c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&vpe->vpe_lock, flags); 41048c2ecf20Sopenharmony_ci } else { 41058c2ecf20Sopenharmony_ci /* 41068c2ecf20Sopenharmony_ci * We're not blocking, so just make the vPE non-resident 41078c2ecf20Sopenharmony_ci * with PendingLast set, indicating that we'll be back. 41088c2ecf20Sopenharmony_ci */ 41098c2ecf20Sopenharmony_ci val = its_clear_vpend_valid(vlpi_base, 41108c2ecf20Sopenharmony_ci 0, 41118c2ecf20Sopenharmony_ci GICR_VPENDBASER_PendingLast); 41128c2ecf20Sopenharmony_ci vpe->pending_last = true; 41138c2ecf20Sopenharmony_ci } 41148c2ecf20Sopenharmony_ci} 41158c2ecf20Sopenharmony_ci 41168c2ecf20Sopenharmony_cistatic void its_vpe_4_1_invall(struct its_vpe *vpe) 41178c2ecf20Sopenharmony_ci{ 41188c2ecf20Sopenharmony_ci void __iomem *rdbase; 41198c2ecf20Sopenharmony_ci unsigned long flags; 41208c2ecf20Sopenharmony_ci u64 val; 41218c2ecf20Sopenharmony_ci int cpu; 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci val = GICR_INVALLR_V; 41248c2ecf20Sopenharmony_ci val |= FIELD_PREP(GICR_INVALLR_VPEID, vpe->vpe_id); 41258c2ecf20Sopenharmony_ci 41268c2ecf20Sopenharmony_ci /* Target the redistributor this vPE is currently known on */ 41278c2ecf20Sopenharmony_ci cpu = vpe_to_cpuid_lock(vpe, &flags); 41288c2ecf20Sopenharmony_ci raw_spin_lock(&gic_data_rdist_cpu(cpu)->rd_lock); 41298c2ecf20Sopenharmony_ci rdbase = per_cpu_ptr(gic_rdists->rdist, cpu)->rd_base; 41308c2ecf20Sopenharmony_ci gic_write_lpir(val, rdbase + GICR_INVALLR); 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ci wait_for_syncr(rdbase); 41338c2ecf20Sopenharmony_ci raw_spin_unlock(&gic_data_rdist_cpu(cpu)->rd_lock); 41348c2ecf20Sopenharmony_ci vpe_to_cpuid_unlock(vpe, flags); 41358c2ecf20Sopenharmony_ci} 41368c2ecf20Sopenharmony_ci 41378c2ecf20Sopenharmony_cistatic int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) 41388c2ecf20Sopenharmony_ci{ 41398c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 41408c2ecf20Sopenharmony_ci struct its_cmd_info *info = vcpu_info; 41418c2ecf20Sopenharmony_ci 41428c2ecf20Sopenharmony_ci switch (info->cmd_type) { 41438c2ecf20Sopenharmony_ci case SCHEDULE_VPE: 41448c2ecf20Sopenharmony_ci its_vpe_4_1_schedule(vpe, info); 41458c2ecf20Sopenharmony_ci return 0; 41468c2ecf20Sopenharmony_ci 41478c2ecf20Sopenharmony_ci case DESCHEDULE_VPE: 41488c2ecf20Sopenharmony_ci its_vpe_4_1_deschedule(vpe, info); 41498c2ecf20Sopenharmony_ci return 0; 41508c2ecf20Sopenharmony_ci 41518c2ecf20Sopenharmony_ci case INVALL_VPE: 41528c2ecf20Sopenharmony_ci its_vpe_4_1_invall(vpe); 41538c2ecf20Sopenharmony_ci return 0; 41548c2ecf20Sopenharmony_ci 41558c2ecf20Sopenharmony_ci default: 41568c2ecf20Sopenharmony_ci return -EINVAL; 41578c2ecf20Sopenharmony_ci } 41588c2ecf20Sopenharmony_ci} 41598c2ecf20Sopenharmony_ci 41608c2ecf20Sopenharmony_cistatic struct irq_chip its_vpe_4_1_irq_chip = { 41618c2ecf20Sopenharmony_ci .name = "GICv4.1-vpe", 41628c2ecf20Sopenharmony_ci .irq_mask = its_vpe_4_1_mask_irq, 41638c2ecf20Sopenharmony_ci .irq_unmask = its_vpe_4_1_unmask_irq, 41648c2ecf20Sopenharmony_ci .irq_eoi = irq_chip_eoi_parent, 41658c2ecf20Sopenharmony_ci .irq_set_affinity = its_vpe_set_affinity, 41668c2ecf20Sopenharmony_ci .irq_set_vcpu_affinity = its_vpe_4_1_set_vcpu_affinity, 41678c2ecf20Sopenharmony_ci}; 41688c2ecf20Sopenharmony_ci 41698c2ecf20Sopenharmony_cistatic void its_configure_sgi(struct irq_data *d, bool clear) 41708c2ecf20Sopenharmony_ci{ 41718c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 41728c2ecf20Sopenharmony_ci struct its_cmd_desc desc; 41738c2ecf20Sopenharmony_ci 41748c2ecf20Sopenharmony_ci desc.its_vsgi_cmd.vpe = vpe; 41758c2ecf20Sopenharmony_ci desc.its_vsgi_cmd.sgi = d->hwirq; 41768c2ecf20Sopenharmony_ci desc.its_vsgi_cmd.priority = vpe->sgi_config[d->hwirq].priority; 41778c2ecf20Sopenharmony_ci desc.its_vsgi_cmd.enable = vpe->sgi_config[d->hwirq].enabled; 41788c2ecf20Sopenharmony_ci desc.its_vsgi_cmd.group = vpe->sgi_config[d->hwirq].group; 41798c2ecf20Sopenharmony_ci desc.its_vsgi_cmd.clear = clear; 41808c2ecf20Sopenharmony_ci 41818c2ecf20Sopenharmony_ci /* 41828c2ecf20Sopenharmony_ci * GICv4.1 allows us to send VSGI commands to any ITS as long as the 41838c2ecf20Sopenharmony_ci * destination VPE is mapped there. Since we map them eagerly at 41848c2ecf20Sopenharmony_ci * activation time, we're pretty sure the first GICv4.1 ITS will do. 41858c2ecf20Sopenharmony_ci */ 41868c2ecf20Sopenharmony_ci its_send_single_vcommand(find_4_1_its(), its_build_vsgi_cmd, &desc); 41878c2ecf20Sopenharmony_ci} 41888c2ecf20Sopenharmony_ci 41898c2ecf20Sopenharmony_cistatic void its_sgi_mask_irq(struct irq_data *d) 41908c2ecf20Sopenharmony_ci{ 41918c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 41928c2ecf20Sopenharmony_ci 41938c2ecf20Sopenharmony_ci vpe->sgi_config[d->hwirq].enabled = false; 41948c2ecf20Sopenharmony_ci its_configure_sgi(d, false); 41958c2ecf20Sopenharmony_ci} 41968c2ecf20Sopenharmony_ci 41978c2ecf20Sopenharmony_cistatic void its_sgi_unmask_irq(struct irq_data *d) 41988c2ecf20Sopenharmony_ci{ 41998c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 42008c2ecf20Sopenharmony_ci 42018c2ecf20Sopenharmony_ci vpe->sgi_config[d->hwirq].enabled = true; 42028c2ecf20Sopenharmony_ci its_configure_sgi(d, false); 42038c2ecf20Sopenharmony_ci} 42048c2ecf20Sopenharmony_ci 42058c2ecf20Sopenharmony_cistatic int its_sgi_set_affinity(struct irq_data *d, 42068c2ecf20Sopenharmony_ci const struct cpumask *mask_val, 42078c2ecf20Sopenharmony_ci bool force) 42088c2ecf20Sopenharmony_ci{ 42098c2ecf20Sopenharmony_ci /* 42108c2ecf20Sopenharmony_ci * There is no notion of affinity for virtual SGIs, at least 42118c2ecf20Sopenharmony_ci * not on the host (since they can only be targeting a vPE). 42128c2ecf20Sopenharmony_ci * Tell the kernel we've done whatever it asked for. 42138c2ecf20Sopenharmony_ci */ 42148c2ecf20Sopenharmony_ci irq_data_update_effective_affinity(d, mask_val); 42158c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK; 42168c2ecf20Sopenharmony_ci} 42178c2ecf20Sopenharmony_ci 42188c2ecf20Sopenharmony_cistatic int its_sgi_set_irqchip_state(struct irq_data *d, 42198c2ecf20Sopenharmony_ci enum irqchip_irq_state which, 42208c2ecf20Sopenharmony_ci bool state) 42218c2ecf20Sopenharmony_ci{ 42228c2ecf20Sopenharmony_ci if (which != IRQCHIP_STATE_PENDING) 42238c2ecf20Sopenharmony_ci return -EINVAL; 42248c2ecf20Sopenharmony_ci 42258c2ecf20Sopenharmony_ci if (state) { 42268c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 42278c2ecf20Sopenharmony_ci struct its_node *its = find_4_1_its(); 42288c2ecf20Sopenharmony_ci u64 val; 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci val = FIELD_PREP(GITS_SGIR_VPEID, vpe->vpe_id); 42318c2ecf20Sopenharmony_ci val |= FIELD_PREP(GITS_SGIR_VINTID, d->hwirq); 42328c2ecf20Sopenharmony_ci writeq_relaxed(val, its->sgir_base + GITS_SGIR - SZ_128K); 42338c2ecf20Sopenharmony_ci } else { 42348c2ecf20Sopenharmony_ci its_configure_sgi(d, true); 42358c2ecf20Sopenharmony_ci } 42368c2ecf20Sopenharmony_ci 42378c2ecf20Sopenharmony_ci return 0; 42388c2ecf20Sopenharmony_ci} 42398c2ecf20Sopenharmony_ci 42408c2ecf20Sopenharmony_cistatic int its_sgi_get_irqchip_state(struct irq_data *d, 42418c2ecf20Sopenharmony_ci enum irqchip_irq_state which, bool *val) 42428c2ecf20Sopenharmony_ci{ 42438c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 42448c2ecf20Sopenharmony_ci void __iomem *base; 42458c2ecf20Sopenharmony_ci unsigned long flags; 42468c2ecf20Sopenharmony_ci u32 count = 1000000; /* 1s! */ 42478c2ecf20Sopenharmony_ci u32 status; 42488c2ecf20Sopenharmony_ci int cpu; 42498c2ecf20Sopenharmony_ci 42508c2ecf20Sopenharmony_ci if (which != IRQCHIP_STATE_PENDING) 42518c2ecf20Sopenharmony_ci return -EINVAL; 42528c2ecf20Sopenharmony_ci 42538c2ecf20Sopenharmony_ci /* 42548c2ecf20Sopenharmony_ci * Locking galore! We can race against two different events: 42558c2ecf20Sopenharmony_ci * 42568c2ecf20Sopenharmony_ci * - Concurrent vPE affinity change: we must make sure it cannot 42578c2ecf20Sopenharmony_ci * happen, or we'll talk to the wrong redistributor. This is 42588c2ecf20Sopenharmony_ci * identical to what happens with vLPIs. 42598c2ecf20Sopenharmony_ci * 42608c2ecf20Sopenharmony_ci * - Concurrent VSGIPENDR access: As it involves accessing two 42618c2ecf20Sopenharmony_ci * MMIO registers, this must be made atomic one way or another. 42628c2ecf20Sopenharmony_ci */ 42638c2ecf20Sopenharmony_ci cpu = vpe_to_cpuid_lock(vpe, &flags); 42648c2ecf20Sopenharmony_ci raw_spin_lock(&gic_data_rdist_cpu(cpu)->rd_lock); 42658c2ecf20Sopenharmony_ci base = gic_data_rdist_cpu(cpu)->rd_base + SZ_128K; 42668c2ecf20Sopenharmony_ci writel_relaxed(vpe->vpe_id, base + GICR_VSGIR); 42678c2ecf20Sopenharmony_ci do { 42688c2ecf20Sopenharmony_ci status = readl_relaxed(base + GICR_VSGIPENDR); 42698c2ecf20Sopenharmony_ci if (!(status & GICR_VSGIPENDR_BUSY)) 42708c2ecf20Sopenharmony_ci goto out; 42718c2ecf20Sopenharmony_ci 42728c2ecf20Sopenharmony_ci count--; 42738c2ecf20Sopenharmony_ci if (!count) { 42748c2ecf20Sopenharmony_ci pr_err_ratelimited("Unable to get SGI status\n"); 42758c2ecf20Sopenharmony_ci goto out; 42768c2ecf20Sopenharmony_ci } 42778c2ecf20Sopenharmony_ci cpu_relax(); 42788c2ecf20Sopenharmony_ci udelay(1); 42798c2ecf20Sopenharmony_ci } while (count); 42808c2ecf20Sopenharmony_ci 42818c2ecf20Sopenharmony_ciout: 42828c2ecf20Sopenharmony_ci raw_spin_unlock(&gic_data_rdist_cpu(cpu)->rd_lock); 42838c2ecf20Sopenharmony_ci vpe_to_cpuid_unlock(vpe, flags); 42848c2ecf20Sopenharmony_ci 42858c2ecf20Sopenharmony_ci if (!count) 42868c2ecf20Sopenharmony_ci return -ENXIO; 42878c2ecf20Sopenharmony_ci 42888c2ecf20Sopenharmony_ci *val = !!(status & (1 << d->hwirq)); 42898c2ecf20Sopenharmony_ci 42908c2ecf20Sopenharmony_ci return 0; 42918c2ecf20Sopenharmony_ci} 42928c2ecf20Sopenharmony_ci 42938c2ecf20Sopenharmony_cistatic int its_sgi_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) 42948c2ecf20Sopenharmony_ci{ 42958c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 42968c2ecf20Sopenharmony_ci struct its_cmd_info *info = vcpu_info; 42978c2ecf20Sopenharmony_ci 42988c2ecf20Sopenharmony_ci switch (info->cmd_type) { 42998c2ecf20Sopenharmony_ci case PROP_UPDATE_VSGI: 43008c2ecf20Sopenharmony_ci vpe->sgi_config[d->hwirq].priority = info->priority; 43018c2ecf20Sopenharmony_ci vpe->sgi_config[d->hwirq].group = info->group; 43028c2ecf20Sopenharmony_ci its_configure_sgi(d, false); 43038c2ecf20Sopenharmony_ci return 0; 43048c2ecf20Sopenharmony_ci 43058c2ecf20Sopenharmony_ci default: 43068c2ecf20Sopenharmony_ci return -EINVAL; 43078c2ecf20Sopenharmony_ci } 43088c2ecf20Sopenharmony_ci} 43098c2ecf20Sopenharmony_ci 43108c2ecf20Sopenharmony_cistatic struct irq_chip its_sgi_irq_chip = { 43118c2ecf20Sopenharmony_ci .name = "GICv4.1-sgi", 43128c2ecf20Sopenharmony_ci .irq_mask = its_sgi_mask_irq, 43138c2ecf20Sopenharmony_ci .irq_unmask = its_sgi_unmask_irq, 43148c2ecf20Sopenharmony_ci .irq_set_affinity = its_sgi_set_affinity, 43158c2ecf20Sopenharmony_ci .irq_set_irqchip_state = its_sgi_set_irqchip_state, 43168c2ecf20Sopenharmony_ci .irq_get_irqchip_state = its_sgi_get_irqchip_state, 43178c2ecf20Sopenharmony_ci .irq_set_vcpu_affinity = its_sgi_set_vcpu_affinity, 43188c2ecf20Sopenharmony_ci}; 43198c2ecf20Sopenharmony_ci 43208c2ecf20Sopenharmony_cistatic int its_sgi_irq_domain_alloc(struct irq_domain *domain, 43218c2ecf20Sopenharmony_ci unsigned int virq, unsigned int nr_irqs, 43228c2ecf20Sopenharmony_ci void *args) 43238c2ecf20Sopenharmony_ci{ 43248c2ecf20Sopenharmony_ci struct its_vpe *vpe = args; 43258c2ecf20Sopenharmony_ci int i; 43268c2ecf20Sopenharmony_ci 43278c2ecf20Sopenharmony_ci /* Yes, we do want 16 SGIs */ 43288c2ecf20Sopenharmony_ci WARN_ON(nr_irqs != 16); 43298c2ecf20Sopenharmony_ci 43308c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 43318c2ecf20Sopenharmony_ci vpe->sgi_config[i].priority = 0; 43328c2ecf20Sopenharmony_ci vpe->sgi_config[i].enabled = false; 43338c2ecf20Sopenharmony_ci vpe->sgi_config[i].group = false; 43348c2ecf20Sopenharmony_ci 43358c2ecf20Sopenharmony_ci irq_domain_set_hwirq_and_chip(domain, virq + i, i, 43368c2ecf20Sopenharmony_ci &its_sgi_irq_chip, vpe); 43378c2ecf20Sopenharmony_ci irq_set_status_flags(virq + i, IRQ_DISABLE_UNLAZY); 43388c2ecf20Sopenharmony_ci } 43398c2ecf20Sopenharmony_ci 43408c2ecf20Sopenharmony_ci return 0; 43418c2ecf20Sopenharmony_ci} 43428c2ecf20Sopenharmony_ci 43438c2ecf20Sopenharmony_cistatic void its_sgi_irq_domain_free(struct irq_domain *domain, 43448c2ecf20Sopenharmony_ci unsigned int virq, 43458c2ecf20Sopenharmony_ci unsigned int nr_irqs) 43468c2ecf20Sopenharmony_ci{ 43478c2ecf20Sopenharmony_ci /* Nothing to do */ 43488c2ecf20Sopenharmony_ci} 43498c2ecf20Sopenharmony_ci 43508c2ecf20Sopenharmony_cistatic int its_sgi_irq_domain_activate(struct irq_domain *domain, 43518c2ecf20Sopenharmony_ci struct irq_data *d, bool reserve) 43528c2ecf20Sopenharmony_ci{ 43538c2ecf20Sopenharmony_ci /* Write out the initial SGI configuration */ 43548c2ecf20Sopenharmony_ci its_configure_sgi(d, false); 43558c2ecf20Sopenharmony_ci return 0; 43568c2ecf20Sopenharmony_ci} 43578c2ecf20Sopenharmony_ci 43588c2ecf20Sopenharmony_cistatic void its_sgi_irq_domain_deactivate(struct irq_domain *domain, 43598c2ecf20Sopenharmony_ci struct irq_data *d) 43608c2ecf20Sopenharmony_ci{ 43618c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 43628c2ecf20Sopenharmony_ci 43638c2ecf20Sopenharmony_ci /* 43648c2ecf20Sopenharmony_ci * The VSGI command is awkward: 43658c2ecf20Sopenharmony_ci * 43668c2ecf20Sopenharmony_ci * - To change the configuration, CLEAR must be set to false, 43678c2ecf20Sopenharmony_ci * leaving the pending bit unchanged. 43688c2ecf20Sopenharmony_ci * - To clear the pending bit, CLEAR must be set to true, leaving 43698c2ecf20Sopenharmony_ci * the configuration unchanged. 43708c2ecf20Sopenharmony_ci * 43718c2ecf20Sopenharmony_ci * You just can't do both at once, hence the two commands below. 43728c2ecf20Sopenharmony_ci */ 43738c2ecf20Sopenharmony_ci vpe->sgi_config[d->hwirq].enabled = false; 43748c2ecf20Sopenharmony_ci its_configure_sgi(d, false); 43758c2ecf20Sopenharmony_ci its_configure_sgi(d, true); 43768c2ecf20Sopenharmony_ci} 43778c2ecf20Sopenharmony_ci 43788c2ecf20Sopenharmony_cistatic const struct irq_domain_ops its_sgi_domain_ops = { 43798c2ecf20Sopenharmony_ci .alloc = its_sgi_irq_domain_alloc, 43808c2ecf20Sopenharmony_ci .free = its_sgi_irq_domain_free, 43818c2ecf20Sopenharmony_ci .activate = its_sgi_irq_domain_activate, 43828c2ecf20Sopenharmony_ci .deactivate = its_sgi_irq_domain_deactivate, 43838c2ecf20Sopenharmony_ci}; 43848c2ecf20Sopenharmony_ci 43858c2ecf20Sopenharmony_cistatic int its_vpe_id_alloc(void) 43868c2ecf20Sopenharmony_ci{ 43878c2ecf20Sopenharmony_ci return ida_simple_get(&its_vpeid_ida, 0, ITS_MAX_VPEID, GFP_KERNEL); 43888c2ecf20Sopenharmony_ci} 43898c2ecf20Sopenharmony_ci 43908c2ecf20Sopenharmony_cistatic void its_vpe_id_free(u16 id) 43918c2ecf20Sopenharmony_ci{ 43928c2ecf20Sopenharmony_ci ida_simple_remove(&its_vpeid_ida, id); 43938c2ecf20Sopenharmony_ci} 43948c2ecf20Sopenharmony_ci 43958c2ecf20Sopenharmony_cistatic int its_vpe_init(struct its_vpe *vpe) 43968c2ecf20Sopenharmony_ci{ 43978c2ecf20Sopenharmony_ci struct page *vpt_page; 43988c2ecf20Sopenharmony_ci int vpe_id; 43998c2ecf20Sopenharmony_ci 44008c2ecf20Sopenharmony_ci /* Allocate vpe_id */ 44018c2ecf20Sopenharmony_ci vpe_id = its_vpe_id_alloc(); 44028c2ecf20Sopenharmony_ci if (vpe_id < 0) 44038c2ecf20Sopenharmony_ci return vpe_id; 44048c2ecf20Sopenharmony_ci 44058c2ecf20Sopenharmony_ci /* Allocate VPT */ 44068c2ecf20Sopenharmony_ci vpt_page = its_allocate_pending_table(GFP_KERNEL); 44078c2ecf20Sopenharmony_ci if (!vpt_page) { 44088c2ecf20Sopenharmony_ci its_vpe_id_free(vpe_id); 44098c2ecf20Sopenharmony_ci return -ENOMEM; 44108c2ecf20Sopenharmony_ci } 44118c2ecf20Sopenharmony_ci 44128c2ecf20Sopenharmony_ci if (!its_alloc_vpe_table(vpe_id)) { 44138c2ecf20Sopenharmony_ci its_vpe_id_free(vpe_id); 44148c2ecf20Sopenharmony_ci its_free_pending_table(vpt_page); 44158c2ecf20Sopenharmony_ci return -ENOMEM; 44168c2ecf20Sopenharmony_ci } 44178c2ecf20Sopenharmony_ci 44188c2ecf20Sopenharmony_ci raw_spin_lock_init(&vpe->vpe_lock); 44198c2ecf20Sopenharmony_ci vpe->vpe_id = vpe_id; 44208c2ecf20Sopenharmony_ci vpe->vpt_page = vpt_page; 44218c2ecf20Sopenharmony_ci if (gic_rdists->has_rvpeid) 44228c2ecf20Sopenharmony_ci atomic_set(&vpe->vmapp_count, 0); 44238c2ecf20Sopenharmony_ci else 44248c2ecf20Sopenharmony_ci vpe->vpe_proxy_event = -1; 44258c2ecf20Sopenharmony_ci 44268c2ecf20Sopenharmony_ci return 0; 44278c2ecf20Sopenharmony_ci} 44288c2ecf20Sopenharmony_ci 44298c2ecf20Sopenharmony_cistatic void its_vpe_teardown(struct its_vpe *vpe) 44308c2ecf20Sopenharmony_ci{ 44318c2ecf20Sopenharmony_ci its_vpe_db_proxy_unmap(vpe); 44328c2ecf20Sopenharmony_ci its_vpe_id_free(vpe->vpe_id); 44338c2ecf20Sopenharmony_ci its_free_pending_table(vpe->vpt_page); 44348c2ecf20Sopenharmony_ci} 44358c2ecf20Sopenharmony_ci 44368c2ecf20Sopenharmony_cistatic void its_vpe_irq_domain_free(struct irq_domain *domain, 44378c2ecf20Sopenharmony_ci unsigned int virq, 44388c2ecf20Sopenharmony_ci unsigned int nr_irqs) 44398c2ecf20Sopenharmony_ci{ 44408c2ecf20Sopenharmony_ci struct its_vm *vm = domain->host_data; 44418c2ecf20Sopenharmony_ci int i; 44428c2ecf20Sopenharmony_ci 44438c2ecf20Sopenharmony_ci irq_domain_free_irqs_parent(domain, virq, nr_irqs); 44448c2ecf20Sopenharmony_ci 44458c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) { 44468c2ecf20Sopenharmony_ci struct irq_data *data = irq_domain_get_irq_data(domain, 44478c2ecf20Sopenharmony_ci virq + i); 44488c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(data); 44498c2ecf20Sopenharmony_ci 44508c2ecf20Sopenharmony_ci BUG_ON(vm != vpe->its_vm); 44518c2ecf20Sopenharmony_ci 44528c2ecf20Sopenharmony_ci clear_bit(data->hwirq, vm->db_bitmap); 44538c2ecf20Sopenharmony_ci its_vpe_teardown(vpe); 44548c2ecf20Sopenharmony_ci irq_domain_reset_irq_data(data); 44558c2ecf20Sopenharmony_ci } 44568c2ecf20Sopenharmony_ci 44578c2ecf20Sopenharmony_ci if (bitmap_empty(vm->db_bitmap, vm->nr_db_lpis)) { 44588c2ecf20Sopenharmony_ci its_lpi_free(vm->db_bitmap, vm->db_lpi_base, vm->nr_db_lpis); 44598c2ecf20Sopenharmony_ci its_free_prop_table(vm->vprop_page); 44608c2ecf20Sopenharmony_ci } 44618c2ecf20Sopenharmony_ci} 44628c2ecf20Sopenharmony_ci 44638c2ecf20Sopenharmony_cistatic int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 44648c2ecf20Sopenharmony_ci unsigned int nr_irqs, void *args) 44658c2ecf20Sopenharmony_ci{ 44668c2ecf20Sopenharmony_ci struct irq_chip *irqchip = &its_vpe_irq_chip; 44678c2ecf20Sopenharmony_ci struct its_vm *vm = args; 44688c2ecf20Sopenharmony_ci unsigned long *bitmap; 44698c2ecf20Sopenharmony_ci struct page *vprop_page; 44708c2ecf20Sopenharmony_ci int base, nr_ids, i, err = 0; 44718c2ecf20Sopenharmony_ci 44728c2ecf20Sopenharmony_ci BUG_ON(!vm); 44738c2ecf20Sopenharmony_ci 44748c2ecf20Sopenharmony_ci bitmap = its_lpi_alloc(roundup_pow_of_two(nr_irqs), &base, &nr_ids); 44758c2ecf20Sopenharmony_ci if (!bitmap) 44768c2ecf20Sopenharmony_ci return -ENOMEM; 44778c2ecf20Sopenharmony_ci 44788c2ecf20Sopenharmony_ci if (nr_ids < nr_irqs) { 44798c2ecf20Sopenharmony_ci its_lpi_free(bitmap, base, nr_ids); 44808c2ecf20Sopenharmony_ci return -ENOMEM; 44818c2ecf20Sopenharmony_ci } 44828c2ecf20Sopenharmony_ci 44838c2ecf20Sopenharmony_ci vprop_page = its_allocate_prop_table(GFP_KERNEL); 44848c2ecf20Sopenharmony_ci if (!vprop_page) { 44858c2ecf20Sopenharmony_ci its_lpi_free(bitmap, base, nr_ids); 44868c2ecf20Sopenharmony_ci return -ENOMEM; 44878c2ecf20Sopenharmony_ci } 44888c2ecf20Sopenharmony_ci 44898c2ecf20Sopenharmony_ci vm->db_bitmap = bitmap; 44908c2ecf20Sopenharmony_ci vm->db_lpi_base = base; 44918c2ecf20Sopenharmony_ci vm->nr_db_lpis = nr_ids; 44928c2ecf20Sopenharmony_ci vm->vprop_page = vprop_page; 44938c2ecf20Sopenharmony_ci 44948c2ecf20Sopenharmony_ci if (gic_rdists->has_rvpeid) 44958c2ecf20Sopenharmony_ci irqchip = &its_vpe_4_1_irq_chip; 44968c2ecf20Sopenharmony_ci 44978c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) { 44988c2ecf20Sopenharmony_ci vm->vpes[i]->vpe_db_lpi = base + i; 44998c2ecf20Sopenharmony_ci err = its_vpe_init(vm->vpes[i]); 45008c2ecf20Sopenharmony_ci if (err) 45018c2ecf20Sopenharmony_ci break; 45028c2ecf20Sopenharmony_ci err = its_irq_gic_domain_alloc(domain, virq + i, 45038c2ecf20Sopenharmony_ci vm->vpes[i]->vpe_db_lpi); 45048c2ecf20Sopenharmony_ci if (err) 45058c2ecf20Sopenharmony_ci break; 45068c2ecf20Sopenharmony_ci irq_domain_set_hwirq_and_chip(domain, virq + i, i, 45078c2ecf20Sopenharmony_ci irqchip, vm->vpes[i]); 45088c2ecf20Sopenharmony_ci set_bit(i, bitmap); 45098c2ecf20Sopenharmony_ci } 45108c2ecf20Sopenharmony_ci 45118c2ecf20Sopenharmony_ci if (err) 45128c2ecf20Sopenharmony_ci its_vpe_irq_domain_free(domain, virq, i); 45138c2ecf20Sopenharmony_ci 45148c2ecf20Sopenharmony_ci return err; 45158c2ecf20Sopenharmony_ci} 45168c2ecf20Sopenharmony_ci 45178c2ecf20Sopenharmony_cistatic int its_vpe_irq_domain_activate(struct irq_domain *domain, 45188c2ecf20Sopenharmony_ci struct irq_data *d, bool reserve) 45198c2ecf20Sopenharmony_ci{ 45208c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 45218c2ecf20Sopenharmony_ci struct its_node *its; 45228c2ecf20Sopenharmony_ci 45238c2ecf20Sopenharmony_ci /* 45248c2ecf20Sopenharmony_ci * If we use the list map, we issue VMAPP on demand... Unless 45258c2ecf20Sopenharmony_ci * we're on a GICv4.1 and we eagerly map the VPE on all ITSs 45268c2ecf20Sopenharmony_ci * so that VSGIs can work. 45278c2ecf20Sopenharmony_ci */ 45288c2ecf20Sopenharmony_ci if (!gic_requires_eager_mapping()) 45298c2ecf20Sopenharmony_ci return 0; 45308c2ecf20Sopenharmony_ci 45318c2ecf20Sopenharmony_ci /* Map the VPE to the first possible CPU */ 45328c2ecf20Sopenharmony_ci vpe->col_idx = cpumask_first(cpu_online_mask); 45338c2ecf20Sopenharmony_ci 45348c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 45358c2ecf20Sopenharmony_ci if (!is_v4(its)) 45368c2ecf20Sopenharmony_ci continue; 45378c2ecf20Sopenharmony_ci 45388c2ecf20Sopenharmony_ci its_send_vmapp(its, vpe, true); 45398c2ecf20Sopenharmony_ci its_send_vinvall(its, vpe); 45408c2ecf20Sopenharmony_ci } 45418c2ecf20Sopenharmony_ci 45428c2ecf20Sopenharmony_ci irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); 45438c2ecf20Sopenharmony_ci 45448c2ecf20Sopenharmony_ci return 0; 45458c2ecf20Sopenharmony_ci} 45468c2ecf20Sopenharmony_ci 45478c2ecf20Sopenharmony_cistatic void its_vpe_irq_domain_deactivate(struct irq_domain *domain, 45488c2ecf20Sopenharmony_ci struct irq_data *d) 45498c2ecf20Sopenharmony_ci{ 45508c2ecf20Sopenharmony_ci struct its_vpe *vpe = irq_data_get_irq_chip_data(d); 45518c2ecf20Sopenharmony_ci struct its_node *its; 45528c2ecf20Sopenharmony_ci 45538c2ecf20Sopenharmony_ci /* 45548c2ecf20Sopenharmony_ci * If we use the list map on GICv4.0, we unmap the VPE once no 45558c2ecf20Sopenharmony_ci * VLPIs are associated with the VM. 45568c2ecf20Sopenharmony_ci */ 45578c2ecf20Sopenharmony_ci if (!gic_requires_eager_mapping()) 45588c2ecf20Sopenharmony_ci return; 45598c2ecf20Sopenharmony_ci 45608c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 45618c2ecf20Sopenharmony_ci if (!is_v4(its)) 45628c2ecf20Sopenharmony_ci continue; 45638c2ecf20Sopenharmony_ci 45648c2ecf20Sopenharmony_ci its_send_vmapp(its, vpe, false); 45658c2ecf20Sopenharmony_ci } 45668c2ecf20Sopenharmony_ci} 45678c2ecf20Sopenharmony_ci 45688c2ecf20Sopenharmony_cistatic const struct irq_domain_ops its_vpe_domain_ops = { 45698c2ecf20Sopenharmony_ci .alloc = its_vpe_irq_domain_alloc, 45708c2ecf20Sopenharmony_ci .free = its_vpe_irq_domain_free, 45718c2ecf20Sopenharmony_ci .activate = its_vpe_irq_domain_activate, 45728c2ecf20Sopenharmony_ci .deactivate = its_vpe_irq_domain_deactivate, 45738c2ecf20Sopenharmony_ci}; 45748c2ecf20Sopenharmony_ci 45758c2ecf20Sopenharmony_cistatic int its_force_quiescent(void __iomem *base) 45768c2ecf20Sopenharmony_ci{ 45778c2ecf20Sopenharmony_ci u32 count = 1000000; /* 1s */ 45788c2ecf20Sopenharmony_ci u32 val; 45798c2ecf20Sopenharmony_ci 45808c2ecf20Sopenharmony_ci val = readl_relaxed(base + GITS_CTLR); 45818c2ecf20Sopenharmony_ci /* 45828c2ecf20Sopenharmony_ci * GIC architecture specification requires the ITS to be both 45838c2ecf20Sopenharmony_ci * disabled and quiescent for writes to GITS_BASER<n> or 45848c2ecf20Sopenharmony_ci * GITS_CBASER to not have UNPREDICTABLE results. 45858c2ecf20Sopenharmony_ci */ 45868c2ecf20Sopenharmony_ci if ((val & GITS_CTLR_QUIESCENT) && !(val & GITS_CTLR_ENABLE)) 45878c2ecf20Sopenharmony_ci return 0; 45888c2ecf20Sopenharmony_ci 45898c2ecf20Sopenharmony_ci /* Disable the generation of all interrupts to this ITS */ 45908c2ecf20Sopenharmony_ci val &= ~(GITS_CTLR_ENABLE | GITS_CTLR_ImDe); 45918c2ecf20Sopenharmony_ci writel_relaxed(val, base + GITS_CTLR); 45928c2ecf20Sopenharmony_ci 45938c2ecf20Sopenharmony_ci /* Poll GITS_CTLR and wait until ITS becomes quiescent */ 45948c2ecf20Sopenharmony_ci while (1) { 45958c2ecf20Sopenharmony_ci val = readl_relaxed(base + GITS_CTLR); 45968c2ecf20Sopenharmony_ci if (val & GITS_CTLR_QUIESCENT) 45978c2ecf20Sopenharmony_ci return 0; 45988c2ecf20Sopenharmony_ci 45998c2ecf20Sopenharmony_ci count--; 46008c2ecf20Sopenharmony_ci if (!count) 46018c2ecf20Sopenharmony_ci return -EBUSY; 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci cpu_relax(); 46048c2ecf20Sopenharmony_ci udelay(1); 46058c2ecf20Sopenharmony_ci } 46068c2ecf20Sopenharmony_ci} 46078c2ecf20Sopenharmony_ci 46088c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_cavium_22375(void *data) 46098c2ecf20Sopenharmony_ci{ 46108c2ecf20Sopenharmony_ci struct its_node *its = data; 46118c2ecf20Sopenharmony_ci 46128c2ecf20Sopenharmony_ci /* erratum 22375: only alloc 8MB table size (20 bits) */ 46138c2ecf20Sopenharmony_ci its->typer &= ~GITS_TYPER_DEVBITS; 46148c2ecf20Sopenharmony_ci its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, 20 - 1); 46158c2ecf20Sopenharmony_ci its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; 46168c2ecf20Sopenharmony_ci 46178c2ecf20Sopenharmony_ci return true; 46188c2ecf20Sopenharmony_ci} 46198c2ecf20Sopenharmony_ci 46208c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_cavium_23144(void *data) 46218c2ecf20Sopenharmony_ci{ 46228c2ecf20Sopenharmony_ci struct its_node *its = data; 46238c2ecf20Sopenharmony_ci 46248c2ecf20Sopenharmony_ci its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_23144; 46258c2ecf20Sopenharmony_ci 46268c2ecf20Sopenharmony_ci return true; 46278c2ecf20Sopenharmony_ci} 46288c2ecf20Sopenharmony_ci 46298c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data) 46308c2ecf20Sopenharmony_ci{ 46318c2ecf20Sopenharmony_ci struct its_node *its = data; 46328c2ecf20Sopenharmony_ci 46338c2ecf20Sopenharmony_ci /* On QDF2400, the size of the ITE is 16Bytes */ 46348c2ecf20Sopenharmony_ci its->typer &= ~GITS_TYPER_ITT_ENTRY_SIZE; 46358c2ecf20Sopenharmony_ci its->typer |= FIELD_PREP(GITS_TYPER_ITT_ENTRY_SIZE, 16 - 1); 46368c2ecf20Sopenharmony_ci 46378c2ecf20Sopenharmony_ci return true; 46388c2ecf20Sopenharmony_ci} 46398c2ecf20Sopenharmony_ci 46408c2ecf20Sopenharmony_cistatic u64 its_irq_get_msi_base_pre_its(struct its_device *its_dev) 46418c2ecf20Sopenharmony_ci{ 46428c2ecf20Sopenharmony_ci struct its_node *its = its_dev->its; 46438c2ecf20Sopenharmony_ci 46448c2ecf20Sopenharmony_ci /* 46458c2ecf20Sopenharmony_ci * The Socionext Synquacer SoC has a so-called 'pre-ITS', 46468c2ecf20Sopenharmony_ci * which maps 32-bit writes targeted at a separate window of 46478c2ecf20Sopenharmony_ci * size '4 << device_id_bits' onto writes to GITS_TRANSLATER 46488c2ecf20Sopenharmony_ci * with device ID taken from bits [device_id_bits + 1:2] of 46498c2ecf20Sopenharmony_ci * the window offset. 46508c2ecf20Sopenharmony_ci */ 46518c2ecf20Sopenharmony_ci return its->pre_its_base + (its_dev->device_id << 2); 46528c2ecf20Sopenharmony_ci} 46538c2ecf20Sopenharmony_ci 46548c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data) 46558c2ecf20Sopenharmony_ci{ 46568c2ecf20Sopenharmony_ci struct its_node *its = data; 46578c2ecf20Sopenharmony_ci u32 pre_its_window[2]; 46588c2ecf20Sopenharmony_ci u32 ids; 46598c2ecf20Sopenharmony_ci 46608c2ecf20Sopenharmony_ci if (!fwnode_property_read_u32_array(its->fwnode_handle, 46618c2ecf20Sopenharmony_ci "socionext,synquacer-pre-its", 46628c2ecf20Sopenharmony_ci pre_its_window, 46638c2ecf20Sopenharmony_ci ARRAY_SIZE(pre_its_window))) { 46648c2ecf20Sopenharmony_ci 46658c2ecf20Sopenharmony_ci its->pre_its_base = pre_its_window[0]; 46668c2ecf20Sopenharmony_ci its->get_msi_base = its_irq_get_msi_base_pre_its; 46678c2ecf20Sopenharmony_ci 46688c2ecf20Sopenharmony_ci ids = ilog2(pre_its_window[1]) - 2; 46698c2ecf20Sopenharmony_ci if (device_ids(its) > ids) { 46708c2ecf20Sopenharmony_ci its->typer &= ~GITS_TYPER_DEVBITS; 46718c2ecf20Sopenharmony_ci its->typer |= FIELD_PREP(GITS_TYPER_DEVBITS, ids - 1); 46728c2ecf20Sopenharmony_ci } 46738c2ecf20Sopenharmony_ci 46748c2ecf20Sopenharmony_ci /* the pre-ITS breaks isolation, so disable MSI remapping */ 46758c2ecf20Sopenharmony_ci its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP; 46768c2ecf20Sopenharmony_ci return true; 46778c2ecf20Sopenharmony_ci } 46788c2ecf20Sopenharmony_ci return false; 46798c2ecf20Sopenharmony_ci} 46808c2ecf20Sopenharmony_ci 46818c2ecf20Sopenharmony_cistatic bool __maybe_unused its_enable_quirk_hip07_161600802(void *data) 46828c2ecf20Sopenharmony_ci{ 46838c2ecf20Sopenharmony_ci struct its_node *its = data; 46848c2ecf20Sopenharmony_ci 46858c2ecf20Sopenharmony_ci /* 46868c2ecf20Sopenharmony_ci * Hip07 insists on using the wrong address for the VLPI 46878c2ecf20Sopenharmony_ci * page. Trick it into doing the right thing... 46888c2ecf20Sopenharmony_ci */ 46898c2ecf20Sopenharmony_ci its->vlpi_redist_offset = SZ_128K; 46908c2ecf20Sopenharmony_ci return true; 46918c2ecf20Sopenharmony_ci} 46928c2ecf20Sopenharmony_ci 46938c2ecf20Sopenharmony_cistatic const struct gic_quirk its_quirks[] = { 46948c2ecf20Sopenharmony_ci#ifdef CONFIG_CAVIUM_ERRATUM_22375 46958c2ecf20Sopenharmony_ci { 46968c2ecf20Sopenharmony_ci .desc = "ITS: Cavium errata 22375, 24313", 46978c2ecf20Sopenharmony_ci .iidr = 0xa100034c, /* ThunderX pass 1.x */ 46988c2ecf20Sopenharmony_ci .mask = 0xffff0fff, 46998c2ecf20Sopenharmony_ci .init = its_enable_quirk_cavium_22375, 47008c2ecf20Sopenharmony_ci }, 47018c2ecf20Sopenharmony_ci#endif 47028c2ecf20Sopenharmony_ci#ifdef CONFIG_CAVIUM_ERRATUM_23144 47038c2ecf20Sopenharmony_ci { 47048c2ecf20Sopenharmony_ci .desc = "ITS: Cavium erratum 23144", 47058c2ecf20Sopenharmony_ci .iidr = 0xa100034c, /* ThunderX pass 1.x */ 47068c2ecf20Sopenharmony_ci .mask = 0xffff0fff, 47078c2ecf20Sopenharmony_ci .init = its_enable_quirk_cavium_23144, 47088c2ecf20Sopenharmony_ci }, 47098c2ecf20Sopenharmony_ci#endif 47108c2ecf20Sopenharmony_ci#ifdef CONFIG_QCOM_QDF2400_ERRATUM_0065 47118c2ecf20Sopenharmony_ci { 47128c2ecf20Sopenharmony_ci .desc = "ITS: QDF2400 erratum 0065", 47138c2ecf20Sopenharmony_ci .iidr = 0x00001070, /* QDF2400 ITS rev 1.x */ 47148c2ecf20Sopenharmony_ci .mask = 0xffffffff, 47158c2ecf20Sopenharmony_ci .init = its_enable_quirk_qdf2400_e0065, 47168c2ecf20Sopenharmony_ci }, 47178c2ecf20Sopenharmony_ci#endif 47188c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCIONEXT_SYNQUACER_PREITS 47198c2ecf20Sopenharmony_ci { 47208c2ecf20Sopenharmony_ci /* 47218c2ecf20Sopenharmony_ci * The Socionext Synquacer SoC incorporates ARM's own GIC-500 47228c2ecf20Sopenharmony_ci * implementation, but with a 'pre-ITS' added that requires 47238c2ecf20Sopenharmony_ci * special handling in software. 47248c2ecf20Sopenharmony_ci */ 47258c2ecf20Sopenharmony_ci .desc = "ITS: Socionext Synquacer pre-ITS", 47268c2ecf20Sopenharmony_ci .iidr = 0x0001143b, 47278c2ecf20Sopenharmony_ci .mask = 0xffffffff, 47288c2ecf20Sopenharmony_ci .init = its_enable_quirk_socionext_synquacer, 47298c2ecf20Sopenharmony_ci }, 47308c2ecf20Sopenharmony_ci#endif 47318c2ecf20Sopenharmony_ci#ifdef CONFIG_HISILICON_ERRATUM_161600802 47328c2ecf20Sopenharmony_ci { 47338c2ecf20Sopenharmony_ci .desc = "ITS: Hip07 erratum 161600802", 47348c2ecf20Sopenharmony_ci .iidr = 0x00000004, 47358c2ecf20Sopenharmony_ci .mask = 0xffffffff, 47368c2ecf20Sopenharmony_ci .init = its_enable_quirk_hip07_161600802, 47378c2ecf20Sopenharmony_ci }, 47388c2ecf20Sopenharmony_ci#endif 47398c2ecf20Sopenharmony_ci { 47408c2ecf20Sopenharmony_ci } 47418c2ecf20Sopenharmony_ci}; 47428c2ecf20Sopenharmony_ci 47438c2ecf20Sopenharmony_cistatic void its_enable_quirks(struct its_node *its) 47448c2ecf20Sopenharmony_ci{ 47458c2ecf20Sopenharmony_ci u32 iidr = readl_relaxed(its->base + GITS_IIDR); 47468c2ecf20Sopenharmony_ci 47478c2ecf20Sopenharmony_ci gic_enable_quirks(iidr, its_quirks, its); 47488c2ecf20Sopenharmony_ci} 47498c2ecf20Sopenharmony_ci 47508c2ecf20Sopenharmony_cistatic int its_save_disable(void) 47518c2ecf20Sopenharmony_ci{ 47528c2ecf20Sopenharmony_ci struct its_node *its; 47538c2ecf20Sopenharmony_ci int err = 0; 47548c2ecf20Sopenharmony_ci 47558c2ecf20Sopenharmony_ci raw_spin_lock(&its_lock); 47568c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 47578c2ecf20Sopenharmony_ci void __iomem *base; 47588c2ecf20Sopenharmony_ci 47598c2ecf20Sopenharmony_ci base = its->base; 47608c2ecf20Sopenharmony_ci its->ctlr_save = readl_relaxed(base + GITS_CTLR); 47618c2ecf20Sopenharmony_ci err = its_force_quiescent(base); 47628c2ecf20Sopenharmony_ci if (err) { 47638c2ecf20Sopenharmony_ci pr_err("ITS@%pa: failed to quiesce: %d\n", 47648c2ecf20Sopenharmony_ci &its->phys_base, err); 47658c2ecf20Sopenharmony_ci writel_relaxed(its->ctlr_save, base + GITS_CTLR); 47668c2ecf20Sopenharmony_ci goto err; 47678c2ecf20Sopenharmony_ci } 47688c2ecf20Sopenharmony_ci 47698c2ecf20Sopenharmony_ci its->cbaser_save = gits_read_cbaser(base + GITS_CBASER); 47708c2ecf20Sopenharmony_ci } 47718c2ecf20Sopenharmony_ci 47728c2ecf20Sopenharmony_cierr: 47738c2ecf20Sopenharmony_ci if (err) { 47748c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(its, &its_nodes, entry) { 47758c2ecf20Sopenharmony_ci void __iomem *base; 47768c2ecf20Sopenharmony_ci 47778c2ecf20Sopenharmony_ci base = its->base; 47788c2ecf20Sopenharmony_ci writel_relaxed(its->ctlr_save, base + GITS_CTLR); 47798c2ecf20Sopenharmony_ci } 47808c2ecf20Sopenharmony_ci } 47818c2ecf20Sopenharmony_ci raw_spin_unlock(&its_lock); 47828c2ecf20Sopenharmony_ci 47838c2ecf20Sopenharmony_ci return err; 47848c2ecf20Sopenharmony_ci} 47858c2ecf20Sopenharmony_ci 47868c2ecf20Sopenharmony_cistatic void its_restore_enable(void) 47878c2ecf20Sopenharmony_ci{ 47888c2ecf20Sopenharmony_ci struct its_node *its; 47898c2ecf20Sopenharmony_ci int ret; 47908c2ecf20Sopenharmony_ci 47918c2ecf20Sopenharmony_ci raw_spin_lock(&its_lock); 47928c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 47938c2ecf20Sopenharmony_ci void __iomem *base; 47948c2ecf20Sopenharmony_ci int i; 47958c2ecf20Sopenharmony_ci 47968c2ecf20Sopenharmony_ci base = its->base; 47978c2ecf20Sopenharmony_ci 47988c2ecf20Sopenharmony_ci /* 47998c2ecf20Sopenharmony_ci * Make sure that the ITS is disabled. If it fails to quiesce, 48008c2ecf20Sopenharmony_ci * don't restore it since writing to CBASER or BASER<n> 48018c2ecf20Sopenharmony_ci * registers is undefined according to the GIC v3 ITS 48028c2ecf20Sopenharmony_ci * Specification. 48038c2ecf20Sopenharmony_ci * 48048c2ecf20Sopenharmony_ci * Firmware resuming with the ITS enabled is terminally broken. 48058c2ecf20Sopenharmony_ci */ 48068c2ecf20Sopenharmony_ci WARN_ON(readl_relaxed(base + GITS_CTLR) & GITS_CTLR_ENABLE); 48078c2ecf20Sopenharmony_ci ret = its_force_quiescent(base); 48088c2ecf20Sopenharmony_ci if (ret) { 48098c2ecf20Sopenharmony_ci pr_err("ITS@%pa: failed to quiesce on resume: %d\n", 48108c2ecf20Sopenharmony_ci &its->phys_base, ret); 48118c2ecf20Sopenharmony_ci continue; 48128c2ecf20Sopenharmony_ci } 48138c2ecf20Sopenharmony_ci 48148c2ecf20Sopenharmony_ci gits_write_cbaser(its->cbaser_save, base + GITS_CBASER); 48158c2ecf20Sopenharmony_ci 48168c2ecf20Sopenharmony_ci /* 48178c2ecf20Sopenharmony_ci * Writing CBASER resets CREADR to 0, so make CWRITER and 48188c2ecf20Sopenharmony_ci * cmd_write line up with it. 48198c2ecf20Sopenharmony_ci */ 48208c2ecf20Sopenharmony_ci its->cmd_write = its->cmd_base; 48218c2ecf20Sopenharmony_ci gits_write_cwriter(0, base + GITS_CWRITER); 48228c2ecf20Sopenharmony_ci 48238c2ecf20Sopenharmony_ci /* Restore GITS_BASER from the value cache. */ 48248c2ecf20Sopenharmony_ci for (i = 0; i < GITS_BASER_NR_REGS; i++) { 48258c2ecf20Sopenharmony_ci struct its_baser *baser = &its->tables[i]; 48268c2ecf20Sopenharmony_ci 48278c2ecf20Sopenharmony_ci if (!(baser->val & GITS_BASER_VALID)) 48288c2ecf20Sopenharmony_ci continue; 48298c2ecf20Sopenharmony_ci 48308c2ecf20Sopenharmony_ci its_write_baser(its, baser, baser->val); 48318c2ecf20Sopenharmony_ci } 48328c2ecf20Sopenharmony_ci writel_relaxed(its->ctlr_save, base + GITS_CTLR); 48338c2ecf20Sopenharmony_ci 48348c2ecf20Sopenharmony_ci /* 48358c2ecf20Sopenharmony_ci * Reinit the collection if it's stored in the ITS. This is 48368c2ecf20Sopenharmony_ci * indicated by the col_id being less than the HCC field. 48378c2ecf20Sopenharmony_ci * CID < HCC as specified in the GIC v3 Documentation. 48388c2ecf20Sopenharmony_ci */ 48398c2ecf20Sopenharmony_ci if (its->collections[smp_processor_id()].col_id < 48408c2ecf20Sopenharmony_ci GITS_TYPER_HCC(gic_read_typer(base + GITS_TYPER))) 48418c2ecf20Sopenharmony_ci its_cpu_init_collection(its); 48428c2ecf20Sopenharmony_ci } 48438c2ecf20Sopenharmony_ci raw_spin_unlock(&its_lock); 48448c2ecf20Sopenharmony_ci} 48458c2ecf20Sopenharmony_ci 48468c2ecf20Sopenharmony_cistatic struct syscore_ops its_syscore_ops = { 48478c2ecf20Sopenharmony_ci .suspend = its_save_disable, 48488c2ecf20Sopenharmony_ci .resume = its_restore_enable, 48498c2ecf20Sopenharmony_ci}; 48508c2ecf20Sopenharmony_ci 48518c2ecf20Sopenharmony_cistatic int its_init_domain(struct fwnode_handle *handle, struct its_node *its) 48528c2ecf20Sopenharmony_ci{ 48538c2ecf20Sopenharmony_ci struct irq_domain *inner_domain; 48548c2ecf20Sopenharmony_ci struct msi_domain_info *info; 48558c2ecf20Sopenharmony_ci 48568c2ecf20Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 48578c2ecf20Sopenharmony_ci if (!info) 48588c2ecf20Sopenharmony_ci return -ENOMEM; 48598c2ecf20Sopenharmony_ci 48608c2ecf20Sopenharmony_ci inner_domain = irq_domain_create_tree(handle, &its_domain_ops, its); 48618c2ecf20Sopenharmony_ci if (!inner_domain) { 48628c2ecf20Sopenharmony_ci kfree(info); 48638c2ecf20Sopenharmony_ci return -ENOMEM; 48648c2ecf20Sopenharmony_ci } 48658c2ecf20Sopenharmony_ci 48668c2ecf20Sopenharmony_ci inner_domain->parent = its_parent; 48678c2ecf20Sopenharmony_ci irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS); 48688c2ecf20Sopenharmony_ci inner_domain->flags |= its->msi_domain_flags; 48698c2ecf20Sopenharmony_ci info->ops = &its_msi_domain_ops; 48708c2ecf20Sopenharmony_ci info->data = its; 48718c2ecf20Sopenharmony_ci inner_domain->host_data = info; 48728c2ecf20Sopenharmony_ci 48738c2ecf20Sopenharmony_ci return 0; 48748c2ecf20Sopenharmony_ci} 48758c2ecf20Sopenharmony_ci 48768c2ecf20Sopenharmony_cistatic int its_init_vpe_domain(void) 48778c2ecf20Sopenharmony_ci{ 48788c2ecf20Sopenharmony_ci struct its_node *its; 48798c2ecf20Sopenharmony_ci u32 devid; 48808c2ecf20Sopenharmony_ci int entries; 48818c2ecf20Sopenharmony_ci 48828c2ecf20Sopenharmony_ci if (gic_rdists->has_direct_lpi) { 48838c2ecf20Sopenharmony_ci pr_info("ITS: Using DirectLPI for VPE invalidation\n"); 48848c2ecf20Sopenharmony_ci return 0; 48858c2ecf20Sopenharmony_ci } 48868c2ecf20Sopenharmony_ci 48878c2ecf20Sopenharmony_ci /* Any ITS will do, even if not v4 */ 48888c2ecf20Sopenharmony_ci its = list_first_entry(&its_nodes, struct its_node, entry); 48898c2ecf20Sopenharmony_ci 48908c2ecf20Sopenharmony_ci entries = roundup_pow_of_two(nr_cpu_ids); 48918c2ecf20Sopenharmony_ci vpe_proxy.vpes = kcalloc(entries, sizeof(*vpe_proxy.vpes), 48928c2ecf20Sopenharmony_ci GFP_KERNEL); 48938c2ecf20Sopenharmony_ci if (!vpe_proxy.vpes) { 48948c2ecf20Sopenharmony_ci pr_err("ITS: Can't allocate GICv4 proxy device array\n"); 48958c2ecf20Sopenharmony_ci return -ENOMEM; 48968c2ecf20Sopenharmony_ci } 48978c2ecf20Sopenharmony_ci 48988c2ecf20Sopenharmony_ci /* Use the last possible DevID */ 48998c2ecf20Sopenharmony_ci devid = GENMASK(device_ids(its) - 1, 0); 49008c2ecf20Sopenharmony_ci vpe_proxy.dev = its_create_device(its, devid, entries, false); 49018c2ecf20Sopenharmony_ci if (!vpe_proxy.dev) { 49028c2ecf20Sopenharmony_ci kfree(vpe_proxy.vpes); 49038c2ecf20Sopenharmony_ci pr_err("ITS: Can't allocate GICv4 proxy device\n"); 49048c2ecf20Sopenharmony_ci return -ENOMEM; 49058c2ecf20Sopenharmony_ci } 49068c2ecf20Sopenharmony_ci 49078c2ecf20Sopenharmony_ci BUG_ON(entries > vpe_proxy.dev->nr_ites); 49088c2ecf20Sopenharmony_ci 49098c2ecf20Sopenharmony_ci raw_spin_lock_init(&vpe_proxy.lock); 49108c2ecf20Sopenharmony_ci vpe_proxy.next_victim = 0; 49118c2ecf20Sopenharmony_ci pr_info("ITS: Allocated DevID %x as GICv4 proxy device (%d slots)\n", 49128c2ecf20Sopenharmony_ci devid, vpe_proxy.dev->nr_ites); 49138c2ecf20Sopenharmony_ci 49148c2ecf20Sopenharmony_ci return 0; 49158c2ecf20Sopenharmony_ci} 49168c2ecf20Sopenharmony_ci 49178c2ecf20Sopenharmony_cistatic int __init its_compute_its_list_map(struct resource *res, 49188c2ecf20Sopenharmony_ci void __iomem *its_base) 49198c2ecf20Sopenharmony_ci{ 49208c2ecf20Sopenharmony_ci int its_number; 49218c2ecf20Sopenharmony_ci u32 ctlr; 49228c2ecf20Sopenharmony_ci 49238c2ecf20Sopenharmony_ci /* 49248c2ecf20Sopenharmony_ci * This is assumed to be done early enough that we're 49258c2ecf20Sopenharmony_ci * guaranteed to be single-threaded, hence no 49268c2ecf20Sopenharmony_ci * locking. Should this change, we should address 49278c2ecf20Sopenharmony_ci * this. 49288c2ecf20Sopenharmony_ci */ 49298c2ecf20Sopenharmony_ci its_number = find_first_zero_bit(&its_list_map, GICv4_ITS_LIST_MAX); 49308c2ecf20Sopenharmony_ci if (its_number >= GICv4_ITS_LIST_MAX) { 49318c2ecf20Sopenharmony_ci pr_err("ITS@%pa: No ITSList entry available!\n", 49328c2ecf20Sopenharmony_ci &res->start); 49338c2ecf20Sopenharmony_ci return -EINVAL; 49348c2ecf20Sopenharmony_ci } 49358c2ecf20Sopenharmony_ci 49368c2ecf20Sopenharmony_ci ctlr = readl_relaxed(its_base + GITS_CTLR); 49378c2ecf20Sopenharmony_ci ctlr &= ~GITS_CTLR_ITS_NUMBER; 49388c2ecf20Sopenharmony_ci ctlr |= its_number << GITS_CTLR_ITS_NUMBER_SHIFT; 49398c2ecf20Sopenharmony_ci writel_relaxed(ctlr, its_base + GITS_CTLR); 49408c2ecf20Sopenharmony_ci ctlr = readl_relaxed(its_base + GITS_CTLR); 49418c2ecf20Sopenharmony_ci if ((ctlr & GITS_CTLR_ITS_NUMBER) != (its_number << GITS_CTLR_ITS_NUMBER_SHIFT)) { 49428c2ecf20Sopenharmony_ci its_number = ctlr & GITS_CTLR_ITS_NUMBER; 49438c2ecf20Sopenharmony_ci its_number >>= GITS_CTLR_ITS_NUMBER_SHIFT; 49448c2ecf20Sopenharmony_ci } 49458c2ecf20Sopenharmony_ci 49468c2ecf20Sopenharmony_ci if (test_and_set_bit(its_number, &its_list_map)) { 49478c2ecf20Sopenharmony_ci pr_err("ITS@%pa: Duplicate ITSList entry %d\n", 49488c2ecf20Sopenharmony_ci &res->start, its_number); 49498c2ecf20Sopenharmony_ci return -EINVAL; 49508c2ecf20Sopenharmony_ci } 49518c2ecf20Sopenharmony_ci 49528c2ecf20Sopenharmony_ci return its_number; 49538c2ecf20Sopenharmony_ci} 49548c2ecf20Sopenharmony_ci 49558c2ecf20Sopenharmony_cistatic int __init its_probe_one(struct resource *res, 49568c2ecf20Sopenharmony_ci struct fwnode_handle *handle, int numa_node) 49578c2ecf20Sopenharmony_ci{ 49588c2ecf20Sopenharmony_ci struct its_node *its; 49598c2ecf20Sopenharmony_ci void __iomem *its_base; 49608c2ecf20Sopenharmony_ci u32 val, ctlr; 49618c2ecf20Sopenharmony_ci u64 baser, tmp, typer; 49628c2ecf20Sopenharmony_ci struct page *page; 49638c2ecf20Sopenharmony_ci int err; 49648c2ecf20Sopenharmony_ci 49658c2ecf20Sopenharmony_ci its_base = ioremap(res->start, SZ_64K); 49668c2ecf20Sopenharmony_ci if (!its_base) { 49678c2ecf20Sopenharmony_ci pr_warn("ITS@%pa: Unable to map ITS registers\n", &res->start); 49688c2ecf20Sopenharmony_ci return -ENOMEM; 49698c2ecf20Sopenharmony_ci } 49708c2ecf20Sopenharmony_ci 49718c2ecf20Sopenharmony_ci val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; 49728c2ecf20Sopenharmony_ci if (val != 0x30 && val != 0x40) { 49738c2ecf20Sopenharmony_ci pr_warn("ITS@%pa: No ITS detected, giving up\n", &res->start); 49748c2ecf20Sopenharmony_ci err = -ENODEV; 49758c2ecf20Sopenharmony_ci goto out_unmap; 49768c2ecf20Sopenharmony_ci } 49778c2ecf20Sopenharmony_ci 49788c2ecf20Sopenharmony_ci err = its_force_quiescent(its_base); 49798c2ecf20Sopenharmony_ci if (err) { 49808c2ecf20Sopenharmony_ci pr_warn("ITS@%pa: Failed to quiesce, giving up\n", &res->start); 49818c2ecf20Sopenharmony_ci goto out_unmap; 49828c2ecf20Sopenharmony_ci } 49838c2ecf20Sopenharmony_ci 49848c2ecf20Sopenharmony_ci pr_info("ITS %pR\n", res); 49858c2ecf20Sopenharmony_ci 49868c2ecf20Sopenharmony_ci its = kzalloc(sizeof(*its), GFP_KERNEL); 49878c2ecf20Sopenharmony_ci if (!its) { 49888c2ecf20Sopenharmony_ci err = -ENOMEM; 49898c2ecf20Sopenharmony_ci goto out_unmap; 49908c2ecf20Sopenharmony_ci } 49918c2ecf20Sopenharmony_ci 49928c2ecf20Sopenharmony_ci raw_spin_lock_init(&its->lock); 49938c2ecf20Sopenharmony_ci mutex_init(&its->dev_alloc_lock); 49948c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&its->entry); 49958c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&its->its_device_list); 49968c2ecf20Sopenharmony_ci typer = gic_read_typer(its_base + GITS_TYPER); 49978c2ecf20Sopenharmony_ci its->typer = typer; 49988c2ecf20Sopenharmony_ci its->base = its_base; 49998c2ecf20Sopenharmony_ci its->phys_base = res->start; 50008c2ecf20Sopenharmony_ci if (is_v4(its)) { 50018c2ecf20Sopenharmony_ci if (!(typer & GITS_TYPER_VMOVP)) { 50028c2ecf20Sopenharmony_ci err = its_compute_its_list_map(res, its_base); 50038c2ecf20Sopenharmony_ci if (err < 0) 50048c2ecf20Sopenharmony_ci goto out_free_its; 50058c2ecf20Sopenharmony_ci 50068c2ecf20Sopenharmony_ci its->list_nr = err; 50078c2ecf20Sopenharmony_ci 50088c2ecf20Sopenharmony_ci pr_info("ITS@%pa: Using ITS number %d\n", 50098c2ecf20Sopenharmony_ci &res->start, err); 50108c2ecf20Sopenharmony_ci } else { 50118c2ecf20Sopenharmony_ci pr_info("ITS@%pa: Single VMOVP capable\n", &res->start); 50128c2ecf20Sopenharmony_ci } 50138c2ecf20Sopenharmony_ci 50148c2ecf20Sopenharmony_ci if (is_v4_1(its)) { 50158c2ecf20Sopenharmony_ci u32 svpet = FIELD_GET(GITS_TYPER_SVPET, typer); 50168c2ecf20Sopenharmony_ci 50178c2ecf20Sopenharmony_ci its->sgir_base = ioremap(res->start + SZ_128K, SZ_64K); 50188c2ecf20Sopenharmony_ci if (!its->sgir_base) { 50198c2ecf20Sopenharmony_ci err = -ENOMEM; 50208c2ecf20Sopenharmony_ci goto out_free_its; 50218c2ecf20Sopenharmony_ci } 50228c2ecf20Sopenharmony_ci 50238c2ecf20Sopenharmony_ci its->mpidr = readl_relaxed(its_base + GITS_MPIDR); 50248c2ecf20Sopenharmony_ci 50258c2ecf20Sopenharmony_ci pr_info("ITS@%pa: Using GICv4.1 mode %08x %08x\n", 50268c2ecf20Sopenharmony_ci &res->start, its->mpidr, svpet); 50278c2ecf20Sopenharmony_ci } 50288c2ecf20Sopenharmony_ci } 50298c2ecf20Sopenharmony_ci 50308c2ecf20Sopenharmony_ci its->numa_node = numa_node; 50318c2ecf20Sopenharmony_ci 50328c2ecf20Sopenharmony_ci page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO, 50338c2ecf20Sopenharmony_ci get_order(ITS_CMD_QUEUE_SZ)); 50348c2ecf20Sopenharmony_ci if (!page) { 50358c2ecf20Sopenharmony_ci err = -ENOMEM; 50368c2ecf20Sopenharmony_ci goto out_unmap_sgir; 50378c2ecf20Sopenharmony_ci } 50388c2ecf20Sopenharmony_ci its->cmd_base = (void *)page_address(page); 50398c2ecf20Sopenharmony_ci its->cmd_write = its->cmd_base; 50408c2ecf20Sopenharmony_ci its->fwnode_handle = handle; 50418c2ecf20Sopenharmony_ci its->get_msi_base = its_irq_get_msi_base; 50428c2ecf20Sopenharmony_ci its->msi_domain_flags = IRQ_DOMAIN_FLAG_MSI_REMAP; 50438c2ecf20Sopenharmony_ci 50448c2ecf20Sopenharmony_ci its_enable_quirks(its); 50458c2ecf20Sopenharmony_ci 50468c2ecf20Sopenharmony_ci err = its_alloc_tables(its); 50478c2ecf20Sopenharmony_ci if (err) 50488c2ecf20Sopenharmony_ci goto out_free_cmd; 50498c2ecf20Sopenharmony_ci 50508c2ecf20Sopenharmony_ci err = its_alloc_collections(its); 50518c2ecf20Sopenharmony_ci if (err) 50528c2ecf20Sopenharmony_ci goto out_free_tables; 50538c2ecf20Sopenharmony_ci 50548c2ecf20Sopenharmony_ci baser = (virt_to_phys(its->cmd_base) | 50558c2ecf20Sopenharmony_ci GITS_CBASER_RaWaWb | 50568c2ecf20Sopenharmony_ci GITS_CBASER_InnerShareable | 50578c2ecf20Sopenharmony_ci (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | 50588c2ecf20Sopenharmony_ci GITS_CBASER_VALID); 50598c2ecf20Sopenharmony_ci 50608c2ecf20Sopenharmony_ci gits_write_cbaser(baser, its->base + GITS_CBASER); 50618c2ecf20Sopenharmony_ci tmp = gits_read_cbaser(its->base + GITS_CBASER); 50628c2ecf20Sopenharmony_ci 50638c2ecf20Sopenharmony_ci if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) { 50648c2ecf20Sopenharmony_ci if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) { 50658c2ecf20Sopenharmony_ci /* 50668c2ecf20Sopenharmony_ci * The HW reports non-shareable, we must 50678c2ecf20Sopenharmony_ci * remove the cacheability attributes as 50688c2ecf20Sopenharmony_ci * well. 50698c2ecf20Sopenharmony_ci */ 50708c2ecf20Sopenharmony_ci baser &= ~(GITS_CBASER_SHAREABILITY_MASK | 50718c2ecf20Sopenharmony_ci GITS_CBASER_CACHEABILITY_MASK); 50728c2ecf20Sopenharmony_ci baser |= GITS_CBASER_nC; 50738c2ecf20Sopenharmony_ci gits_write_cbaser(baser, its->base + GITS_CBASER); 50748c2ecf20Sopenharmony_ci } 50758c2ecf20Sopenharmony_ci pr_info("ITS: using cache flushing for cmd queue\n"); 50768c2ecf20Sopenharmony_ci its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; 50778c2ecf20Sopenharmony_ci } 50788c2ecf20Sopenharmony_ci 50798c2ecf20Sopenharmony_ci gits_write_cwriter(0, its->base + GITS_CWRITER); 50808c2ecf20Sopenharmony_ci ctlr = readl_relaxed(its->base + GITS_CTLR); 50818c2ecf20Sopenharmony_ci ctlr |= GITS_CTLR_ENABLE; 50828c2ecf20Sopenharmony_ci if (is_v4(its)) 50838c2ecf20Sopenharmony_ci ctlr |= GITS_CTLR_ImDe; 50848c2ecf20Sopenharmony_ci writel_relaxed(ctlr, its->base + GITS_CTLR); 50858c2ecf20Sopenharmony_ci 50868c2ecf20Sopenharmony_ci err = its_init_domain(handle, its); 50878c2ecf20Sopenharmony_ci if (err) 50888c2ecf20Sopenharmony_ci goto out_free_tables; 50898c2ecf20Sopenharmony_ci 50908c2ecf20Sopenharmony_ci raw_spin_lock(&its_lock); 50918c2ecf20Sopenharmony_ci list_add(&its->entry, &its_nodes); 50928c2ecf20Sopenharmony_ci raw_spin_unlock(&its_lock); 50938c2ecf20Sopenharmony_ci 50948c2ecf20Sopenharmony_ci return 0; 50958c2ecf20Sopenharmony_ci 50968c2ecf20Sopenharmony_ciout_free_tables: 50978c2ecf20Sopenharmony_ci its_free_tables(its); 50988c2ecf20Sopenharmony_ciout_free_cmd: 50998c2ecf20Sopenharmony_ci free_pages((unsigned long)its->cmd_base, get_order(ITS_CMD_QUEUE_SZ)); 51008c2ecf20Sopenharmony_ciout_unmap_sgir: 51018c2ecf20Sopenharmony_ci if (its->sgir_base) 51028c2ecf20Sopenharmony_ci iounmap(its->sgir_base); 51038c2ecf20Sopenharmony_ciout_free_its: 51048c2ecf20Sopenharmony_ci kfree(its); 51058c2ecf20Sopenharmony_ciout_unmap: 51068c2ecf20Sopenharmony_ci iounmap(its_base); 51078c2ecf20Sopenharmony_ci pr_err("ITS@%pa: failed probing (%d)\n", &res->start, err); 51088c2ecf20Sopenharmony_ci return err; 51098c2ecf20Sopenharmony_ci} 51108c2ecf20Sopenharmony_ci 51118c2ecf20Sopenharmony_cistatic bool gic_rdists_supports_plpis(void) 51128c2ecf20Sopenharmony_ci{ 51138c2ecf20Sopenharmony_ci return !!(gic_read_typer(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS); 51148c2ecf20Sopenharmony_ci} 51158c2ecf20Sopenharmony_ci 51168c2ecf20Sopenharmony_cistatic int redist_disable_lpis(void) 51178c2ecf20Sopenharmony_ci{ 51188c2ecf20Sopenharmony_ci void __iomem *rbase = gic_data_rdist_rd_base(); 51198c2ecf20Sopenharmony_ci u64 timeout = USEC_PER_SEC; 51208c2ecf20Sopenharmony_ci u64 val; 51218c2ecf20Sopenharmony_ci 51228c2ecf20Sopenharmony_ci if (!gic_rdists_supports_plpis()) { 51238c2ecf20Sopenharmony_ci pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); 51248c2ecf20Sopenharmony_ci return -ENXIO; 51258c2ecf20Sopenharmony_ci } 51268c2ecf20Sopenharmony_ci 51278c2ecf20Sopenharmony_ci val = readl_relaxed(rbase + GICR_CTLR); 51288c2ecf20Sopenharmony_ci if (!(val & GICR_CTLR_ENABLE_LPIS)) 51298c2ecf20Sopenharmony_ci return 0; 51308c2ecf20Sopenharmony_ci 51318c2ecf20Sopenharmony_ci /* 51328c2ecf20Sopenharmony_ci * If coming via a CPU hotplug event, we don't need to disable 51338c2ecf20Sopenharmony_ci * LPIs before trying to re-enable them. They are already 51348c2ecf20Sopenharmony_ci * configured and all is well in the world. 51358c2ecf20Sopenharmony_ci * 51368c2ecf20Sopenharmony_ci * If running with preallocated tables, there is nothing to do. 51378c2ecf20Sopenharmony_ci */ 51388c2ecf20Sopenharmony_ci if (gic_data_rdist()->lpi_enabled || 51398c2ecf20Sopenharmony_ci (gic_rdists->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED)) 51408c2ecf20Sopenharmony_ci return 0; 51418c2ecf20Sopenharmony_ci 51428c2ecf20Sopenharmony_ci /* 51438c2ecf20Sopenharmony_ci * From that point on, we only try to do some damage control. 51448c2ecf20Sopenharmony_ci */ 51458c2ecf20Sopenharmony_ci pr_warn("GICv3: CPU%d: Booted with LPIs enabled, memory probably corrupted\n", 51468c2ecf20Sopenharmony_ci smp_processor_id()); 51478c2ecf20Sopenharmony_ci add_taint(TAINT_CRAP, LOCKDEP_STILL_OK); 51488c2ecf20Sopenharmony_ci 51498c2ecf20Sopenharmony_ci /* Disable LPIs */ 51508c2ecf20Sopenharmony_ci val &= ~GICR_CTLR_ENABLE_LPIS; 51518c2ecf20Sopenharmony_ci writel_relaxed(val, rbase + GICR_CTLR); 51528c2ecf20Sopenharmony_ci 51538c2ecf20Sopenharmony_ci /* Make sure any change to GICR_CTLR is observable by the GIC */ 51548c2ecf20Sopenharmony_ci dsb(sy); 51558c2ecf20Sopenharmony_ci 51568c2ecf20Sopenharmony_ci /* 51578c2ecf20Sopenharmony_ci * Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIs 51588c2ecf20Sopenharmony_ci * from 1 to 0 before programming GICR_PEND{PROP}BASER registers. 51598c2ecf20Sopenharmony_ci * Error out if we time out waiting for RWP to clear. 51608c2ecf20Sopenharmony_ci */ 51618c2ecf20Sopenharmony_ci while (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_RWP) { 51628c2ecf20Sopenharmony_ci if (!timeout) { 51638c2ecf20Sopenharmony_ci pr_err("CPU%d: Timeout while disabling LPIs\n", 51648c2ecf20Sopenharmony_ci smp_processor_id()); 51658c2ecf20Sopenharmony_ci return -ETIMEDOUT; 51668c2ecf20Sopenharmony_ci } 51678c2ecf20Sopenharmony_ci udelay(1); 51688c2ecf20Sopenharmony_ci timeout--; 51698c2ecf20Sopenharmony_ci } 51708c2ecf20Sopenharmony_ci 51718c2ecf20Sopenharmony_ci /* 51728c2ecf20Sopenharmony_ci * After it has been written to 1, it is IMPLEMENTATION 51738c2ecf20Sopenharmony_ci * DEFINED whether GICR_CTLR.EnableLPI becomes RES1 or can be 51748c2ecf20Sopenharmony_ci * cleared to 0. Error out if clearing the bit failed. 51758c2ecf20Sopenharmony_ci */ 51768c2ecf20Sopenharmony_ci if (readl_relaxed(rbase + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) { 51778c2ecf20Sopenharmony_ci pr_err("CPU%d: Failed to disable LPIs\n", smp_processor_id()); 51788c2ecf20Sopenharmony_ci return -EBUSY; 51798c2ecf20Sopenharmony_ci } 51808c2ecf20Sopenharmony_ci 51818c2ecf20Sopenharmony_ci return 0; 51828c2ecf20Sopenharmony_ci} 51838c2ecf20Sopenharmony_ci 51848c2ecf20Sopenharmony_ciint its_cpu_init(void) 51858c2ecf20Sopenharmony_ci{ 51868c2ecf20Sopenharmony_ci if (!list_empty(&its_nodes)) { 51878c2ecf20Sopenharmony_ci int ret; 51888c2ecf20Sopenharmony_ci 51898c2ecf20Sopenharmony_ci ret = redist_disable_lpis(); 51908c2ecf20Sopenharmony_ci if (ret) 51918c2ecf20Sopenharmony_ci return ret; 51928c2ecf20Sopenharmony_ci 51938c2ecf20Sopenharmony_ci its_cpu_init_lpis(); 51948c2ecf20Sopenharmony_ci its_cpu_init_collections(); 51958c2ecf20Sopenharmony_ci } 51968c2ecf20Sopenharmony_ci 51978c2ecf20Sopenharmony_ci return 0; 51988c2ecf20Sopenharmony_ci} 51998c2ecf20Sopenharmony_ci 52008c2ecf20Sopenharmony_cistatic const struct of_device_id its_device_id[] = { 52018c2ecf20Sopenharmony_ci { .compatible = "arm,gic-v3-its", }, 52028c2ecf20Sopenharmony_ci {}, 52038c2ecf20Sopenharmony_ci}; 52048c2ecf20Sopenharmony_ci 52058c2ecf20Sopenharmony_cistatic int __init its_of_probe(struct device_node *node) 52068c2ecf20Sopenharmony_ci{ 52078c2ecf20Sopenharmony_ci struct device_node *np; 52088c2ecf20Sopenharmony_ci struct resource res; 52098c2ecf20Sopenharmony_ci 52108c2ecf20Sopenharmony_ci for (np = of_find_matching_node(node, its_device_id); np; 52118c2ecf20Sopenharmony_ci np = of_find_matching_node(np, its_device_id)) { 52128c2ecf20Sopenharmony_ci if (!of_device_is_available(np)) 52138c2ecf20Sopenharmony_ci continue; 52148c2ecf20Sopenharmony_ci if (!of_property_read_bool(np, "msi-controller")) { 52158c2ecf20Sopenharmony_ci pr_warn("%pOF: no msi-controller property, ITS ignored\n", 52168c2ecf20Sopenharmony_ci np); 52178c2ecf20Sopenharmony_ci continue; 52188c2ecf20Sopenharmony_ci } 52198c2ecf20Sopenharmony_ci 52208c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, &res)) { 52218c2ecf20Sopenharmony_ci pr_warn("%pOF: no regs?\n", np); 52228c2ecf20Sopenharmony_ci continue; 52238c2ecf20Sopenharmony_ci } 52248c2ecf20Sopenharmony_ci 52258c2ecf20Sopenharmony_ci its_probe_one(&res, &np->fwnode, of_node_to_nid(np)); 52268c2ecf20Sopenharmony_ci } 52278c2ecf20Sopenharmony_ci return 0; 52288c2ecf20Sopenharmony_ci} 52298c2ecf20Sopenharmony_ci 52308c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 52318c2ecf20Sopenharmony_ci 52328c2ecf20Sopenharmony_ci#define ACPI_GICV3_ITS_MEM_SIZE (SZ_128K) 52338c2ecf20Sopenharmony_ci 52348c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI_NUMA 52358c2ecf20Sopenharmony_cistruct its_srat_map { 52368c2ecf20Sopenharmony_ci /* numa node id */ 52378c2ecf20Sopenharmony_ci u32 numa_node; 52388c2ecf20Sopenharmony_ci /* GIC ITS ID */ 52398c2ecf20Sopenharmony_ci u32 its_id; 52408c2ecf20Sopenharmony_ci}; 52418c2ecf20Sopenharmony_ci 52428c2ecf20Sopenharmony_cistatic struct its_srat_map *its_srat_maps __initdata; 52438c2ecf20Sopenharmony_cistatic int its_in_srat __initdata; 52448c2ecf20Sopenharmony_ci 52458c2ecf20Sopenharmony_cistatic int __init acpi_get_its_numa_node(u32 its_id) 52468c2ecf20Sopenharmony_ci{ 52478c2ecf20Sopenharmony_ci int i; 52488c2ecf20Sopenharmony_ci 52498c2ecf20Sopenharmony_ci for (i = 0; i < its_in_srat; i++) { 52508c2ecf20Sopenharmony_ci if (its_id == its_srat_maps[i].its_id) 52518c2ecf20Sopenharmony_ci return its_srat_maps[i].numa_node; 52528c2ecf20Sopenharmony_ci } 52538c2ecf20Sopenharmony_ci return NUMA_NO_NODE; 52548c2ecf20Sopenharmony_ci} 52558c2ecf20Sopenharmony_ci 52568c2ecf20Sopenharmony_cistatic int __init gic_acpi_match_srat_its(union acpi_subtable_headers *header, 52578c2ecf20Sopenharmony_ci const unsigned long end) 52588c2ecf20Sopenharmony_ci{ 52598c2ecf20Sopenharmony_ci return 0; 52608c2ecf20Sopenharmony_ci} 52618c2ecf20Sopenharmony_ci 52628c2ecf20Sopenharmony_cistatic int __init gic_acpi_parse_srat_its(union acpi_subtable_headers *header, 52638c2ecf20Sopenharmony_ci const unsigned long end) 52648c2ecf20Sopenharmony_ci{ 52658c2ecf20Sopenharmony_ci int node; 52668c2ecf20Sopenharmony_ci struct acpi_srat_gic_its_affinity *its_affinity; 52678c2ecf20Sopenharmony_ci 52688c2ecf20Sopenharmony_ci its_affinity = (struct acpi_srat_gic_its_affinity *)header; 52698c2ecf20Sopenharmony_ci if (!its_affinity) 52708c2ecf20Sopenharmony_ci return -EINVAL; 52718c2ecf20Sopenharmony_ci 52728c2ecf20Sopenharmony_ci if (its_affinity->header.length < sizeof(*its_affinity)) { 52738c2ecf20Sopenharmony_ci pr_err("SRAT: Invalid header length %d in ITS affinity\n", 52748c2ecf20Sopenharmony_ci its_affinity->header.length); 52758c2ecf20Sopenharmony_ci return -EINVAL; 52768c2ecf20Sopenharmony_ci } 52778c2ecf20Sopenharmony_ci 52788c2ecf20Sopenharmony_ci /* 52798c2ecf20Sopenharmony_ci * Note that in theory a new proximity node could be created by this 52808c2ecf20Sopenharmony_ci * entry as it is an SRAT resource allocation structure. 52818c2ecf20Sopenharmony_ci * We do not currently support doing so. 52828c2ecf20Sopenharmony_ci */ 52838c2ecf20Sopenharmony_ci node = pxm_to_node(its_affinity->proximity_domain); 52848c2ecf20Sopenharmony_ci 52858c2ecf20Sopenharmony_ci if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) { 52868c2ecf20Sopenharmony_ci pr_err("SRAT: Invalid NUMA node %d in ITS affinity\n", node); 52878c2ecf20Sopenharmony_ci return 0; 52888c2ecf20Sopenharmony_ci } 52898c2ecf20Sopenharmony_ci 52908c2ecf20Sopenharmony_ci its_srat_maps[its_in_srat].numa_node = node; 52918c2ecf20Sopenharmony_ci its_srat_maps[its_in_srat].its_id = its_affinity->its_id; 52928c2ecf20Sopenharmony_ci its_in_srat++; 52938c2ecf20Sopenharmony_ci pr_info("SRAT: PXM %d -> ITS %d -> Node %d\n", 52948c2ecf20Sopenharmony_ci its_affinity->proximity_domain, its_affinity->its_id, node); 52958c2ecf20Sopenharmony_ci 52968c2ecf20Sopenharmony_ci return 0; 52978c2ecf20Sopenharmony_ci} 52988c2ecf20Sopenharmony_ci 52998c2ecf20Sopenharmony_cistatic void __init acpi_table_parse_srat_its(void) 53008c2ecf20Sopenharmony_ci{ 53018c2ecf20Sopenharmony_ci int count; 53028c2ecf20Sopenharmony_ci 53038c2ecf20Sopenharmony_ci count = acpi_table_parse_entries(ACPI_SIG_SRAT, 53048c2ecf20Sopenharmony_ci sizeof(struct acpi_table_srat), 53058c2ecf20Sopenharmony_ci ACPI_SRAT_TYPE_GIC_ITS_AFFINITY, 53068c2ecf20Sopenharmony_ci gic_acpi_match_srat_its, 0); 53078c2ecf20Sopenharmony_ci if (count <= 0) 53088c2ecf20Sopenharmony_ci return; 53098c2ecf20Sopenharmony_ci 53108c2ecf20Sopenharmony_ci its_srat_maps = kmalloc_array(count, sizeof(struct its_srat_map), 53118c2ecf20Sopenharmony_ci GFP_KERNEL); 53128c2ecf20Sopenharmony_ci if (!its_srat_maps) { 53138c2ecf20Sopenharmony_ci pr_warn("SRAT: Failed to allocate memory for its_srat_maps!\n"); 53148c2ecf20Sopenharmony_ci return; 53158c2ecf20Sopenharmony_ci } 53168c2ecf20Sopenharmony_ci 53178c2ecf20Sopenharmony_ci acpi_table_parse_entries(ACPI_SIG_SRAT, 53188c2ecf20Sopenharmony_ci sizeof(struct acpi_table_srat), 53198c2ecf20Sopenharmony_ci ACPI_SRAT_TYPE_GIC_ITS_AFFINITY, 53208c2ecf20Sopenharmony_ci gic_acpi_parse_srat_its, 0); 53218c2ecf20Sopenharmony_ci} 53228c2ecf20Sopenharmony_ci 53238c2ecf20Sopenharmony_ci/* free the its_srat_maps after ITS probing */ 53248c2ecf20Sopenharmony_cistatic void __init acpi_its_srat_maps_free(void) 53258c2ecf20Sopenharmony_ci{ 53268c2ecf20Sopenharmony_ci kfree(its_srat_maps); 53278c2ecf20Sopenharmony_ci} 53288c2ecf20Sopenharmony_ci#else 53298c2ecf20Sopenharmony_cistatic void __init acpi_table_parse_srat_its(void) { } 53308c2ecf20Sopenharmony_cistatic int __init acpi_get_its_numa_node(u32 its_id) { return NUMA_NO_NODE; } 53318c2ecf20Sopenharmony_cistatic void __init acpi_its_srat_maps_free(void) { } 53328c2ecf20Sopenharmony_ci#endif 53338c2ecf20Sopenharmony_ci 53348c2ecf20Sopenharmony_cistatic int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header, 53358c2ecf20Sopenharmony_ci const unsigned long end) 53368c2ecf20Sopenharmony_ci{ 53378c2ecf20Sopenharmony_ci struct acpi_madt_generic_translator *its_entry; 53388c2ecf20Sopenharmony_ci struct fwnode_handle *dom_handle; 53398c2ecf20Sopenharmony_ci struct resource res; 53408c2ecf20Sopenharmony_ci int err; 53418c2ecf20Sopenharmony_ci 53428c2ecf20Sopenharmony_ci its_entry = (struct acpi_madt_generic_translator *)header; 53438c2ecf20Sopenharmony_ci memset(&res, 0, sizeof(res)); 53448c2ecf20Sopenharmony_ci res.start = its_entry->base_address; 53458c2ecf20Sopenharmony_ci res.end = its_entry->base_address + ACPI_GICV3_ITS_MEM_SIZE - 1; 53468c2ecf20Sopenharmony_ci res.flags = IORESOURCE_MEM; 53478c2ecf20Sopenharmony_ci 53488c2ecf20Sopenharmony_ci dom_handle = irq_domain_alloc_fwnode(&res.start); 53498c2ecf20Sopenharmony_ci if (!dom_handle) { 53508c2ecf20Sopenharmony_ci pr_err("ITS@%pa: Unable to allocate GICv3 ITS domain token\n", 53518c2ecf20Sopenharmony_ci &res.start); 53528c2ecf20Sopenharmony_ci return -ENOMEM; 53538c2ecf20Sopenharmony_ci } 53548c2ecf20Sopenharmony_ci 53558c2ecf20Sopenharmony_ci err = iort_register_domain_token(its_entry->translation_id, res.start, 53568c2ecf20Sopenharmony_ci dom_handle); 53578c2ecf20Sopenharmony_ci if (err) { 53588c2ecf20Sopenharmony_ci pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n", 53598c2ecf20Sopenharmony_ci &res.start, its_entry->translation_id); 53608c2ecf20Sopenharmony_ci goto dom_err; 53618c2ecf20Sopenharmony_ci } 53628c2ecf20Sopenharmony_ci 53638c2ecf20Sopenharmony_ci err = its_probe_one(&res, dom_handle, 53648c2ecf20Sopenharmony_ci acpi_get_its_numa_node(its_entry->translation_id)); 53658c2ecf20Sopenharmony_ci if (!err) 53668c2ecf20Sopenharmony_ci return 0; 53678c2ecf20Sopenharmony_ci 53688c2ecf20Sopenharmony_ci iort_deregister_domain_token(its_entry->translation_id); 53698c2ecf20Sopenharmony_cidom_err: 53708c2ecf20Sopenharmony_ci irq_domain_free_fwnode(dom_handle); 53718c2ecf20Sopenharmony_ci return err; 53728c2ecf20Sopenharmony_ci} 53738c2ecf20Sopenharmony_ci 53748c2ecf20Sopenharmony_cistatic void __init its_acpi_probe(void) 53758c2ecf20Sopenharmony_ci{ 53768c2ecf20Sopenharmony_ci acpi_table_parse_srat_its(); 53778c2ecf20Sopenharmony_ci acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, 53788c2ecf20Sopenharmony_ci gic_acpi_parse_madt_its, 0); 53798c2ecf20Sopenharmony_ci acpi_its_srat_maps_free(); 53808c2ecf20Sopenharmony_ci} 53818c2ecf20Sopenharmony_ci#else 53828c2ecf20Sopenharmony_cistatic void __init its_acpi_probe(void) { } 53838c2ecf20Sopenharmony_ci#endif 53848c2ecf20Sopenharmony_ci 53858c2ecf20Sopenharmony_ciint __init its_init(struct fwnode_handle *handle, struct rdists *rdists, 53868c2ecf20Sopenharmony_ci struct irq_domain *parent_domain) 53878c2ecf20Sopenharmony_ci{ 53888c2ecf20Sopenharmony_ci struct device_node *of_node; 53898c2ecf20Sopenharmony_ci struct its_node *its; 53908c2ecf20Sopenharmony_ci bool has_v4 = false; 53918c2ecf20Sopenharmony_ci bool has_v4_1 = false; 53928c2ecf20Sopenharmony_ci int err; 53938c2ecf20Sopenharmony_ci 53948c2ecf20Sopenharmony_ci gic_rdists = rdists; 53958c2ecf20Sopenharmony_ci 53968c2ecf20Sopenharmony_ci its_parent = parent_domain; 53978c2ecf20Sopenharmony_ci of_node = to_of_node(handle); 53988c2ecf20Sopenharmony_ci if (of_node) 53998c2ecf20Sopenharmony_ci its_of_probe(of_node); 54008c2ecf20Sopenharmony_ci else 54018c2ecf20Sopenharmony_ci its_acpi_probe(); 54028c2ecf20Sopenharmony_ci 54038c2ecf20Sopenharmony_ci if (list_empty(&its_nodes)) { 54048c2ecf20Sopenharmony_ci pr_warn("ITS: No ITS available, not enabling LPIs\n"); 54058c2ecf20Sopenharmony_ci return -ENXIO; 54068c2ecf20Sopenharmony_ci } 54078c2ecf20Sopenharmony_ci 54088c2ecf20Sopenharmony_ci err = allocate_lpi_tables(); 54098c2ecf20Sopenharmony_ci if (err) 54108c2ecf20Sopenharmony_ci return err; 54118c2ecf20Sopenharmony_ci 54128c2ecf20Sopenharmony_ci list_for_each_entry(its, &its_nodes, entry) { 54138c2ecf20Sopenharmony_ci has_v4 |= is_v4(its); 54148c2ecf20Sopenharmony_ci has_v4_1 |= is_v4_1(its); 54158c2ecf20Sopenharmony_ci } 54168c2ecf20Sopenharmony_ci 54178c2ecf20Sopenharmony_ci /* Don't bother with inconsistent systems */ 54188c2ecf20Sopenharmony_ci if (WARN_ON(!has_v4_1 && rdists->has_rvpeid)) 54198c2ecf20Sopenharmony_ci rdists->has_rvpeid = false; 54208c2ecf20Sopenharmony_ci 54218c2ecf20Sopenharmony_ci if (has_v4 & rdists->has_vlpis) { 54228c2ecf20Sopenharmony_ci const struct irq_domain_ops *sgi_ops; 54238c2ecf20Sopenharmony_ci 54248c2ecf20Sopenharmony_ci if (has_v4_1) 54258c2ecf20Sopenharmony_ci sgi_ops = &its_sgi_domain_ops; 54268c2ecf20Sopenharmony_ci else 54278c2ecf20Sopenharmony_ci sgi_ops = NULL; 54288c2ecf20Sopenharmony_ci 54298c2ecf20Sopenharmony_ci if (its_init_vpe_domain() || 54308c2ecf20Sopenharmony_ci its_init_v4(parent_domain, &its_vpe_domain_ops, sgi_ops)) { 54318c2ecf20Sopenharmony_ci rdists->has_vlpis = false; 54328c2ecf20Sopenharmony_ci pr_err("ITS: Disabling GICv4 support\n"); 54338c2ecf20Sopenharmony_ci } 54348c2ecf20Sopenharmony_ci } 54358c2ecf20Sopenharmony_ci 54368c2ecf20Sopenharmony_ci register_syscore_ops(&its_syscore_ops); 54378c2ecf20Sopenharmony_ci 54388c2ecf20Sopenharmony_ci return 0; 54398c2ecf20Sopenharmony_ci} 5440