diff --git a/boards/nxp/imx95_evk/Kconfig.defconfig b/boards/nxp/imx95_evk/Kconfig.defconfig new file mode 100644 index 000000000000..a32ec5b99bbd --- /dev/null +++ b/boards/nxp/imx95_evk/Kconfig.defconfig @@ -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 diff --git a/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55.dts b/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55.dts index d4aff0f100de..4d4866c1a858 100644 --- a/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55.dts +++ b/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55.dts @@ -43,7 +43,7 @@ }; dram: memory@d0000000 { - reg = <0xd0000000 DT_SIZE_M(1)>; + reg = <0xd0000000 DT_SIZE_M(10)>; }; }; diff --git a/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55_defconfig b/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55_defconfig index 23e5dd52c5a4..88a5148a54ed 100644 --- a/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55_defconfig +++ b/boards/nxp/imx95_evk/imx95_evk_mimx9596_a55_defconfig @@ -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 diff --git a/drivers/interrupt_controller/Kconfig.gic b/drivers/interrupt_controller/Kconfig.gic index b482bf3336b1..c3175c74ff2f 100644 --- a/drivers/interrupt_controller/Kconfig.gic +++ b/drivers/interrupt_controller/Kconfig.gic @@ -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 diff --git a/drivers/interrupt_controller/intc_gicv3.c b/drivers/interrupt_controller/intc_gicv3.c index cc22eaedb17f..afc5c6d4377b 100644 --- a/drivers/interrupt_controller/intc_gicv3.c +++ b/drivers/interrupt_controller/intc_gicv3.c @@ -1,11 +1,12 @@ /* * Copyright 2020 Broadcom - * Copyright 2024 NXP + * Copyright 2024-2025 NXP * Copyright 2025 Arm Limited and/or its affiliates * * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -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(); } @@ -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(); } @@ -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 */ @@ -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); @@ -429,7 +443,16 @@ 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) | @@ -437,7 +460,16 @@ static void gicv3_rdist_setup_lpis(mem_addr_t rdist) (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; diff --git a/drivers/interrupt_controller/intc_gicv3_its.c b/drivers/interrupt_controller/intc_gicv3_its.c index 6316e686c755..544158685e1d 100644 --- a/drivers/interrupt_controller/intc_gicv3_its.c +++ b/drivers/interrupt_controller/intc_gicv3_its.c @@ -1,5 +1,6 @@ /* * Copyright 2021 BayLibre, SAS + * Copyright 2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,6 +8,7 @@ #include LOG_MODULE_REGISTER(intc_gicv3_its, LOG_LEVEL_ERR); +#include #include #include #include @@ -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; @@ -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; } @@ -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 */ @@ -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); @@ -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; @@ -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); } @@ -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); @@ -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); @@ -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); @@ -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; @@ -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, @@ -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(); } } @@ -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); diff --git a/dts/arm64/nxp/nxp_mimx95_a55.dtsi b/dts/arm64/nxp/nxp_mimx95_a55.dtsi index 37df069d3dd0..cc52bd1d2b23 100644 --- a/dts/arm64/nxp/nxp_mimx95_a55.dtsi +++ b/dts/arm64/nxp/nxp_mimx95_a55.dtsi @@ -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 { diff --git a/soc/nxp/imx/imx9/imx95/Kconfig.defconfig.mimx95.a55 b/soc/nxp/imx/imx9/imx95/Kconfig.defconfig.mimx95.a55 index 0fc9f23d6888..0d7fef5ac1fd 100644 --- a/soc/nxp/imx/imx9/imx95/Kconfig.defconfig.mimx95.a55 +++ b/soc/nxp/imx/imx9/imx95/Kconfig.defconfig.mimx95.a55 @@ -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 diff --git a/tests/arch/arm64/arm64_gicv3_its/boards/imx95_evk_mimx9596_a55.conf b/tests/arch/arm64/arm64_gicv3_its/boards/imx95_evk_mimx9596_a55.conf new file mode 100644 index 000000000000..70a699068df6 --- /dev/null +++ b/tests/arch/arm64/arm64_gicv3_its/boards/imx95_evk_mimx9596_a55.conf @@ -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 diff --git a/tests/arch/arm64/arm64_gicv3_its/src/main.c b/tests/arch/arm64/arm64_gicv3_its/src/main.c index 42f18067b33f..940e4f504f0d 100644 --- a/tests/arch/arm64/arm64_gicv3_its/src/main.c +++ b/tests/arch/arm64/arm64_gicv3_its/src/main.c @@ -19,12 +19,19 @@ static void lpi_irq_handle(const void *parameter) last_lpi_irq_num = i; } +#ifdef CONFIG_SOC_MIMX9596_A55 +/* DeviceID is 8bits */ +#define ITS_TEST_DEV(id) (id & 0xff) +/* Cover up to 832 LPIs over 26 DevicesIDs and 32 EventIDs per DeviceID */ +#define ITS_TEST_NUM_DEVS 26 +#define ITS_TEST_NUM_ITES 32 +#else /* Generate a DeviceID over the whole 16bits */ #define ITS_TEST_DEV(id) ((((id + 256) % 16) << 12) | (((id + 256) % 24) << 8) | (id & 0xff)) - /* Cover up to 8192 LPIs over 256 DevicesIDs and 32 EventIDs per DeviceID */ #define ITS_TEST_NUM_DEVS 256 #define ITS_TEST_NUM_ITES 32 +#endif /* Do not test all 8192 irqs, iterate with a prime offset to cover most of the possible event_ids */ #define ITS_TEST_NEXT 13