Skip to content

enhance GIC v3 ITS and enable it on i.MX 95 EVK A55 platform #92346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions boards/nxp/imx95_evk/Kconfig.defconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2024-2025 NXP
# SPDX-License-Identifier: Apache-2.0

if SOC_MIMX9596_A55

# GIC ITS depends on kernel heap which init priority is 30, so set
# GIC to be 31, mailbox and SCMI will be initialized by the order
# according to dts dependency although they use the same init priority.
config INTC_INIT_PRIORITY
default 31

config MBOX_INIT_PRIORITY
default 31

config ARM_SCMI_SHMEM_INIT_PRIORITY
default 31

config ARM_SCMI_TRANSPORT_INIT_PRIORITY
default 31

config CLOCK_CONTROL_INIT_PRIORITY
default 31

endif
2 changes: 1 addition & 1 deletion boards/nxp/imx95_evk/imx95_evk_mimx9596_a55.dts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
};

dram: memory@d0000000 {
reg = <0xd0000000 DT_SIZE_M(1)>;
reg = <0xd0000000 DT_SIZE_M(10)>;
};
};

Expand Down
2 changes: 0 additions & 2 deletions boards/nxp/imx95_evk/imx95_evk_mimx9596_a55_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,3 @@ CONFIG_CLOCK_CONTROL=y

CONFIG_MBOX=y
CONFIG_ARM_SCMI=y
CONFIG_INTC_INIT_PRIORITY=2
CONFIG_MBOX_INIT_PRIORITY=3
9 changes: 9 additions & 0 deletions drivers/interrupt_controller/Kconfig.gic
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,13 @@ config GIC_SAFE_CONFIG
crash the OS has already been started. With this enabled, it will bypass
GIC distributor configuration if it has been configured by other OS.

config GIC_V3_RDIST_DMA_NONCOHERENT
bool "GIC redistributor is DMA noncoherent"
depends on GIC_V3
default n
help
GIC redistributor on Some platform are connected to a non-coherent
downstream interconnect, it need to use Non-cahable and Non-shareable
access atttributes to access external memory.

endif # CPU_CORTEX
40 changes: 36 additions & 4 deletions drivers/interrupt_controller/intc_gicv3.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/*
* Copyright 2020 Broadcom
* Copyright 2024 NXP
* Copyright 2024-2025 NXP
* Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/cache.h>
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
Expand Down Expand Up @@ -111,7 +112,11 @@ static void arm_gic_lpi_setup(unsigned int intid, bool enable)
*cfg &= ~BIT(0);
}

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range(cfg, sizeof(*cfg));
#else
barrier_dsync_fence_full();
#endif

its_rdist_invall();
}
Expand All @@ -123,7 +128,11 @@ static void arm_gic_lpi_set_priority(unsigned int intid, unsigned int prio)
*cfg &= 0xfc;
*cfg |= prio & 0xfc;

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range(cfg, sizeof(*cfg));
#else
barrier_dsync_fence_full();
#endif

its_rdist_invall();
}
Expand Down Expand Up @@ -406,7 +415,7 @@ static void gicv3_rdist_setup_lpis(mem_addr_t rdist)
unsigned int lpi_id_bits =
MIN(GICD_TYPER_IDBITS(sys_read32(GICD_TYPER)), ITS_MAX_LPI_NRBITS);
uintptr_t lpi_pend_table;
uint64_t reg;
uint64_t reg, tmp;
uint32_t ctlr;

/* If not, alloc a common prop table for all redistributors */
Expand All @@ -418,6 +427,11 @@ static void gicv3_rdist_setup_lpis(mem_addr_t rdist)
lpi_pend_table = (uintptr_t)k_aligned_alloc(64 * 1024, LPI_PENDBASE_SZ(lpi_id_bits));
memset((void *)lpi_pend_table, 0, LPI_PENDBASE_SZ(lpi_id_bits));

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range((void *)lpi_prop_table, LPI_PROPBASE_SZ(lpi_id_bits));
arch_dcache_flush_and_invd_range((void *)lpi_pend_table, LPI_PENDBASE_SZ(lpi_id_bits));
#endif

ctlr = sys_read32(rdist + GICR_CTLR);
ctlr &= ~GICR_CTLR_ENABLE_LPIS;
sys_write32(ctlr, rdist + GICR_CTLR);
Expand All @@ -429,15 +443,33 @@ static void gicv3_rdist_setup_lpis(mem_addr_t rdist)
(GIC_BASER_CACHE_INNERLIKE << GITR_PROPBASER_OUTER_CACHE_SHIFT) |
((lpi_id_bits - 1) & GITR_PROPBASER_ID_BITS_MASK);
sys_write64(reg, rdist + GICR_PROPBASER);
/* TOFIX: check SHAREABILITY validity */
/* Check SHAREABILITY validity */
tmp = sys_read64(rdist + GICR_PROPBASER);
#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
tmp &= ~MASK(GITR_PROPBASER_SHAREABILITY);
#endif
if (!(tmp & MASK(GITR_PROPBASER_SHAREABILITY))) {
reg &= ~(MASK(GITR_PROPBASER_SHAREABILITY) | MASK(GITR_PROPBASER_INNER_CACHE));
reg |= GIC_BASER_CACHE_NCACHEABLE << GITR_PROPBASER_INNER_CACHE_SHIFT;
sys_write64(reg, rdist + GICR_PROPBASER);
}

/* PENDBASE */
reg = (GIC_BASER_SHARE_INNER << GITR_PENDBASER_SHAREABILITY_SHIFT) |
(GIC_BASER_CACHE_RAWAWB << GITR_PENDBASER_INNER_CACHE_SHIFT) |
(lpi_pend_table & (GITR_PENDBASER_ADDR_MASK << GITR_PENDBASER_ADDR_SHIFT)) |
(GIC_BASER_CACHE_INNERLIKE << GITR_PENDBASER_OUTER_CACHE_SHIFT) | GITR_PENDBASER_PTZ;
sys_write64(reg, rdist + GICR_PENDBASER);
/* TOFIX: check SHAREABILITY validity */
/* Check SHAREABILITY validity */
tmp = sys_read64(rdist + GICR_PENDBASER);
#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
tmp &= ~MASK(GITR_PENDBASER_SHAREABILITY);
#endif
if (!(tmp & MASK(GITR_PENDBASER_SHAREABILITY))) {
reg &= ~(MASK(GITR_PENDBASER_SHAREABILITY) | MASK(GITR_PENDBASER_INNER_CACHE));
reg |= GIC_BASER_CACHE_NCACHEABLE << GITR_PENDBASER_INNER_CACHE_SHIFT;
sys_write64(reg, rdist + GICR_PENDBASER);
}

ctlr = sys_read32(rdist + GICR_CTLR);
ctlr |= GICR_CTLR_ENABLE_LPIS;
Expand Down
71 changes: 60 additions & 11 deletions drivers/interrupt_controller/intc_gicv3_its.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/*
* Copyright 2021 BayLibre, SAS
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(intc_gicv3_its, LOG_LEVEL_ERR);

#include <zephyr/cache.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/interrupt_controller/gicv3_its.h>
Expand Down Expand Up @@ -39,6 +41,9 @@ struct its_cmd_block {
#define ITS_CMD_QUEUE_SIZE SIZE_64K
#define ITS_CMD_QUEUE_NR_ENTRIES (ITS_CMD_QUEUE_SIZE / sizeof(struct its_cmd_block))

/* The level 1 entry size is a 64bit pointer */
#define GITS_LVL1_ENTRY_SIZE (8UL)

struct gicv3_its_data {
mm_reg_t base;
struct its_cmd_block *cmd_base;
Expand Down Expand Up @@ -190,8 +195,7 @@ static int its_alloc_tables(struct gicv3_its_data *data)
lvl2_width = fls_z(page_size / entry_size) - 1;
device_ids -= lvl2_width + 1;

/* The level 1 entry size is a 64bit pointer */
entry_size = sizeof(uint64_t);
entry_size = GITS_LVL1_ENTRY_SIZE;

indirect = true;
}
Expand Down Expand Up @@ -229,13 +233,22 @@ static int its_alloc_tables(struct gicv3_its_data *data)
}

reg |= MASK_SET(page_cnt - 1, GITS_BASER_SIZE);
reg |= MASK_SET(GIC_BASER_SHARE_INNER, GITS_BASER_SHAREABILITY);
reg |= MASK_SET((uintptr_t)alloc_addr >> GITS_BASER_ADDR_SHIFT, GITS_BASER_ADDR);
reg |= MASK_SET(GIC_BASER_CACHE_INNERLIKE, GITS_BASER_OUTER_CACHE);
#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
reg |= MASK_SET(GIC_BASER_SHARE_NO, GITS_BASER_SHAREABILITY);
reg |= MASK_SET(GIC_BASER_CACHE_NCACHEABLE, GITS_BASER_INNER_CACHE);
#else
reg |= MASK_SET(GIC_BASER_SHARE_INNER, GITS_BASER_SHAREABILITY);
reg |= MASK_SET(GIC_BASER_CACHE_RAWAWB, GITS_BASER_INNER_CACHE);
#endif
reg |= MASK_SET(indirect ? 1 : 0, GITS_BASER_INDIRECT);
reg |= MASK_SET(1, GITS_BASER_VALID);

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range(alloc_addr, page_size * page_cnt);
#endif

sys_write64(reg, data->base + GITS_BASER(i));

/* TOFIX: check page size & SHAREABILITY validity after write */
Expand Down Expand Up @@ -300,6 +313,10 @@ static int its_post_command(struct gicv3_its_data *data, struct its_cmd_block *c
uint64_t wr_idx, rd_idx, idx;
unsigned int count = 1000000; /* 1s! */

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range(cmd, sizeof(*cmd));
#endif

wr_idx = (data->cmd_write - data->cmd_base) * sizeof(*cmd);
rd_idx = sys_read32(data->base + GITS_CREADR);

Expand All @@ -320,7 +337,11 @@ static int its_post_command(struct gicv3_its_data *data, struct its_cmd_block *c
rd_idx, idx, wr_idx);
return -ETIMEDOUT;
}
k_usleep(1);
if (k_is_pre_kernel()) {
k_busy_wait(1);
} else {
k_usleep(1);
}
}

return 0;
Expand All @@ -335,7 +356,7 @@ static int its_send_sync_cmd(struct gicv3_its_data *data, uintptr_t rd_addr)
}

cmd->raw_cmd[0] = MASK_SET(GITS_CMD_ID_SYNC, GITS_CMD_ID);
cmd->raw_cmd[2] = MASK_SET(rd_addr >> GITS_CMD_RDBASE_ALIGN, GITS_CMD_RDBASE);
cmd->raw_cmd[2] = MASK_SET(rd_addr, GITS_CMD_RDBASE);

return its_post_command(data, cmd);
}
Expand All @@ -350,8 +371,7 @@ static int its_send_mapc_cmd(struct gicv3_its_data *data, uint32_t icid,
}

cmd->raw_cmd[0] = MASK_SET(GITS_CMD_ID_MAPC, GITS_CMD_ID);
cmd->raw_cmd[2] = MASK_SET(icid, GITS_CMD_ICID) |
MASK_SET(rd_addr >> GITS_CMD_RDBASE_ALIGN, GITS_CMD_RDBASE) |
cmd->raw_cmd[2] = MASK_SET(icid, GITS_CMD_ICID) | MASK_SET(rd_addr, GITS_CMD_RDBASE) |
MASK_SET(valid ? 1 : 0, GITS_CMD_VALID);

return its_post_command(data, cmd);
Expand Down Expand Up @@ -426,7 +446,6 @@ static int its_send_invall_cmd(struct gicv3_its_data *data, uint32_t icid)
static int gicv3_its_send_int(const struct device *dev, uint32_t device_id, uint32_t event_id)
{
struct gicv3_its_data *data = dev->data;

/* TOFIX check device_id & event_id bounds */

return its_send_int_cmd(data, device_id, event_id);
Expand All @@ -436,7 +455,7 @@ static void its_setup_cmd_queue(const struct device *dev)
{
const struct gicv3_its_config *cfg = dev->config;
struct gicv3_its_data *data = dev->data;
uint64_t reg = 0;
uint64_t reg = 0, tmp;

/* Zero out cmd table */
memset(cfg->cmd_queue, 0, cfg->cmd_queue_size);
Expand All @@ -450,6 +469,17 @@ static void its_setup_cmd_queue(const struct device *dev)

sys_write64(reg, data->base + GITS_CBASER);

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
reg &= ~(MASK(GITS_BASER_SHAREABILITY));
#endif
/* Check whether hardware supports sharable */
tmp = sys_read64(data->base + GITS_CBASER);
if (!(tmp & MASK(GITS_BASER_SHAREABILITY))) {
reg &= ~(MASK(GITS_BASER_SHAREABILITY) | MASK(GITS_BASER_INNER_CACHE));
reg |= MASK_SET(GIC_BASER_CACHE_NCACHEABLE, GITS_CBASER_INNER_CACHE);
sys_write64(reg, data->base + GITS_CBASER);
}

data->cmd_base = (struct its_cmd_block *)cfg->cmd_queue;
data->cmd_write = data->cmd_base;

Expand All @@ -462,12 +492,18 @@ static uintptr_t gicv3_rdist_get_rdbase(const struct device *dev, unsigned int c
{
struct gicv3_its_data *data = dev->data;
uint64_t typer = sys_read64(data->base + GITS_TYPER);
uintptr_t rdbase;

if (GITS_TYPER_PTA_GET(typer)) {
return gic_rdists[cpuid];
rdbase = gic_rdists[cpuid];
/* RDbase must be 64KB aligned, only return bits[51:16] of the address */
rdbase = rdbase >> GITS_CMD_RDBASE_ALIGN;
} else {
return GICR_TYPER_PROCESSOR_NUMBER_GET(sys_read64(gic_rdists[cpuid] + GICR_TYPER));
rdbase =
GICR_TYPER_PROCESSOR_NUMBER_GET(sys_read64(gic_rdists[cpuid] + GICR_TYPER));
}

return rdbase;
}

static int gicv3_its_map_intid(const struct device *dev, uint32_t device_id, uint32_t event_id,
Expand Down Expand Up @@ -529,9 +565,18 @@ static int gicv3_its_init_device_id(const struct device *dev, uint32_t device_id

memset(alloc_addr, 0, data->indirect_dev_page_size);

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range(alloc_addr, data->indirect_dev_page_size);
#endif

data->indirect_dev_lvl1_table[offset] = (uintptr_t)alloc_addr |
MASK_SET(1, GITS_BASER_VALID);

#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range(data->indirect_dev_lvl1_table + offset,
GITS_LVL1_ENTRY_SIZE);
#endif

barrier_dsync_fence_full();
}
}
Expand All @@ -547,6 +592,10 @@ static int gicv3_its_init_device_id(const struct device *dev, uint32_t device_id
if (!itt) {
return -ENOMEM;
}
memset(itt, 0, alloc_size);
#ifdef CONFIG_GIC_V3_RDIST_DMA_NONCOHERENT
arch_dcache_flush_and_invd_range(itt, alloc_size);
#endif

/* size is log2(ites) - 1, equivalent to (fls(ites) - 1) - 1 */
ret = its_send_mapd_cmd(data, device_id, fls_z(nr_ites) - 2, (uintptr_t)itt, true);
Expand Down
8 changes: 8 additions & 0 deletions dts/arm64/nxp/nxp_mimx95_a55.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,15 @@
<0x48060000 0xc0000>; /* GICR (RD_base + SGI_base) */
interrupt-controller;
#interrupt-cells = <4>;
#address-cells = <1>;
#size-cells = <1>;
status = "okay";

its: msi-controller@48040000 {
compatible = "arm,gic-v3-its";
reg = <0x48040000 0x20000>;
status = "okay";
};
};

reserved-memory {
Expand Down
7 changes: 6 additions & 1 deletion soc/nxp/imx/imx9/imx95/Kconfig.defconfig.mimx95.a55
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@ config FLASH_BASE_ADDRESS
config GIC_SAFE_CONFIG
default y

config GIC_V3_RDIST_DMA_NONCOHERENT
default y

# Disable data cache until MMU is enabled when booting from EL2
config ARM64_DCACHE_ALL_OPS
default y
config ARM64_BOOT_DISABLE_DCACHE
default y

# Reserve 8192 LPI interrupt ID starts from 8192 when ITS is enabled
config NUM_IRQS
default 320
default 16384 if GIC_V3_ITS
default 407 if !GIC_V3_ITS

config SYS_CLOCK_HW_CYCLES_PER_SEC
default 24000000
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# The GICv3 & ITS drivers allocation needs are:
# - LPI prop table: global 1x64K aligned on 64K
# - LPI pend table: for each redistributor/cpu 1x64K aligned on 64K
# - Devices table: 128x4K aligned on 4K
# - Interrupt Collections table: 1x4K aligned on 4K
#
# This makes 11x64K to permit all allocations to success.
#
# Note, will need 64K HEAP_MEM per CPUs added.
#
# This doesn't necessarily include the Interrupt Translation Table, which are
# 256bytes aligned tables, for reference a 32 ITEs table needs 256bytes.
#
# To permit allocating 256 ITT tables of 32 ITEs, 13x64K HEAP_MEM is needed
CONFIG_HEAP_MEM_POOL_SIZE=851968
Loading