diff --git a/components/drivers/pic/Kconfig b/components/drivers/pic/Kconfig index 16c14105a0..d8d077fb76 100755 --- a/components/drivers/pic/Kconfig +++ b/components/drivers/pic/Kconfig @@ -1,6 +1,7 @@ menuconfig RT_USING_PIC bool "Using Programmable Interrupt Controller (PIC)" - select RT_USING_BITMAP + select RT_USING_ADT + select RT_USING_ADT_BITMAP depends on RT_USING_DM default n @@ -22,12 +23,31 @@ config RT_PIC_ARM_GIC select RT_USING_OFW default n +config RT_PIC_ARM_GIC_V2M + bool "ARM GIC V2M" if RT_PIC_ARM_GIC && RT_PCI_MSI + depends on RT_USING_OFW + default n + config RT_PIC_ARM_GIC_V3 bool "ARM GICv3" depends on RT_USING_PIC select RT_USING_OFW default n +config RT_PIC_ARM_GIC_V3_ITS + bool "ARM GICv3 ITS (Interrupt Translation Service)" if RT_PIC_ARM_GIC_V3 && RT_PCI_MSI + depends on RT_USING_OFW + select RT_USING_ADT_REF + default n + +config RT_PIC_ARM_GIC_V3_ITS_IRQ_MAX + int "IRQ maximum used" + depends on RT_PIC_ARM_GIC_V3_ITS + default 127 if ARCH_CPU_64BIT + default 63 + help + Recommended to be based on the bit length (full bits) of maximum usage. + config RT_PIC_ARM_GIC_MAX_NR int depends on RT_USING_PIC diff --git a/components/drivers/pic/SConscript b/components/drivers/pic/SConscript index e820792aa6..53cb66e1ff 100644 --- a/components/drivers/pic/SConscript +++ b/components/drivers/pic/SConscript @@ -16,9 +16,15 @@ if GetDepend(['RT_PIC_ARM_GIC']) or GetDepend(['RT_PIC_ARM_GIC_V3']): if GetDepend(['RT_PIC_ARM_GIC']): src += ['pic-gicv2.c'] +if GetDepend(['RT_PIC_ARM_GIC_V2M']): + src += ['pic-gicv2m.c'] + if GetDepend(['RT_PIC_ARM_GIC_V3']): src += ['pic-gicv3.c'] +if GetDepend(['RT_PIC_ARM_GIC_V3_ITS']): + src += ['pic-gicv3-its.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/pic/pic-gic-common.h b/components/drivers/pic/pic-gic-common.h index 46841e8ca7..d78e0688d6 100644 --- a/components/drivers/pic/pic-gic-common.h +++ b/components/drivers/pic/pic-gic-common.h @@ -8,10 +8,14 @@ * 2023-01-30 GuEe-GUI first version */ -#ifndef __IRQ_GIC_COMMON_H__ -#define __IRQ_GIC_COMMON_H__ +#ifndef __PIC_GIC_COMMON_H__ +#define __PIC_GIC_COMMON_H__ #include + +#ifdef RT_PCI_MSI +#include +#endif #include #define GIC_SGI_NR 16 @@ -52,4 +56,4 @@ rt_err_t gicv2m_ofw_probe(struct rt_ofw_node *ic_np, const struct rt_ofw_node_id rt_err_t gicv3_its_ofw_probe(struct rt_ofw_node *ic_np, const struct rt_ofw_node_id *id); #endif -#endif /* __IRQ_GIC_COMMON_H__ */ +#endif /* __PIC_GIC_COMMON_H__ */ diff --git a/components/drivers/pic/pic-gicv2.c b/components/drivers/pic/pic-gicv2.c index adce046a8d..c92172dd1c 100644 --- a/components/drivers/pic/pic-gicv2.c +++ b/components/drivers/pic/pic-gicv2.c @@ -128,6 +128,8 @@ static void gicv2_cpu_init(struct gicv2 *gic) #ifdef ARCH_SUPPORT_HYP _gicv2_eoi_mode_ns = RT_TRUE; +#else + _gicv2_eoi_mode_ns = !!rt_ofw_bootargs_select("pic.gicv2_eoimode", 0); #endif if (_gicv2_eoi_mode_ns) diff --git a/components/drivers/pic/pic-gicv2m.c b/components/drivers/pic/pic-gicv2m.c new file mode 100755 index 0000000000..fb18a67d1e --- /dev/null +++ b/components/drivers/pic/pic-gicv2m.c @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-07 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.gicv2m" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include "pic-gic-common.h" + +/* +* MSI_TYPER: +* [31:26] Reserved +* [25:16] lowest SPI assigned to MSI +* [15:10] Reserved +* [9:0] Numer of SPIs assigned to MSI +*/ +#define V2M_MSI_TYPER 0x008 +#define V2M_MSI_TYPER_BASE_SHIFT 16 +#define V2M_MSI_TYPER_BASE_MASK 0x3ff +#define V2M_MSI_TYPER_NUM_MASK 0x3ff +#define V2M_MSI_SETSPI_NS 0x040 +#define V2M_MIN_SPI 32 +#define V2M_MAX_SPI 1019 +#define V2M_MSI_IIDR 0xfcc + +#define V2M_MSI_TYPER_BASE_SPI(x) (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK) +#define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK) + +/* APM X-Gene with GICv2m MSI_IIDR register value */ +#define XGENE_GICV2M_MSI_IIDR 0x06000170 +/* Broadcom NS2 GICv2m MSI_IIDR register value */ +#define BCM_NS2_GICV2M_MSI_IIDR 0x0000013f + +/* List of flags for specific v2m implementation */ +#define GICV2M_NEEDS_SPI_OFFSET 0x00000001 +#define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002 + +struct gicv2m +{ + struct rt_pic parent; + + void *base; + void *base_phy; + rt_uint32_t spi_start; /* The SPI number that MSIs start */ + rt_uint32_t spis_nr; /* The number of SPIs for MSIs */ + rt_uint32_t spi_offset; /* Offset to be subtracted from SPI number */ + + rt_bitmap_t *vectors; /* MSI vector bitmap */ + rt_uint32_t flags; /* Flags for v2m's specific implementation */ + + void *gic; + struct rt_spinlock lock; +}; + +#define raw_to_gicv2m(raw) rt_container_of(raw, struct gicv2m, parent) + +static rt_ubase_t gicv2m_get_msi_addr(struct gicv2m *v2m, int hwirq) +{ + rt_ubase_t addr; + + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) + { + addr = (rt_ubase_t)v2m->base_phy | ((hwirq - 32) << 3); + } + else + { + addr = (rt_ubase_t)v2m->base_phy + V2M_MSI_SETSPI_NS; + } + + return addr; +} + +static rt_bool_t is_msi_spi_valid(rt_uint32_t base, rt_uint32_t num) +{ + if (base < V2M_MIN_SPI) + { + LOG_E("Invalid MSI base SPI (base: %u)", base); + + return RT_FALSE; + } + else if ((num == 0) || (base + num > V2M_MAX_SPI)) + { + LOG_E("Number of SPIs (%u) exceed maximum (%u)", num, V2M_MAX_SPI - V2M_MIN_SPI + 1); + + return RT_FALSE; + } + + return RT_TRUE; +} + +static void gicv2m_irq_mask(struct rt_pic_irq *pirq) +{ + rt_pci_msi_mask_irq(pirq); + rt_pic_irq_parent_mask(pirq); +} + +static void gicv2m_irq_unmask(struct rt_pic_irq *pirq) +{ + rt_pci_msi_unmask_irq(pirq); + rt_pic_irq_parent_unmask(pirq); +} + +static void gicv2m_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + rt_ubase_t addr; + struct gicv2m *v2m = raw_to_gicv2m(pirq->pic); + + addr = gicv2m_get_msi_addr(v2m, pirq->hwirq); + + msg->address_hi = rt_upper_32_bits(addr); + msg->address_lo = rt_lower_32_bits(addr); + + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) + { + msg->data = 0; + } + else + { + msg->data = pirq->hwirq; + } + + if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) + { + msg->data -= v2m->spi_offset; + } +} + +static int gicv2m_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) +{ + rt_ubase_t level; + int irq, parent_irq, hwirq, hwirq_index; + struct rt_pic_irq *pirq; + struct gicv2m *v2m = raw_to_gicv2m(pic); + struct rt_pic *ppic = v2m->gic; + + level = rt_spin_lock_irqsave(&v2m->lock); + hwirq_index = rt_bitmap_next_clear_bit(v2m->vectors, 0, v2m->spis_nr); + + if (hwirq_index >= v2m->spis_nr) + { + irq = -RT_EEMPTY; + goto _out_lock; + } + + hwirq = v2m->spi_start + v2m->spi_offset + hwirq_index; + + parent_irq = ppic->ops->irq_map(ppic, hwirq, RT_IRQ_MODE_EDGE_RISING); + if (parent_irq < 0) + { + irq = parent_irq; + goto _out_lock; + } + + irq = rt_pic_config_irq(pic, hwirq_index, hwirq); + if (irq < 0) + { + goto _out_lock; + } + pirq = rt_pic_find_irq(pic, hwirq_index); + + pirq->mode = RT_IRQ_MODE_EDGE_RISING; + rt_pic_cascade(pirq, parent_irq); + + rt_bitmap_set_bit(v2m->vectors, hwirq_index); + +_out_lock: + rt_spin_unlock_irqrestore(&v2m->lock, level); + + return irq; +} + +static void gicv2m_irq_free_msi(struct rt_pic *pic, int irq) +{ + rt_ubase_t level; + struct rt_pic_irq *pirq; + struct gicv2m *v2m = raw_to_gicv2m(pic); + + pirq = rt_pic_find_pirq(pic, irq); + + if (!pirq) + { + return; + } + + rt_pic_uncascade(pirq); + + level = rt_spin_lock_irqsave(&v2m->lock); + rt_bitmap_clear_bit(v2m->vectors, pirq->hwirq - (v2m->spi_start + v2m->spi_offset)); + rt_spin_unlock_irqrestore(&v2m->lock, level); +} + +static rt_err_t gicv2m_irq_set_state(struct rt_pic *pic, int hwirq, int type, rt_bool_t state) +{ + struct gicv2m *v2m = raw_to_gicv2m(pic); + struct rt_pic *ppic = v2m->gic; + + return ppic->ops->irq_set_state(ppic, hwirq, type, state); +} + +static rt_err_t gicv2m_irq_get_state(struct rt_pic *pic, int hwirq, int type, rt_bool_t *out_state) +{ + struct gicv2m *v2m = raw_to_gicv2m(pic); + struct rt_pic *ppic = v2m->gic; + + return ppic->ops->irq_get_state(ppic, hwirq, type, out_state); +} + +const static struct rt_pic_ops gicv2m_ops = +{ + .name = "GICv2m", + .irq_ack = rt_pic_irq_parent_ack, + .irq_mask = gicv2m_irq_mask, + .irq_unmask = gicv2m_irq_unmask, + .irq_eoi = rt_pic_irq_parent_eoi, + .irq_set_priority = rt_pic_irq_parent_set_priority, + .irq_set_affinity = rt_pic_irq_parent_set_affinity, + .irq_compose_msi_msg = gicv2m_compose_msi_msg, + .irq_alloc_msi = gicv2m_irq_alloc_msi, + .irq_free_msi = gicv2m_irq_free_msi, + .irq_set_state = gicv2m_irq_set_state, + .irq_get_state = gicv2m_irq_get_state, + .flags = RT_PIC_F_IRQ_ROUTING, +}; + +static const struct rt_ofw_node_id gicv2m_ofw_match[] = +{ + { .compatible = "arm,gic-v2m-frame" }, + { /* sentinel */ } +}; + +rt_err_t gicv2m_ofw_probe(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err = RT_EOK; + struct rt_ofw_node *v2m_np; + + rt_ofw_foreach_available_child_node(np, v2m_np) + { + struct gicv2m *v2m; + rt_size_t bitmap_size; + rt_uint32_t spi_start = 0, spis_nr = 0; + + if (!rt_ofw_node_match(v2m_np, gicv2m_ofw_match)) + { + continue; + } + + if (!rt_ofw_prop_read_bool(v2m_np, "msi-controller")) + { + continue; + } + + if (!(v2m = rt_malloc(sizeof(*v2m)))) + { + rt_ofw_node_put(v2m_np); + + err = -RT_ENOMEM; + break; + } + + v2m->base = rt_ofw_iomap(v2m_np, 0); + + if (!v2m->base) + { + LOG_E("%s: IO map failed", rt_ofw_node_full_name(v2m_np)); + continue; + } + + v2m->base_phy = rt_kmem_v2p(v2m->base); + v2m->flags = 0; + + if (!rt_ofw_prop_read_u32(v2m_np, "arm,msi-base-spi", &spi_start) && + !rt_ofw_prop_read_u32(v2m_np, "arm,msi-num-spis", &spis_nr)) + { + LOG_I("DT overriding V2M MSI_TYPER (base:%u, num:%u)", spi_start, spis_nr); + } + + if (spi_start && spis_nr) + { + v2m->spi_start = spi_start; + v2m->spis_nr = spis_nr; + } + else + { + rt_uint32_t typer; + + /* Graviton should always have explicit spi_start/spis_nr */ + if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) + { + goto _fail; + } + typer = HWREG32(v2m->base + V2M_MSI_TYPER); + + v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); + v2m->spis_nr = V2M_MSI_TYPER_NUM_SPI(typer); + } + + if (!is_msi_spi_valid(v2m->spi_start, v2m->spis_nr)) + { + goto _fail; + } + + /* + * APM X-Gene GICv2m implementation has an erratum where + * the MSI data needs to be the offset from the spi_start + * in order to trigger the correct MSI interrupt. This is + * different from the standard GICv2m implementation where + * the MSI data is the absolute value within the range from + * spi_start to (spi_start + num_spis). + * + * Broadcom NS2 GICv2m implementation has an erratum where the MSI data + * is 'spi_number - 32' + * + * Reading that register fails on the Graviton implementation + */ + if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) + { + switch (HWREG32(v2m->base + V2M_MSI_IIDR)) + { + case XGENE_GICV2M_MSI_IIDR: + v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = v2m->spi_start; + break; + + case BCM_NS2_GICV2M_MSI_IIDR: + v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; + v2m->spi_offset = 32; + break; + } + } + + bitmap_size = RT_BITMAP_LEN(v2m->spis_nr) * sizeof(bitmap_t); + + if (!(v2m->vectors = rt_calloc(1, bitmap_size))) + { + err = -RT_ENOMEM; + goto _fail; + } + + rt_spin_lock_init(&v2m->lock); + + v2m->parent.priv_data = v2m; + v2m->parent.ops = &gicv2m_ops; + v2m->gic = rt_ofw_data(np); + + rt_pic_linear_irq(&v2m->parent, v2m->spis_nr); + rt_pic_user_extends(&v2m->parent); + + rt_ofw_data(v2m_np) = &v2m->parent; + rt_ofw_node_set_flag(v2m_np, RT_OFW_F_READLY); + + continue; + + _fail: + rt_iounmap(v2m->base); + rt_free(v2m); + + if (err) + { + rt_ofw_node_put(v2m_np); + break; + } + } + + return err; +} diff --git a/components/drivers/pic/pic-gicv3-its.c b/components/drivers/pic/pic-gicv3-its.c new file mode 100755 index 0000000000..f0772d9171 --- /dev/null +++ b/components/drivers/pic/pic-gicv3-its.c @@ -0,0 +1,1584 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-30 GuEe-GUI first version + */ + +#include +#include +#include + +#define DBG_TAG "pic.gicv3-its" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include +#include +#include "pic-gicv3.h" +#include "pic-gic-common.h" + +#define ITS_CMD_QUEUE_SIZE (64 * SIZE_KB) +#define ITS_CMD_QUEUE_ALIGN (64 * SIZE_KB) +#define ITS_CMD_QUEUE_NR (ITS_CMD_QUEUE_SIZE / sizeof(struct its_command)) + +#define ITS_ITT_ALIGN (256 * SIZE_KB) + +#define ITS_LPI_CONFIG_TABLE_ALIGN (64 * SIZE_KB) +#define ITS_LPI_CONFIG_PROP_DEFAULT_PRIO GICD_INT_DEF_PRI +#define ITS_LPI_CONFIG_PROP_SHIFT 2 +#define ITS_LPI_CONFIG_PROP_MASK RT_GENMASK(7, ITS_LPI_CONFIG_PROP_SHIFT) +#define ITS_LPI_PENDING_TABLE_ALIGN (64 * SIZE_KB) + +#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING RT_BIT(0) +#define RDIST_FLAGS_RD_TABLES_PREALLOCATED RT_BIT(1) +#define RDIST_FLAGS_FORCE_NON_SHAREABLE RT_BIT(2) + +#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING RT_BIT(0) +#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 RT_BIT(1) +#define ITS_FLAGS_FORCE_NON_SHAREABLE RT_BIT(2) + +#define RD_LOCAL_LPI_ENABLED RT_BIT(0) +#define RD_LOCAL_PENDTABLE_PREALLOCATED RT_BIT(1) +#define RD_LOCAL_MEMRESERVE_DONE RT_BIT(2) + +struct its_command +{ + union + { + rt_le64_t code_raw[4]; + rt_uint64_t code[4]; + }; +}; + +struct its_table +{ + void *base; + rt_uint64_t val; + rt_uint32_t size_bits; + rt_uint32_t page_size; + union + { + struct + { + rt_uint32_t itt_entries; + rt_uint32_t lvl2_bits; + }; + }; +}; + +struct its_collection +{ + rt_uint64_t target_address; + rt_uint16_t id; +}; + +struct gicv3_its; + +struct its_map +{ + rt_list_t list; + struct rt_ref ref; + struct gicv3_its *its; + + int device_id; + int lpi_base; + int cpu_id; + + void *itt; + void *lvl2_dte; +}; + +struct gicv3_its +{ + struct rt_pic parent; + rt_list_t list; + + void *base; + void *base_phy; + + void *cmd_base; + rt_ubase_t cmd_idx; + rt_uint32_t flags; + struct rt_spinlock cmd_lock; + + struct its_table tbls[GITS_BASER_NR_REGS]; + struct its_collection collections[RT_CPUS_NR]; + + struct gicv3 *gic; + struct rt_ofw_node *np; +}; + +#define raw_to_gicv3_its(raw) rt_container_of(raw, struct gicv3_its, parent) + +static rt_size_t lpi_nr; +static rt_uint32_t lpi_id_bits; +static void *lpi_table; +static void *lpi_pending_table; +static rt_bitmap_t *lpis_vectors = RT_NULL; +static struct rt_spinlock lpis_lock = {}, map_lock = {}; +static rt_list_t its_nodes = RT_LIST_OBJECT_INIT(its_nodes); +static rt_list_t map_nodes = RT_LIST_OBJECT_INIT(map_nodes); + +rt_inline rt_uint64_t its_readq(struct gicv3_its *its, int off) +{ + return HWREG32(its->base + off) | + (rt_uint64_t)HWREG32(its->base + off + 4) << 32; +} + +rt_inline void its_writeq(struct gicv3_its *its, int off, rt_uint64_t value) +{ + HWREG32(its->base + off) = (rt_uint32_t)value; + HWREG32(its->base + off + 4) = (rt_uint32_t)(value >> 32); +} + +rt_inline rt_uint32_t its_readl(struct gicv3_its *its, int off) +{ + return HWREG32(its->base + off); +} + +rt_inline void its_writel(struct gicv3_its *its, int off, rt_uint32_t value) +{ + HWREG32(its->base + off) = value; +} + +rt_inline rt_uint32_t its_pirq_event_id(struct gicv3_its *its, struct rt_pic_irq *pirq) +{ + return pirq->hwirq - 8192; +} + +rt_inline rt_uint32_t its_pirq_device_id(struct gicv3_its *its, struct rt_pic_irq *pirq) +{ + struct its_map *map = pirq->msi_desc->priv; + + return map->device_id; +} + +rt_inline rt_size_t its_device_id_bits(struct gicv3_its *its) +{ + return RT_FIELD_GET(GITS_TYPER_DEVBITS, HWREG64(its->base + GITS_TYPER)) + 1; +} + +rt_inline void *lpi_base_config(int index) +{ + return &((rt_uint8_t *)lpi_table)[index - 8192]; +} + +static void its_mask_encode(rt_uint64_t *raw_code, rt_uint64_t val, int h, int l) +{ + rt_uint64_t mask = RT_GENMASK_ULL(h, l); + *raw_code &= ~mask; + *raw_code |= (val << l) & mask; +} + +rt_inline void its_encode_cmd(struct its_command *cmd, rt_uint8_t cmd_nr) +{ + its_mask_encode(&cmd->code[0], cmd_nr, 7, 0); +} + +rt_inline void its_encode_valid(struct its_command *cmd, rt_bool_t valid) +{ + its_mask_encode(&cmd->code[2], !!valid, 63, 63); +} + +rt_inline void its_encode_phys_id(struct its_command *cmd, rt_uint32_t phys_id) +{ + its_mask_encode(&cmd->code[1], phys_id, 63, 32); +} + +rt_inline void its_encode_size(struct its_command *cmd, rt_uint8_t size) +{ + its_mask_encode(&cmd->code[1], size, 4, 0); +} + +rt_inline void its_encode_itt(struct its_command *cmd, rt_uint64_t itt_addr) +{ + its_mask_encode(&cmd->code[2], itt_addr >> 8, 51, 8); +} + +rt_inline void its_encode_target(struct its_command *cmd, rt_uint64_t target_addr) +{ + its_mask_encode(&cmd->code[2], target_addr >> 16, 51, 16); +} + +rt_inline void its_encode_device_id(struct its_command *cmd, rt_uint32_t device_id) +{ + its_mask_encode(&cmd->code[0], device_id, 63, 32); +} + +rt_inline void its_encode_event_id(struct its_command *cmd, rt_uint32_t event_id) +{ + its_mask_encode(&cmd->code[1], event_id, 31, 0); +} + +rt_inline void its_encode_collection(struct its_command *cmd, rt_uint16_t collection_id) +{ + its_mask_encode(&cmd->code[2], collection_id, 15, 0); +} + +static struct its_table *its_baser_type(struct gicv3_its *its, int type) +{ + for (int i = 0; i < RT_ARRAY_SIZE(its->tbls); ++i) + { + if (GITS_BASER_TYPE(its->tbls[i].val) == type) + { + return &its->tbls[i]; + } + } + + return RT_NULL; +} + +static struct its_command *its_cmd_alloc(struct gicv3_its *its) +{ + struct its_command *cmd = RT_NULL; + + for (rt_uint32_t count = 0; count <= 10000; ++count) + { + if ((its->cmd_idx + 1) % ITS_CMD_QUEUE_NR != its_readl(its, GITS_CREADR) / sizeof(*cmd)) + { + struct its_command *cmds = its->cmd_base; + + cmd = &cmds[its->cmd_idx++]; + its->cmd_idx %= ITS_CMD_QUEUE_NR; + + rt_memset(cmd, 0, sizeof(*cmd)); + + break; + } + + rt_hw_us_delay(10); + } + + return cmd; +} + +static rt_err_t its_cmd_submit_raw(struct gicv3_its *its, struct its_command *cmd) +{ + rt_uint64_t cwriter; + rt_bool_t retry = RT_FALSE; + + cwriter = (void *)(cmd + 1) - its->cmd_base; + rt_hw_rmb(); + +#ifdef ARCH_CPU_BIG_ENDIAN + cmd->code_raw[0] = rt_cpu_to_le64(cmd->code[0]); + cmd->code_raw[1] = rt_cpu_to_le64(cmd->code[1]); + cmd->code_raw[2] = rt_cpu_to_le64(cmd->code[2]); + cmd->code_raw[3] = rt_cpu_to_le64(cmd->code[3]); +#endif /* ARCH_CPU_BIG_ENDIAN */ + + /* Make sure the commands written to memory are observable by the ITS */ + if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, cmd, sizeof(*cmd)); + } + else + { + rt_hw_wmb(); + } + + its_writel(its, GITS_CWRITER, cwriter); + + for (rt_uint32_t count = 0; count < 10000; ++count) + { + if (its_readl(its, GITS_CREADR) == cwriter) + { + return RT_EOK; + } + + /* Stalled */ + if (!retry && its_readl(its, GITS_CREADR) & 1) + { + /* Retry */ + its_writel(its, GITS_CWRITER, cwriter); + retry = RT_TRUE; + } + else if (retry) + { + LOG_E("Retry command 0x%02x fail", cmd->code[0] & 0xff); + + return -RT_EIO; + } + + rt_hw_us_delay(10); + } + + return -RT_ETIMEOUT; +} + +static rt_err_t its_cmd_submit_nomap(struct gicv3_its *its, struct its_command *cmd, + int cpu_id, rt_bool_t sync) +{ + rt_err_t err; + struct its_command *hw_cmd; + + rt_hw_spin_lock(&its->cmd_lock.lock); + + if (!(hw_cmd = its_cmd_alloc(its))) + { + err = -RT_EBUSY; + goto _out_lock; + } + + rt_memcpy(hw_cmd, cmd, sizeof(*hw_cmd)); + + if ((err = its_cmd_submit_raw(its, hw_cmd))) + { + goto _out_lock; + } + + if (sync) + { + if (!(hw_cmd = its_cmd_alloc(its))) + { + err = -RT_EBUSY; + goto _out_lock; + } + + its_encode_cmd(hw_cmd, GITS_CMD_SYNC); + its_encode_target(hw_cmd, its->collections[cpu_id].target_address); + + err = its_cmd_submit_raw(its, hw_cmd); + } + +_out_lock: + rt_hw_spin_unlock(&its->cmd_lock.lock); + + return err; +} + +static rt_err_t its_cmd_submit(struct gicv3_its *its, struct its_command *cmd, + struct its_map *map, rt_bool_t sync) +{ + return its_cmd_submit_nomap(its, cmd, map->cpu_id, sync); +} + +static rt_err_t lpi_flush_config(struct gicv3_its *its, rt_uint8_t *conf, + struct rt_pic_irq *pirq) +{ + struct its_command cmd; + struct its_map *map = pirq->msi_desc->priv; + + if (its->gic->redist_flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING) + { + /* Clean D-cache under command */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, conf, sizeof(*conf)); + } + else + { + /* DSB inner shareable, store */ + rt_hw_wmb(); + } + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_INV); + its_encode_device_id(&cmd, its_pirq_device_id(its, pirq)); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + + return its_cmd_submit(its, &cmd, map, RT_FALSE); +} + +rt_inline void *gicr_rd_base_percpu(struct gicv3 *gic) +{ + return gic->redist_regions[rt_hw_cpu_id()].base; +} + +rt_inline void *gicr_rd_base(struct gicv3_its *its) +{ + return its->gic->redist_percpu_base[rt_hw_cpu_id()]; +} + +rt_inline rt_uint64_t *gicr_rd_flags(struct gicv3_its *its) +{ + return &its->gic->redist_percpu_flags[rt_hw_cpu_id()]; +} + +static rt_bool_t gicr_supports_plpis(struct gicv3_its *its) +{ + return !!(HWREG64(gicr_rd_base(its) + GICR_TYPER) & GICR_TYPER_PLPIS); +} + +static rt_err_t redist_disable_lpis(struct gicv3_its *its) +{ + void *gicr = gicr_rd_base(its); + rt_uint64_t timeout = 1000000L, val; + + if (!gicr_supports_plpis(its)) + { + LOG_E("CPU#%d: LPIs not supported", rt_hw_cpu_id()); + return -RT_ENOSYS; + } + + val = HWREG32(gicr + GICR_CTLR); + if (!(val & GICR_CTLR_ENABLE_LPIS)) + { + return RT_EOK; + } + + /* + * If coming via a CPU hotplug event, we don't need to disable + * LPIs before trying to re-enable them. They are already + * configured and all is well in the world. + * + * If running with preallocated tables, there is nothing to do. + */ + if ((*gicr_rd_flags(its) & RD_LOCAL_LPI_ENABLED) || + (its->gic->flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED)) + { + return RT_EOK; + } + + /* From that point on, we only try to do some damage control */ + LOG_W("CPU%d: Booted with LPIs enabled, memory probably corrupted", rt_hw_cpu_id()); + + /* Disable LPIs */ + val &= ~GICR_CTLR_ENABLE_LPIS; + HWREG32(gicr + GICR_CTLR) = val; + + /* Make sure any change to GICR_CTLR is observable by the GIC */ + rt_hw_barrier(dsb, sy); + + /* + * Software must observe RWP==0 after clearing GICR_CTLR.EnableLPIs + * from 1 to 0 before programming GICR_PEND{PROP}BASER registers. + * Error out if we time out waiting for RWP to clear. + */ + while (HWREG32(gicr + GICR_CTLR) & GICR_CTLR_RWP) + { + if (!timeout) + { + LOG_E("CPU#%d: Timeout while disabling LPIs", rt_hw_cpu_id()); + + return -RT_ETIMEOUT; + } + + rt_hw_us_delay(1); + --timeout; + } + + /* + * After it has been written to 1, it is IMPLEMENTATION + * DEFINED whether GICR_CTLR.EnableLPI becomes RES1 or can be + * cleared to 0. Error out if clearing the bit failed. + */ + if (HWREG32(gicr + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) + { + LOG_E("CPU#%d: Failed to disable LPIs", rt_hw_cpu_id()); + + return -RT_EBUSY; + } + + return RT_EOK; +} + +static void gicv3_its_cpu_init_lpis(struct gicv3_its *its) +{ + void *gicr; + rt_ubase_t paddr; + rt_uint64_t val, tmp; + + if (*gicr_rd_flags(its) & RD_LOCAL_LPI_ENABLED) + { + return; + } + + gicr = gicr_rd_base(its); + + val = HWREG32(gicr + GICR_CTLR); + + if ((its->gic->redist_flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) && + (val & GICR_CTLR_ENABLE_LPIS)) + { + *gicr_rd_flags(its) |= RD_LOCAL_PENDTABLE_PREALLOCATED; + + goto _out; + } + + paddr = (rt_ubase_t)rt_kmem_v2p(lpi_pending_table); + + /* Set PROPBASE */ + val = ((rt_ubase_t)rt_kmem_v2p(lpi_table) | + GITS_CBASER_InnerShareable | + GITS_CBASER_RaWaWb | + ((lpi_id_bits - 1) & GICR_PROPBASER_IDBITS_MASK)); + + HWREG64(gicr + GICR_PROPBASER) = val; + tmp = HWREG64(gicr + GICR_PROPBASER); + + if (its->gic->redist_flags & RDIST_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GICR_PBASER_SHARE_MASK_ALL; + } + + if ((tmp ^ val) & GICR_PBASER_SHARE_MASK_ALL) + { + if (!(tmp & GICR_PBASER_SHARE_MASK_ALL)) + { + /* + * The HW reports non-shareable, + * we must remove the cacheability attributes as well. + */ + val &= ~(GICR_PBASER_SHARE_MASK_ALL | GICR_PBASER_INNER_MASK_ALL); + val |= GICR_PBASER_nC; + HWREG64(gicr + GICR_PROPBASER) = val; + } + + if (!rt_hw_cpu_id()) + { + LOG_I("Using cache flushing for LPI property table"); + } + its->gic->redist_flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; + } + + val = (paddr | GICR_PBASER_InnerShareable | GICR_PBASER_RaWaWb); + + HWREG64(gicr + GICR_PENDBASER) = val; + tmp = HWREG64(gicr + GICR_PENDBASER); + + if (its->gic->redist_flags & RDIST_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GICR_PBASER_SHARE_MASK_ALL; + } + + if (!(tmp & GICR_PBASER_SHARE_MASK_ALL)) + { + /* + * The HW reports non-shareable, we must remove the + * cacheability attributes as well. + */ + val &= ~(GICR_PBASER_SHARE_MASK_ALL | GICR_PBASER_INNER_MASK_ALL); + val |= GICR_PBASER_nC; + HWREG64(gicr + GICR_PENDBASER) = val; + } + + /* Enable LPIs */ + val = HWREG32(gicr + GICR_CTLR); + val |= GICR_CTLR_ENABLE_LPIS; + HWREG32(gicr + GICR_CTLR) = val; + + rt_hw_barrier(dsb, sy); + +_out: + *gicr_rd_flags(its) |= RD_LOCAL_LPI_ENABLED; +} + +static void gicv3_its_cpu_init_collection(struct gicv3_its *its) +{ + rt_uint64_t target; + int cpu_id = rt_hw_cpu_id(); + struct its_command cmd; + struct its_collection *collection; + + if (HWREG64(its->base + GITS_TYPER) & GITS_TYPER_PTA) + { + target = (rt_uint64_t)rt_kmem_v2p(gicr_rd_base(its)); + } + else + { + /* Linear by GICR processor number */ + target = HWREG64(gicr_rd_base(its) + GICR_TYPER); + target = GICR_TYPER_CPU_NO(target) << 16; + } + + collection = &its->collections[cpu_id]; + collection->target_address = target; + collection->id = cpu_id; + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPC); + its_encode_collection(&cmd, collection->id); + its_encode_target(&cmd, target); + its_encode_valid(&cmd, RT_TRUE); + its_cmd_submit_nomap(its, &cmd, cpu_id, RT_TRUE); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_INVALL); + its_encode_collection(&cmd, collection->id); + its_cmd_submit_nomap(its, &cmd, cpu_id, RT_TRUE); +} + +static rt_err_t gicv3_its_irq_init(struct rt_pic *pic) +{ + rt_err_t err; + struct gicv3_its *its = raw_to_gicv3_its(pic); + + if ((err = redist_disable_lpis(its))) + { + return err; + } + + gicv3_its_cpu_init_lpis(its); + gicv3_its_cpu_init_collection(its); + + return RT_EOK; +} + +static void gicv3_its_irq_mask(struct rt_pic_irq *pirq) +{ + rt_uint8_t *conf = lpi_base_config(pirq->hwirq); + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + *conf &= ~GITS_LPI_CFG_ENABLED; + lpi_flush_config(its, conf, pirq); + + rt_pci_msi_mask_irq(pirq); +} + +static void gicv3_its_irq_unmask(struct rt_pic_irq *pirq) +{ + rt_uint8_t *conf = lpi_base_config(pirq->hwirq); + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + *conf |= GITS_LPI_CFG_ENABLED; + lpi_flush_config(its, conf, pirq); + + rt_pci_msi_unmask_irq(pirq); +} + +static rt_err_t gicv3_its_irq_set_priority(struct rt_pic_irq *pirq, rt_uint32_t priority) +{ + rt_uint8_t *conf = lpi_base_config(pirq->hwirq); + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + *conf = (priority << ITS_LPI_CONFIG_PROP_SHIFT) | (*conf & (~ITS_LPI_CONFIG_PROP_MASK)); + + return lpi_flush_config(its, conf, pirq); +} + +static rt_err_t gicv3_its_irq_set_affinity(struct rt_pic_irq *pirq, rt_bitmap_t *affinity) +{ + int cpu_id; + rt_err_t err; + struct its_map *map; + struct its_command cmd; + struct its_collection *collection; + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + map = pirq->msi_desc->priv; + cpu_id = rt_bitmap_next_set_bit(affinity, 0, RT_CPUS_NR); + collection = &its->collections[cpu_id]; + + if (collection->target_address == ~0ULL) + { + return -RT_EIO; + } + + if (map->cpu_id == cpu_id) + { + return RT_EOK; + } + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MOVI); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + its_encode_collection(&cmd, collection->id); + + if (!(err = its_cmd_submit(its, &cmd, map, RT_TRUE))) + { + map->cpu_id = cpu_id; + } + + return err; +} + +static void gicv3_its_irq_compose_msi_msg(struct rt_pic_irq *pirq, struct rt_pci_msi_msg *msg) +{ + rt_ubase_t addr; + struct gicv3_its *its = raw_to_gicv3_its(pirq->pic); + + addr = (rt_ubase_t)its->base_phy + GITS_TRANSLATER; + + msg->address_hi = rt_upper_32_bits(addr); + msg->address_lo = rt_lower_32_bits(addr); + msg->data = its_pirq_event_id(its, pirq); +} + +static int gicv3_its_irq_alloc_msi(struct rt_pic *pic, struct rt_pci_msi_desc *msi_desc) +{ + rt_ubase_t level; + rt_uint32_t device_id = -1; + int irq = -1, hwirq, parent_irq, hwirq_index, lpi_base = 0; + struct its_map *map = RT_NULL, *map_tmp; + struct its_table *tbl; + struct its_command cmd; + struct rt_pic_irq *pirq; + struct rt_pci_device *pdev = msi_desc->pdev; + struct gicv3_its *its = raw_to_gicv3_its(pic); + struct rt_pic *ppic = &its->gic->parent; + + tbl = its_baser_type(its, GITS_BASER_TYPE_DEVICE); + RT_ASSERT(tbl != RT_NULL); + + if (!pdev->parent.ofw_node) + { + device_id = rt_pci_dev_id(pdev); + } + else + { + struct rt_ofw_cell_args args; + + for (int index = 0; ; ++index) + { + rt_err_t err = rt_ofw_parse_phandle_cells(pdev->parent.ofw_node, + "msi-parent", "#msi-cells", index, &args); + + if (err) + { + return (int)err; + } + + if (args.data == its->np) + { + device_id = args.args[0]; + } + + rt_ofw_node_put(args.data); + + if ((rt_int32_t)device_id >= 0) + { + break; + } + } + } + + if (device_id >= (1 << tbl->size_bits)) + { + LOG_E("Device ID = is %x not supported", device_id); + + return -RT_EINVAL; + } + + /* Find old map info */ + level = rt_spin_lock_irqsave(&map_lock); + rt_list_for_each_entry(map_tmp, &map_nodes, list) + { + if (map_tmp->device_id == device_id) + { + map = map_tmp; + lpi_base = map->lpi_base - 8192; + break; + } + } + rt_spin_unlock_irqrestore(&map_lock, level); + + if (!map) + { + rt_size_t itt_size; + + if (!(map = rt_calloc(1, sizeof(*map)))) + { + return -RT_ENOMEM; + } + + itt_size = tbl->itt_entries * (RT_FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, + HWREG64(its->base + GITS_TYPER)) + 1); + itt_size = rt_max_t(rt_size_t, itt_size, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; + + map->itt = rt_malloc_align(itt_size, ITS_ITT_ALIGN); + + if (!map->itt) + { + rt_free(map); + return -RT_ENOMEM; + } + + if (tbl->lvl2_bits) + { + void *lvl2_dte; + rt_uint64_t *entry; + + entry = tbl->base; + entry += device_id / (tbl->page_size / GITS_LVL1_ENTRY_SIZE); + + if (*entry) + { + lvl2_dte = (void *)(*entry - PV_OFFSET); + rt_page_ref_inc(lvl2_dte, tbl->lvl2_bits); + } + else + { + rt_size_t dte_size; + + lvl2_dte = rt_pages_alloc(tbl->lvl2_bits); + + if (!lvl2_dte) + { + rt_free_align(map->itt); + rt_free(map); + return -RT_ENOMEM; + } + + dte_size = rt_page_bits(tbl->lvl2_bits); + rt_memset(lvl2_dte, 0, dte_size); + + if (!(tbl->val & GITS_BASER_SHARE_MASK_ALL)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lvl2_dte, dte_size); + } + + *entry = rt_cpu_to_le64((rt_uint64_t)rt_kmem_v2p(lvl2_dte) | GITS_BASER_VALID); + + if (!(tbl->val & GITS_BASER_SHARE_MASK_ALL)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, entry, sizeof(*entry)); + } + + rt_hw_dsb(); + } + + map->lvl2_dte = lvl2_dte; + } + + rt_memset(map->itt, 0, itt_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, map->itt, itt_size); + } + msi_desc->priv = map; + + /* Alloc the LPI base on the first LPI */ + level = rt_spin_lock_irqsave(&lpis_lock); + hwirq_index = rt_bitmap_next_clear_bit(lpis_vectors, lpi_base, lpi_nr); + + if (hwirq_index >= lpi_nr) + { + irq = -RT_EEMPTY; + goto _out_lock; + } + + hwirq = 8192 + hwirq_index; + parent_irq = ppic->ops->irq_map(ppic, hwirq, RT_IRQ_MODE_EDGE_RISING); + if (parent_irq < 0) + { + irq = parent_irq; + goto _out_lock; + } + + irq = rt_pic_config_irq(pic, hwirq_index, hwirq); + if (irq < 0) + { + goto _out_lock; + } + pirq = rt_pic_find_irq(pic, hwirq_index); + + pirq->mode = RT_IRQ_MODE_EDGE_RISING; + rt_pic_cascade(pirq, parent_irq); + + rt_bitmap_set_bit(lpis_vectors, hwirq_index); + +_out_lock: + rt_spin_unlock_irqrestore(&lpis_lock, level); + + if (irq < 0) + { + return irq; + } + + if (map->its) + { + rt_ref_get(&map->ref); + } + else + { + rt_list_init(&map->list); + rt_ref_init(&map->ref); + map->its = its; + map->device_id = device_id; + map->lpi_base = hwirq; + + level = rt_spin_lock_irqsave(&map_lock); + rt_list_insert_before(&map_nodes, &map->list); + rt_spin_unlock_irqrestore(&map_lock, level); + } + + /* Default to CPU#0 */ + map->cpu_id = 0; + RT_IRQ_AFFINITY_SET(pirq->affinity, map->cpu_id); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPD); + its_encode_device_id(&cmd, device_id); + its_encode_size(&cmd, rt_ilog2(tbl->itt_entries) - 1); + its_encode_itt(&cmd, (rt_uint64_t)rt_kmem_v2p(map->itt)); + its_encode_valid(&cmd, RT_TRUE); + its_cmd_submit(its, &cmd, map, RT_FALSE); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPTI); + its_encode_device_id(&cmd, device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + its_encode_phys_id(&cmd, hwirq); + its_encode_collection(&cmd, its->collections[map->cpu_id].id); + its_cmd_submit(its, &cmd, map, RT_TRUE); + + return irq; +} + +static void its_map_release(struct rt_ref *r) +{ + rt_ubase_t level; + struct gicv3_its *its; + struct its_table *tbl; + struct its_command cmd; + struct its_map *map = rt_container_of(r, struct its_map, ref); + + its = map->its; + tbl = its_baser_type(its, GITS_BASER_TYPE_DEVICE); + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_MAPD); + its_encode_device_id(&cmd, map->device_id); + its_encode_size(&cmd, rt_ilog2(tbl->itt_entries) - 1); + its_encode_itt(&cmd, (rt_uint64_t)rt_kmem_v2p(map->itt)); + its_encode_valid(&cmd, RT_FALSE); + its_cmd_submit(its, &cmd, map, RT_TRUE); + + level = rt_spin_lock_irqsave(&map_lock); + rt_list_insert_before(&map_nodes, &map->list); + rt_spin_unlock_irqrestore(&map_lock, level); + + if (map->itt) + { + rt_free_align(map->itt); + } + if (map->lvl2_dte) + { + if (rt_page_ref_get(map->lvl2_dte, tbl->lvl2_bits) == 1) + { + rt_uint64_t *entry; + + entry = tbl->base + (map->device_id / (tbl->page_size / GITS_LVL1_ENTRY_SIZE)); + *entry = rt_cpu_to_le64(0); + + if (!(tbl->val & GITS_BASER_SHARE_MASK_ALL)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, entry, sizeof(*entry)); + } + } + rt_pages_free(map->lvl2_dte, tbl->lvl2_bits); + } + rt_free(map); +} + +static void gicv3_its_irq_free_msi(struct rt_pic *pic, int irq) +{ + rt_ubase_t level; + struct its_map *map; + struct its_command cmd; + struct rt_pic_irq *pirq; + struct gicv3_its *its = raw_to_gicv3_its(pic); + + pirq = rt_pic_find_pirq(pic, irq); + + if (!pirq) + { + return; + } + + map = pirq->msi_desc->priv; + + rt_memset(&cmd, 0, sizeof(cmd)); + its_encode_cmd(&cmd, GITS_CMD_DISCARD); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + its_cmd_submit(its, &cmd, map, RT_TRUE); + + rt_pic_uncascade(pirq); + + level = rt_spin_lock_irqsave(&lpis_lock); + rt_bitmap_clear_bit(lpis_vectors, pirq->hwirq - 8192); + rt_spin_unlock_irqrestore(&lpis_lock, level); + + rt_ref_put(&map->ref, its_map_release); +} + +static rt_err_t gicv3_its_irq_set_state(struct rt_pic *pic, int hwirq, int type, rt_bool_t state) +{ + struct its_map *map; + struct its_command cmd; + struct rt_pic_irq *pirq; + struct gicv3_its *its = raw_to_gicv3_its(pic); + + if (type != RT_IRQ_STATE_PENDING || hwirq > 8192 + lpi_nr) + { + return -RT_ENOSYS; + } + + if (!(pirq = rt_pic_find_irq(pic, hwirq - 8192))) + { + return -RT_ENOSYS; + } + + map = pirq->msi_desc->priv; + rt_memset(&cmd, 0, sizeof(cmd)); + + if (state) + { + its_encode_cmd(&cmd, GITS_CMD_INT); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + } + else + { + its_encode_cmd(&cmd, GITS_CMD_CLEAR); + its_encode_device_id(&cmd, map->device_id); + its_encode_event_id(&cmd, its_pirq_event_id(its, pirq)); + } + + its_cmd_submit(its, &cmd, map, RT_TRUE); + + return RT_EOK; +} + +const static struct rt_pic_ops gicv3_its_ops = +{ + .name = "GICv3-ITS", + .irq_init = gicv3_its_irq_init, + .irq_ack = rt_pic_irq_parent_ack, + .irq_mask = gicv3_its_irq_mask, + .irq_unmask = gicv3_its_irq_unmask, + .irq_eoi = rt_pic_irq_parent_eoi, + .irq_set_priority = gicv3_its_irq_set_priority, + .irq_set_affinity = gicv3_its_irq_set_affinity, + .irq_compose_msi_msg = gicv3_its_irq_compose_msi_msg, + .irq_alloc_msi = gicv3_its_irq_alloc_msi, + .irq_free_msi = gicv3_its_irq_free_msi, + .irq_set_state = gicv3_its_irq_set_state, + .flags = RT_PIC_F_IRQ_ROUTING, +}; + +static rt_ssize_t its_baser_page_size(struct gicv3_its *its, struct its_table *tbl) +{ + rt_size_t page_size = 64 * SIZE_KB; + + while (page_size) + { + rt_uint64_t val, baser_page_size; + rt_off_t baser = GITS_BASERn((int)(tbl - its->tbls)); + + val = its_readq(its, baser); + val &= ~GITS_BASER_PAGE_SIZE_MASK; + + switch (page_size) + { + case 64 * SIZE_KB: + baser_page_size = GITS_BASER_PAGE_SIZE_64K; + break; + case 16 * SIZE_KB: + baser_page_size = GITS_BASER_PAGE_SIZE_16K; + break; + case 4 * SIZE_KB: + default: + baser_page_size = GITS_BASER_PAGE_SIZE_4K; + break; + } + + baser_page_size >>= GITS_BASER_PAGE_SIZE_SHIFT; + + val |= RT_FIELD_PREP(GITS_BASER_PAGE_SIZE_MASK, baser_page_size); + its_writeq(its, baser, val); + tbl->val = its_readq(its, baser); + + if (RT_FIELD_GET(GITS_BASER_PAGE_SIZE_MASK, tbl->val) == baser_page_size) + { + break; + } + + switch (page_size) + { + case 64 * SIZE_KB: + page_size = 16 * SIZE_KB; + break; + case 16 * SIZE_KB: + page_size = 4 * SIZE_KB; + break; + case 4 * SIZE_KB: + default: + return -RT_EINVAL; + } + } + + return page_size; +} + +static rt_err_t its_table_init(struct gicv3_its *its) +{ + int inited = 0; + rt_off_t baser; + rt_bool_t indirect = RT_FALSE; + rt_size_t pages_nr, alloc_size; + rt_uint64_t val, type, entry_size, share, cache; + struct its_table *tbl; + + share = GITS_BASER_InnerShareable; + cache = GITS_BASER_RaWaWb; + + for (int i = 0; i < RT_ARRAY_SIZE(its->tbls); ++i) + { + tbl = &its->tbls[i]; + + val = its_readq(its, GITS_BASERn(i)); + type = GITS_BASER_TYPE(val); + + if (type != GITS_BASER_TYPE_DEVICE && + type != GITS_BASER_TYPE_COLLECTION) + { + continue; + } + + tbl->page_size = its_baser_page_size(its, tbl); + + if (tbl->page_size < 0) + { + continue; + } + + baser = GITS_BASERn((int)(tbl - its->tbls)); + entry_size = GITS_BASER_ENTRY_SIZE(val); + + if (type == GITS_BASER_TYPE_DEVICE) + { + tbl->size_bits = its_device_id_bits(its); + LOG_D("Device Max IDs = %lu", 1UL << tbl->size_bits); + + /* For MSI-X */ + tbl->itt_entries = 2048; + while (MAX_HANDLERS / tbl->itt_entries < (1 << tbl->size_bits) && + tbl->itt_entries > 32) + { + tbl->itt_entries >>= 1; + } + } + + its_writeq(its, baser, tbl->val | GITS_BASER_INDIRECT); + tbl->val = its_readq(its, baser); + + indirect = !!(tbl->val & GITS_BASER_INDIRECT); + if (indirect && type == GITS_BASER_TYPE_DEVICE) + { + /* The size of the level 2 table is equal to ITS page size */ + tbl->lvl2_bits = tbl->size_bits - rt_ilog2(tbl->page_size / (int)entry_size); + + /* Get level 1 entries count */ + alloc_size = (1 << tbl->size_bits) / (tbl->page_size / entry_size); + alloc_size *= GITS_LVL1_ENTRY_SIZE; + } + else + { + alloc_size = (1 << tbl->size_bits) * entry_size; + indirect = RT_FALSE; + } + + tbl->base = rt_malloc_align(alloc_size, tbl->page_size); + pages_nr = alloc_size / tbl->page_size; + + if (!tbl->base) + { + return -RT_ENOMEM; + } + + if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) + { + cache = GITS_BASER_nCnB; + } + + if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE) + { + cache = GITS_BASER_nC; + share = 0; + } + + val = ((rt_ubase_t)rt_kmem_v2p(tbl->base) | + (type << GITS_BASER_TYPE_SHIFT) | + ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | + (pages_nr << GITS_BASER_PAGES_SHIFT) | + cache | share | GITS_BASER_VALID); + val |= indirect ? GITS_BASER_INDIRECT : 0; + + switch (tbl->page_size) + { + case 4 * SIZE_KB: + val |= GITS_BASER_PAGE_SIZE_4K; + break; + case 16 * SIZE_KB: + val |= GITS_BASER_PAGE_SIZE_16K; + break; + case 64 * SIZE_KB: + val |= GITS_BASER_PAGE_SIZE_64K; + break; + } + + its_writeq(its, baser, val); + tbl->val = its_readq(its, baser); + + rt_memset(tbl->base, 0, alloc_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, tbl->base, alloc_size); + + cache = tbl->val & GITS_BASER_INNER_MASK_ALL; + share = tbl->val & GITS_BASER_SHARE_MASK_ALL; + + ++inited; + } + + return inited == 2 ? RT_EOK : -RT_ENOSYS; +} + +static rt_err_t its_cmd_queue_init(struct gicv3_its *its) +{ + void *cmd_phy_base; + rt_uint64_t baser, tmp; + + its->cmd_base = rt_malloc_align(ITS_CMD_QUEUE_SIZE, ITS_CMD_QUEUE_ALIGN); + + if (!its->cmd_base) + { + return -RT_ENOMEM; + } + + its->cmd_idx = 0; + rt_memset(its->cmd_base, 0, ITS_CMD_QUEUE_SIZE); + + cmd_phy_base = rt_kmem_v2p(its->cmd_base); + + baser = GITS_CBASER_VALID | GITS_CBASER_RaWaWb | GITS_CBASER_InnerShareable | \ + ((rt_uint64_t)cmd_phy_base) | (ITS_CMD_QUEUE_SIZE / (4 * SIZE_KB) - 1); + + its_writeq(its, GITS_CBASER, baser); + tmp = its_readq(its, GITS_CBASER); + + if (its->flags & ITS_FLAGS_FORCE_NON_SHAREABLE) + { + tmp &= ~GITS_CBASER_SHARE_MASK_ALL; + } + + if ((tmp ^ baser) & GITS_CBASER_SHARE_MASK_ALL) + { + if (!(tmp & GITS_CBASER_SHARE_MASK_ALL)) + { + /* The HW reports non-shareable, we must remove the cacheability attributes as well */ + baser &= ~(GITS_CBASER_SHARE_MASK_ALL | GITS_CBASER_INNER_MASK_ALL); + baser |= GITS_CBASER_nC; + + its_writeq(its, GITS_CBASER, baser); + } + + LOG_I("Using cache flushing for CMD queue"); + its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, its->cmd_base, ITS_CMD_QUEUE_SIZE); + } + + /* Get the next command from the start of the buffer */ + its_writeq(its, GITS_CWRITER, 0); + + return RT_EOK; +} + +static rt_err_t its_lpi_table_init(struct gicv3 *gic) +{ + rt_size_t lpi_table_size, lpi_pending_table_size; + rt_uint32_t numlpis = 1UL << GICD_TYPER_NUM_LPIS(gic->gicd_typer); + + if (HWREG32(gicr_rd_base_percpu(gic) + GICR_CTLR) & GICR_CTLR_ENABLE_LPIS) + { + gic->redist_flags |= RDIST_FLAGS_RD_TABLES_PREALLOCATED; + gic->redist_flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; + + LOG_I("Using preallocated redistributor tables"); + } + + lpi_id_bits = GICD_TYPER_ID_BITS(gic->gicd_typer); + + if (gic->redist_flags & RDIST_FLAGS_RD_TABLES_PREALLOCATED) + { + rt_uint64_t val = HWREG64(gicr_rd_base_percpu(gic) + GICR_PROPBASER); + lpi_id_bits = rt_min_t(rt_uint32_t, lpi_id_bits, (val & GICR_PROPBASER_IDBITS_MASK) + 1); + } + + lpi_nr = rt_min_t(rt_size_t, (1UL << lpi_id_bits) - 8192, gic->lpi_nr); + lpi_id_bits = __rt_clz(lpi_nr + 8192); + + if (numlpis > 2 && numlpis > lpi_nr) + { + lpi_nr = numlpis; + LOG_W("Using hypervisor restricted LPI range [%u]", lpi_nr); + } + + gic->lpi_nr = lpi_nr; + + /* LPI Configuration table entry is 1 byte, Pending table bytes is N / 8. */ + lpi_table_size = RT_GENMASK(lpi_id_bits, 0); + lpi_pending_table_size = lpi_table_size / 8; + + lpi_table = rt_malloc_align(lpi_table_size, ITS_LPI_CONFIG_TABLE_ALIGN); + lpi_pending_table = rt_malloc_align(lpi_pending_table_size, ITS_LPI_PENDING_TABLE_ALIGN); + lpis_vectors = rt_calloc(1, RT_BITMAP_LEN(lpi_nr) * sizeof(rt_bitmap_t)); + + if (!lpi_table || !lpi_pending_table || !lpis_vectors) + { + if (lpi_table) + { + rt_free_align(lpi_table); + } + if (lpi_pending_table) + { + rt_free_align(lpi_pending_table); + } + if (lpis_vectors) + { + rt_free_align(lpis_vectors); + } + + lpi_table = RT_NULL; + lpi_pending_table = RT_NULL; + lpis_vectors = RT_NULL; + + return -RT_ENOMEM; + } + + /* Set the default configuration */ + rt_memset(lpi_table, ITS_LPI_CONFIG_PROP_DEFAULT_PRIO | GITS_LPI_CFG_GROUP1, lpi_table_size); + /* + * We should make a full mask size with lpi_id_bits, + * otherwise 'undefined' LPI will occur. + */ + rt_memset(lpi_pending_table, 0, lpi_pending_table_size); + + /* Flush the table to memory */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lpi_table, lpi_table_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lpi_pending_table, lpi_pending_table_size); + + LOG_D("ITS: Allocator initialized for %u LPIs", lpi_nr); + + return RT_EOK; +} + +static void its_init_fail(struct gicv3_its *its) +{ + if (its->base) + { + rt_iounmap(its->base); + } + + if (its->cmd_base) + { + rt_free_align(its->cmd_base); + } + + for (int i = 0; i < RT_ARRAY_SIZE(its->tbls); ++i) + { + struct its_table *tbl = &its->tbls[i]; + + if (tbl->base) + { + rt_free_align(tbl->base); + } + } + + rt_list_remove(&its->list); + rt_free(its); +} + +static rt_err_t its_quirk_cavium_22375(void *data) +{ + struct gicv3_its *its = data; + + its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; + + return RT_EOK; +} + +static rt_err_t its_enable_rockchip(void *data) +{ + struct gicv3_its *its = data; + struct gicv3 *gic = its->gic; + + if (!rt_ofw_machine_is_compatible("rockchip,rk3566") && + !rt_ofw_machine_is_compatible("rockchip,rk3567") && + !rt_ofw_machine_is_compatible("rockchip,rk3568") && + !rt_ofw_machine_is_compatible("rockchip,rk3588") && + !rt_ofw_machine_is_compatible("rockchip,rk3588s")) + { + return -RT_EINVAL; + } + + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + gic->redist_flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + + return RT_EOK; +} + +static rt_err_t its_set_non_coherent(void *data) +{ + struct gicv3_its *its = data; + + if (!rt_ofw_prop_read_bool(its->np, "dma-noncoherent")) + { + return -RT_EINVAL; + } + + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + + return RT_EOK; +} + +static const struct gic_quirk _its_quirks[] = +{ + { + .desc = "ITS: Cavium ThunderX errata: 22375, 24313", + .iidr = 0xa100034c, + .iidr_mask = 0xffff0fff, + .init = its_quirk_cavium_22375, + }, + { + .desc = "ITS: Rockchip erratum RK3566 ~ RK3588", + .iidr = 0x0201743b, + .iidr_mask = 0xffffffff, + .init = its_enable_rockchip, + }, + { + .desc = "ITS: non-coherent attribute", + .compatible = "arm,gic-v3-its", + .init = its_set_non_coherent, + }, + { /* sentinel */ } +}; + +static const struct rt_ofw_node_id gicv3_its_ofw_match[] = +{ + { .compatible = "arm,gic-v3-its" }, + { /* sentinel */ } +}; + +rt_err_t gicv3_its_ofw_probe(struct rt_ofw_node *np, const struct rt_ofw_node_id *id) +{ + rt_err_t err = -RT_EEMPTY; + struct rt_ofw_node *its_np; + struct gicv3_its *its, *its_next; + + rt_ofw_foreach_available_child_node(np, its_np) + { + if (!rt_ofw_node_match(its_np, gicv3_its_ofw_match)) + { + continue; + } + + if (!rt_ofw_prop_read_bool(its_np, "msi-controller")) + { + continue; + } + + if (!(its = rt_calloc(1, sizeof(struct gicv3_its)))) + { + rt_ofw_node_put(its_np); + + err = -RT_ENOMEM; + goto _free_all; + } + + its->base = rt_ofw_iomap(its_np, 0); + + if (!its->base) + { + LOG_E("%s: IO map failed", rt_ofw_node_full_name(its_np)); + its_init_fail(its); + continue; + } + + /* + * Make sure ALL the ITS are reset before we probe any, + * as they may be sharing memory + */ + for (int i = 0; i < GITS_BASER_NR_REGS; ++i) + { + its_writeq(its, GITS_BASER + (i << 3), 0); + } + + its->np = its_np; + rt_list_init(&its->list); + rt_list_insert_before(&its_nodes, &its->list); + } + + if (!rt_list_isempty(&its_nodes)) + { + if ((err = its_lpi_table_init(rt_ofw_data(np)))) + { + goto _free_all; + } + } + + rt_list_for_each_entry_safe(its, its_next, &its_nodes, list) + { + rt_uint32_t ctlr; + + its->base_phy = rt_kmem_v2p(its->base); + its->gic = rt_ofw_data(np); + + gic_common_init_quirk_hw(HWREG32(its->base + GITS_IIDR), _its_quirks, its); + gic_common_init_quirk_ofw(its->np, _its_quirks, its); + + if ((err = its_cmd_queue_init(its))) + { + goto _fail; + } + rt_spin_lock_init(&its->cmd_lock); + + if ((err = its_table_init(its))) + { + goto _fail; + } + + for (int i = 0; i < RT_CPUS_NR; ++i) + { + its->collections[i].target_address = ~0ULL; + } + + ctlr = its_readl(its, GITS_CTLR); + ctlr |= GITS_CTLR_ENABLE; + its_writel(its, GITS_CTLR, ctlr); + + its->parent.priv_data = its; + its->parent.ops = &gicv3_its_ops; + + rt_pic_linear_irq(&its->parent, its->gic->lpi_nr); + rt_pic_user_extends(&its->parent); + + its_np = its->np; + rt_ofw_data(its_np) = &its->parent; + rt_ofw_node_set_flag(its_np, RT_OFW_F_READLY); + + continue; + + _fail: + its_init_fail(its); + + if (err == -RT_ENOMEM) + { + break; + } + } + + if (rt_list_isempty(&its_nodes) && lpis_vectors) + { + rt_free(lpis_vectors); + rt_free_align(lpi_table); + rt_free_align(lpi_pending_table); + lpis_vectors = RT_NULL; + } + + return err; + +_free_all: + rt_list_for_each_entry_safe(its, its_next, &its_nodes, list) + { + rt_free(its); + rt_list_remove(&its->list); + } + + return err; +} diff --git a/components/drivers/pic/pic-gicv3.c b/components/drivers/pic/pic-gicv3.c index c5e4bc780b..cb8e0388e4 100644 --- a/components/drivers/pic/pic-gicv3.c +++ b/components/drivers/pic/pic-gicv3.c @@ -266,20 +266,25 @@ static void gicv3_dist_init(void) HWREG64(base + GICD_IROUTERnE + i * 8) = affinity; } - if (GICD_TYPER_NUM_LPIS(_gic.gicd_typer)) + if (GICD_TYPER_NUM_LPIS(_gic.gicd_typer) > 1) { /* Max LPI = 8192 + Math.pow(2, num_LPIs + 1) - 1 */ - rt_size_t num_lpis = (1 << (GICD_TYPER_NUM_LPIS(_gic.gicd_typer) + 1)) + 1; + rt_size_t num_lpis = 1UL << (GICD_TYPER_NUM_LPIS(_gic.gicd_typer) + 1); - _gic.lpi_nr = rt_min_t(int, num_lpis, 1 << GICD_TYPER_ID_BITS(_gic.gicd_typer)); + _gic.lpi_nr = rt_min_t(int, num_lpis, 1UL << GICD_TYPER_ID_BITS(_gic.gicd_typer)); } else { - _gic.lpi_nr = 1 << GICD_TYPER_ID_BITS(_gic.gicd_typer); + _gic.lpi_nr = 1UL << GICD_TYPER_ID_BITS(_gic.gicd_typer); } /* SPI + eSPI + LPIs */ - _gic.irq_nr = _gic.line_nr - 32 + _gic.espi_nr + _gic.lpi_nr; + _gic.irq_nr = _gic.line_nr - 32 + _gic.espi_nr; +#ifdef RT_PIC_ARM_GIC_V3_ITS + /* ITS will allocate the same number of lpi PIRQs */ + _gic.lpi_nr = rt_min_t(rt_size_t, RT_PIC_ARM_GIC_V3_ITS_IRQ_MAX, _gic.lpi_nr); + _gic.irq_nr += _gic.lpi_nr; +#endif } static void gicv3_redist_enable(rt_bool_t enable) @@ -389,6 +394,8 @@ static void gicv3_cpu_init(void) int cpu_id = rt_hw_cpu_id(); #ifdef ARCH_SUPPORT_HYP _gicv3_eoi_mode_ns = RT_TRUE; +#else + _gicv3_eoi_mode_ns = !!rt_ofw_bootargs_select("pic.gicv3_eoimode", 0); #endif base = gicv3_percpu_redist_sgi_base(); @@ -700,6 +707,7 @@ static int gicv3_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) if (pirq && hwirq >= GIC_SGI_NR) { pirq->mode = mode; + pirq->priority = GICD_INT_DEF_PRI; switch (gicv3_hwirq_type(hwirq)) { @@ -708,7 +716,6 @@ static int gicv3_irq_map(struct rt_pic *pic, int hwirq, rt_uint32_t mode) break; case SPI_TYPE: case ESPI_TYPE: - pirq->priority = GICD_INT_DEF_PRI; RT_IRQ_AFFINITY_SET(pirq->affinity, _init_cpu_id); default: break; @@ -823,7 +830,18 @@ static rt_bool_t gicv3_handler(void *data) } else { - pirq = rt_pic_find_irq(&gic->parent, hwirq - GIC_SGI_NR); + int irq_index; + + if (hwirq < 8192) + { + irq_index = hwirq - GIC_SGI_NR; + } + else + { + irq_index = gic->irq_nr - gic->lpi_nr + hwirq - 8192; + } + + pirq = rt_pic_find_irq(&gic->parent, irq_index); } gicv3_irq_ack(pirq); diff --git a/components/drivers/pic/pic-gicv3.h b/components/drivers/pic/pic-gicv3.h index 6046d9b431..dcd64d6582 100644 --- a/components/drivers/pic/pic-gicv3.h +++ b/components/drivers/pic/pic-gicv3.h @@ -12,8 +12,8 @@ * 2023-02-01 GuEe-GUI move macros to header */ -#ifndef __IRQ_GICV3_H__ -#define __IRQ_GICV3_H__ +#ifndef __PIC_GICV3_H__ +#define __PIC_GICV3_H__ #include @@ -101,6 +101,8 @@ #define GICR_CTLR_IR (1UL << 2) #define GICR_CTLR_RWP (1UL << 3) +#define GICR_TYPER_CPU_NO(r) (((r) >> 8) & 0xffff) + #define GICR_RD_BASE_SIZE (64 * SIZE_KB) #define GICR_SGI_OFFSET (64 * SIZE_KB) #define GICR_SGI_BASE_SIZE GICR_SGI_OFFSET @@ -152,66 +154,142 @@ #define GITS_CTLR 0x0000 #define GITS_IIDR 0x0004 #define GITS_TYPER 0x0008 -#define GITS_MPAMIDR 0x0010 -#define GITS_PARTIDR 0x0014 #define GITS_MPIDR 0x0018 -#define GITS_STATUSR 0x0040 -#define GITS_UMSIR 0x0048 -#define GITS_CBASER 0x0048 +#define GITS_CBASER 0x0080 #define GITS_CWRITER 0x0088 #define GITS_CREADR 0x0090 -#define GITS_BASER 0x0100 /* 0x0100~0x0138 */ +#define GITS_BASER 0x0100 +#define GITS_IDREGS_BASE 0xffd0 +#define GITS_PIDR0 0xffe0 +#define GITS_PIDR1 0xffe4 +#define GITS_PIDR2 GICR_PIDR2 +#define GITS_PIDR4 0xffd0 +#define GITS_CIDR0 0xfff0 +#define GITS_CIDR1 0xfff4 +#define GITS_CIDR2 0xfff8 +#define GITS_CIDR3 0xfffc + +#define GITS_TRANSLATER 0x10040 + +#define GITS_SGIR 0x20020 + +#define GITS_SGIR_VPEID RT_GENMASK_ULL(47, 32) +#define GITS_SGIR_VINTID RT_GENMASK_ULL(3, 0) + +#define GITS_CTLR_ENABLE (1U << 0) +#define GITS_CTLR_ImDe (1U << 1) +#define GITS_CTLR_ITS_NUMBER_SHIFT 4 +#define GITS_CTLR_ITS_NUMBER (0xFU << GITS_CTLR_ITS_NUMBER_SHIFT) +#define GITS_CTLR_QUIESCENT (1U << 31) + +#define GITS_TYPER_PLPIS (1UL << 0) +#define GITS_TYPER_VLPIS (1UL << 1) +#define GITS_TYPER_ITT_ENTRY_SIZE_SHIFT 4 +#define GITS_TYPER_ITT_ENTRY_SIZE RT_GENMASK_ULL(7, 4) +#define GITS_TYPER_IDBITS_SHIFT 8 +#define GITS_TYPER_DEVBITS_SHIFT 13 +#define GITS_TYPER_DEVBITS RT_GENMASK_ULL(17, 13) +#define GITS_TYPER_PTA (1UL << 19) +#define GITS_TYPER_HCC_SHIFT 24 +#define GITS_TYPER_HCC(r) (((r) >> GITS_TYPER_HCC_SHIFT) & 0xff) +#define GITS_TYPER_VMOVP (1ULL << 37) +#define GITS_TYPER_VMAPP (1ULL << 40) +#define GITS_TYPER_SVPET RT_GENMASK_ULL(42, 41) /* * ITS commands */ -#define GITS_CMD_MAPD 0x08 -#define GITS_CMD_MAPC 0x09 -#define GITS_CMD_MAPTI 0x0a -#define GITS_CMD_MAPI 0x0b -#define GITS_CMD_MOVI 0x01 -#define GITS_CMD_DISCARD 0x0f -#define GITS_CMD_INV 0x0c -#define GITS_CMD_MOVALL 0x0e -#define GITS_CMD_INVALL 0x0d -#define GITS_CMD_INT 0x03 -#define GITS_CMD_CLEAR 0x04 -#define GITS_CMD_SYNC 0x05 +#define GITS_CMD_MAPD 0x08 +#define GITS_CMD_MAPC 0x09 +#define GITS_CMD_MAPTI 0x0a +#define GITS_CMD_MAPI 0x0b +#define GITS_CMD_MOVI 0x01 +#define GITS_CMD_DISCARD 0x0f +#define GITS_CMD_INV 0x0c +#define GITS_CMD_MOVALL 0x0e +#define GITS_CMD_INVALL 0x0d +#define GITS_CMD_INT 0x03 +#define GITS_CMD_CLEAR 0x04 +#define GITS_CMD_SYNC 0x05 /* ITS Config Area */ -#define GITS_LPI_CFG_GROUP1 (1 << 1) -#define GITS_LPI_CFG_ENABLED (1 << 0) +#define GITS_LPI_CFG_GROUP1 (1 << 1) +#define GITS_LPI_CFG_ENABLED (1 << 0) /* ITS Command Queue Descriptor */ +#define GITS_BASER_SHAREABILITY_SHIFT 10 +#define GITS_BASER_INNER_CACHEABILITY_SHIFT 59 +#define GITS_BASER_OUTER_CACHEABILITY_SHIFT 53 #define GITS_CBASER_VALID (1UL << 63) -#define GITS_CBASER_SHAREABILITY_SHIFT (10) -#define GITS_CBASER_INNER_CACHEABILITY_SHIFT (59) -#define GITS_CBASER_OUTER_CACHEABILITY_SHIFT (53) +#define GITS_CBASER_SHAREABILITY_SHIFT 10 +#define GITS_CBASER_INNER_CACHEABILITY_SHIFT 59 +#define GITS_CBASER_OUTER_CACHEABILITY_SHIFT 53 +#define GICR_PBASER_SHAREABILITY_SHIFT 10 +#define GICR_PBASER_INNER_CACHEABILITY_SHIFT 7 +#define GICR_PBASER_OUTER_CACHEABILITY_SHIFT 56 -#define GITS_TRANSLATION_TABLE_DESCRIPTORS_NR 8 +#define GITS_BASER_NR_REGS 8 +#define GITS_BASERn(idx) (GITS_BASER + sizeof(rt_uint64_t) * idx) + +#define GITS_BASER_VALID (1ULL << 63) +#define GITS_BASER_INDIRECT (1ULL << 62) +#define GITS_BASER_TYPE_SHIFT 56 +#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7) +#define GITS_BASER_ENTRY_SIZE_SHIFT 48 +#define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0x1f) + 1) +#define GITS_BASER_PAGE_SIZE_SHIFT 8 +#define GITS_BASER_PAGE_SIZE_4K (0ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGE_SIZE_16K (1ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGE_SIZE_64K (2ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGE_SIZE_MASK (3ULL << GITS_BASER_PAGE_SIZE_SHIFT) +#define GITS_BASER_PAGES_SHIFT 0 + +#define GITS_LVL1_ENTRY_SIZE 8UL + +#define GITS_BASER_TYPE_NONE 0 +#define GITS_BASER_TYPE_DEVICE 1 +#define GITS_BASER_TYPE_VPE 2 +#define GITS_BASER_TYPE_RESERVED3 3 +#define GITS_BASER_TYPE_COLLECTION 4 +#define GITS_BASER_TYPE_RESERVED5 5 +#define GITS_BASER_TYPE_RESERVED6 6 +#define GITS_BASER_TYPE_RESERVED7 7 #define GITS_BASER_CACHEABILITY(reg, inner_outer, type) \ - (GITS_CBASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) + (GIC_BASER_CACHE_##type << reg##_##inner_outer##_CACHEABILITY_SHIFT) #define GITS_BASER_SHAREABILITY(reg, type) \ - (GITS_CBASER_##type << reg##_SHAREABILITY_SHIFT) + (GIC_BASER_##type << reg##_SHAREABILITY_SHIFT) -#define GITS_CBASER_CACHE_DnGnRnE 0x0UL /* Device-nGnRnE. */ -#define GITS_CBASER_CACHE_NIN 0x1UL /* Normal Inner Non-cacheable. */ -#define GITS_CBASER_CACHE_NIRAWT 0x2UL /* Normal Inner Cacheable Read-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIRAWB 0x3UL /* Normal Inner Cacheable Read-allocate, Write-back. */ -#define GITS_CBASER_CACHE_NIWAWT 0x4UL /* Normal Inner Cacheable Write-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIWAWB 0x5UL /* Normal Inner Cacheable Write-allocate, Write-back. */ -#define GITS_CBASER_CACHE_NIRAWAWT 0x6UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-through. */ -#define GITS_CBASER_CACHE_NIRAWAWB 0x7UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-back. */ -#define GITS_CBASER_CACHE_MASK 0x7UL -#define GITS_CBASER_SHARE_NS 0x0UL /* Non-shareable. */ -#define GITS_CBASER_SHARE_IS 0x1UL /* Inner Shareable. */ -#define GITS_CBASER_SHARE_OS 0x2UL /* Outer Shareable. */ -#define GITS_CBASER_SHARE_RES 0x3UL /* Reserved. Treated as 0b00 */ -#define GITS_CBASER_SHARE_MASK 0x3UL +#define GIC_BASER_CACHE_DnGnRnE 0x0UL /* Device-nGnRnE. */ +#define GIC_BASER_CACHE_NIN 0x1UL /* Normal Inner Non-cacheable. */ +#define GIC_BASER_CACHE_NIRAWT 0x2UL /* Normal Inner Cacheable Read-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIRAWB 0x3UL /* Normal Inner Cacheable Read-allocate, Write-back. */ +#define GIC_BASER_CACHE_NIWAWT 0x4UL /* Normal Inner Cacheable Write-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIWAWB 0x5UL /* Normal Inner Cacheable Write-allocate, Write-back. */ +#define GIC_BASER_CACHE_NIRAWAWT 0x6UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-through. */ +#define GIC_BASER_CACHE_NIRAWAWB 0x7UL /* Normal Inner Cacheable Read-allocate, Write-allocate, Write-back. */ +#define GIC_BASER_CACHE_MASK 0x7UL +#define GIC_BASER_SHARE_NS 0x0UL /* Non-shareable. */ +#define GIC_BASER_SHARE_IS 0x1UL /* Inner Shareable. */ +#define GIC_BASER_SHARE_OS 0x2UL /* Outer Shareable. */ +#define GIC_BASER_SHARE_RES 0x3UL /* Reserved. Treated as 0b00 */ +#define GIC_BASER_SHARE_MASK 0x3UL + +#define GITS_BASER_InnerShareable GITS_BASER_SHAREABILITY(GITS_BASER, SHARE_IS) +#define GITS_BASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GITS_BASER, SHARE_MASK) +#define GITS_BASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GITS_BASER, INNER, MASK) +#define GITS_BASER_nCnB GITS_BASER_CACHEABILITY(GITS_BASER, INNER, DnGnRnE) +#define GITS_BASER_nC GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIN) +#define GITS_BASER_RaWt GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWT) +#define GITS_BASER_RaWb GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWB) +#define GITS_BASER_WaWt GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIWAWT) +#define GITS_BASER_WaWb GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIWAWB) +#define GITS_BASER_RaWaWt GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWAWT) +#define GITS_BASER_RaWaWb GITS_BASER_CACHEABILITY(GITS_BASER, INNER, NIRAWAWB) #define GITS_CBASER_InnerShareable GITS_BASER_SHAREABILITY(GITS_CBASER, SHARE_IS) #define GITS_CBASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GITS_CBASER, SHARE_MASK) +#define GITS_CBASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, MASK) #define GITS_CBASER_nCnB GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, DnGnRnE) #define GITS_CBASER_nC GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIN) #define GITS_CBASER_RaWt GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWT) @@ -221,6 +299,18 @@ #define GITS_CBASER_RaWaWt GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWAWT) #define GITS_CBASER_RaWaWb GITS_BASER_CACHEABILITY(GITS_CBASER, INNER, NIRAWAWB) +#define GICR_PBASER_InnerShareable GITS_BASER_SHAREABILITY(GICR_PBASER, SHARE_IS) +#define GICR_PBASER_SHARE_MASK_ALL GITS_BASER_SHAREABILITY(GICR_PBASER, SHARE_MASK) +#define GICR_PBASER_INNER_MASK_ALL GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, MASK) +#define GICR_PBASER_nCnB GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, DnGnRnE) +#define GICR_PBASER_nC GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIN) +#define GICR_PBASER_RaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWT) +#define GICR_PBASER_RaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWB) +#define GICR_PBASER_WaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIWAWT) +#define GICR_PBASER_WaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIWAWB) +#define GICR_PBASER_RaWaWt GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWAWT) +#define GICR_PBASER_RaWaWb GITS_BASER_CACHEABILITY(GICR_PBASER, INNER, NIRAWAWB) + #define GIC_EPPI_BASE_INTID 1056 #define GIC_ESPI_BASE_INTID 4096 @@ -283,6 +373,7 @@ struct gicv3 rt_size_t dist_size; void *redist_percpu_base[RT_CPUS_NR]; + rt_uint64_t redist_percpu_flags[RT_CPUS_NR]; rt_size_t percpu_ppi_nr[RT_CPUS_NR]; struct @@ -296,4 +387,4 @@ struct gicv3 rt_size_t redist_regions_nr; }; -#endif /* __IRQ_GICV3_H__ */ +#endif /* __PIC_GICV3_H__ */