// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2024-2025 ARM Limited, All Rights Reserved.
*/
#define pr_fmt(fmt) "GICv5 ITS: " fmt
#include <linux/bitmap.h>
#include <linux/iommu.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic-v5.h>
#include <linux/irqchip/irq-msi-lib.h>
#include "irq-gic-its-msi-parent.h"
#define ITS_FLAGS_NON_COHERENT BIT(0)
struct gicv5_its_chip_data {
struct xarray its_devices;
struct mutex dev_alloc_lock;
struct fwnode_handle *fwnode;
struct gicv5_its_devtab_cfg devtab_cfgr;
void __iomem *its_base;
u32 flags;
unsigned int msi_domain_flags;
};
struct gicv5_its_dev {
struct gicv5_its_chip_data *its_node;
struct gicv5_its_itt_cfg itt_cfg;
unsigned long *event_map;
u32 device_id;
u32 num_events;
phys_addr_t its_trans_phys_base;
};
static u32 its_readl_relaxed(struct gicv5_its_chip_data *its_node, const u32 reg_offset)
{
return readl_relaxed(its_node->its_base + reg_offset);
}
static void its_writel_relaxed(struct gicv5_its_chip_data *its_node, const u32 val,
const u32 reg_offset)
{
writel_relaxed(val, its_node->its_base + reg_offset);
}
static void its_writeq_relaxed(struct gicv5_its_chip_data *its_node, const u64 val,
const u32 reg_offset)
{
writeq_relaxed(val, its_node->its_base + reg_offset);
}
static void gicv5_its_dcache_clean(struct gicv5_its_chip_data *its, void *start,
size_t sz)
{
void *end = start + sz;
if (its->flags & ITS_FLAGS_NON_COHERENT)
dcache_clean_inval_poc((unsigned long)start, (unsigned long)end);
else
dsb(ishst);
}
static void its_write_table_entry(struct gicv5_its_chip_data *its, __le64 *entry,
u64 val)
{
WRITE_ONCE(*entry, cpu_to_le64(val));
gicv5_its_dcache_clean(its, entry, sizeof(*entry));
}
#define devtab_cfgr_field(its, f) \
FIELD_GET(GICV5_ITS_DT_CFGR_##f, (its)->devtab_cfgr.cfgr)
static int gicv5_its_cache_sync(struct gicv5_its_chip_data *its)
{
return gicv5_wait_for_op_atomic(its->its_base, GICV5_ITS_STATUSR,
GICV5_ITS_STATUSR_IDLE, NULL);
}
static void gicv5_its_syncr(struct gicv5_its_chip_data *its,
struct gicv5_its_dev *its_dev)
{
u64 syncr;
syncr =