[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v2 10/16] xen/riscv: imsic_init() implementation
imsic_init() is introduced to parse device tree node, which has the following bindings [2], and based on the parsed information update IMSIC configuration which is stored in imsic_cfg. The following helpers are introduces for imsic_init() usage: - imsic_parse_node() parses IMSIC node from DTS - imsic_get_parent_cpuid() returns the hart ( CPU ) ID of the given device tree node. This patch is based on the code from [1]. Since Microchip originally developed imsic.{c,h}, an internal discussion with them led to the decision to use the MIT license. [1] https://gitlab.com/xen-project/people/olkur/xen/-/commit/0b1a94f2bc3bb1a81cd26bb75f0bf578f84cb4d4 [2] https://elixir.bootlin.com/linux/v6.12/source/Documentation/devicetree/bindings/interrupt-controller/riscv,imsics.yaml Co-developed-by: Romain Caritey <Romain.Caritey@xxxxxxxxxxxxx> Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx> --- Changes in V2: - Drop years in copyrights. - s/riscv_of_processor_hartid/dt_processor_cpuid. - s/imsic_get_parent_hartid/imsic_get_parent_cpuid. Rename argument hartid to cpuid. Make node argument const. Return res instead of -EINVAL for the failure case of dt_processor_cpuid(). Drop local variable hart and use cpuid argument instead. Drop useless return res; - imsic_parse_node() changes: - Make node argument const. - Check the return value of dt_property_read_u32() directly instead of saving it to rc variable. - Update tmp usage, use short form "-=". - Update a check (imsic_cfg.nr_ids >= IMSIC_MAX_ID) to (imsic_cfg.nr_ids > IMSIC_MAX_ID) as IMSIC_MAX_ID is changed to maximum valid value, not just the firsr out-of-range. - Use `rc` to return value instead of explicitly use -EINVAL. - Use do {} while() to find number of MMIO register sets. - Set IMSIC_MAX_ID to 2047 (maximum possible IRQ number). - imsic_init() changes: - Use unsigned int in for's expression1. - s/xfree/XFEE. - Allocate msi and cpus array dynamically. - Drop forward declaration before declaration of imsic_get_config() in asm/imsic.h as it is not used as parameter type. - Align declaration of imisic_init with defintion. - s/harts/cpus in imisic_mmios. Also, change type from bool harts[NR_CPUS] to unsigned long *cpus. - Allocate msi member of imsic_config dynamically to save some memory. - Code style fixes. - Update the commit message. --- xen/arch/riscv/Makefile | 1 + xen/arch/riscv/imsic.c | 286 +++++++++++++++++++++++++++++ xen/arch/riscv/include/asm/imsic.h | 65 +++++++ 3 files changed, 352 insertions(+) create mode 100644 xen/arch/riscv/imsic.c create mode 100644 xen/arch/riscv/include/asm/imsic.h diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index a1c145c506..e2b8aa42c8 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -2,6 +2,7 @@ obj-y += aplic.o obj-y += cpufeature.o obj-$(CONFIG_EARLY_PRINTK) += early_printk.o obj-y += entry.o +obj-y += imsic.o obj-y += intc.o obj-y += irq.o obj-y += mm.o diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c new file mode 100644 index 0000000000..43d0c92cbd --- /dev/null +++ b/xen/arch/riscv/imsic.c @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: MIT */ + +/* + * xen/arch/riscv/imsic.c + * + * RISC-V Incoming MSI Controller support + * + * (c) Microchip Technology Inc. + * (c) Vates + */ + +#include <xen/const.h> +#include <xen/device_tree.h> +#include <xen/errno.h> +#include <xen/init.h> +#include <xen/macros.h> +#include <xen/xmalloc.h> + +#include <asm/imsic.h> + +static struct imsic_config imsic_cfg; + +/* Callers aren't expected to changed imsic_cfg so return const. */ +const struct imsic_config *imsic_get_config(void) +{ + return &imsic_cfg; +} + +static int __init imsic_get_parent_cpuid(const struct dt_device_node *node, + unsigned int index, + unsigned long *cpuid) +{ + int res; + struct dt_phandle_args args; + + res = dt_parse_phandle_with_args(node, "interrupts-extended", + "#interrupt-cells", index, &args); + if ( !res ) + res = dt_processor_cpuid(args.np->parent, cpuid); + + return res; +} + +static int imsic_parse_node(const struct dt_device_node *node, + unsigned int *nr_parent_irqs) +{ + int rc; + unsigned int tmp; + paddr_t base_addr; + + /* Find number of parent interrupts */ + *nr_parent_irqs = dt_number_of_irq(node); + if ( !*nr_parent_irqs ) + { + printk(XENLOG_ERR "%s: no parent irqs available\n", node->name); + return -ENOENT; + } + + if ( !dt_property_read_u32(node, "riscv,guest-index-bits", + &imsic_cfg.guest_index_bits) ) + imsic_cfg.guest_index_bits = 0; + tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT; + if ( tmp < imsic_cfg.guest_index_bits ) + { + printk(XENLOG_ERR "%s: guest index bits too big\n", node->name); + return -ENOENT; + } + + /* Find number of HART index bits */ + if ( !dt_property_read_u32(node, "riscv,hart-index-bits", + &imsic_cfg.hart_index_bits) ) + { + /* Assume default value */ + imsic_cfg.hart_index_bits = fls(*nr_parent_irqs); + if ( BIT(imsic_cfg.hart_index_bits, UL) < *nr_parent_irqs ) + imsic_cfg.hart_index_bits++; + } + tmp -= imsic_cfg.guest_index_bits; + if ( tmp < imsic_cfg.hart_index_bits ) + { + printk(XENLOG_ERR "%s: HART index bits too big\n", node->name); + return -ENOENT; + } + + /* Find number of group index bits */ + if ( !dt_property_read_u32(node, "riscv,group-index-bits", + &imsic_cfg.group_index_bits) ) + imsic_cfg.group_index_bits = 0; + tmp -= imsic_cfg.hart_index_bits; + if ( tmp < imsic_cfg.group_index_bits ) + { + printk(XENLOG_ERR "%s: group index bits too big\n", node->name); + return -ENOENT; + } + + /* Find first bit position of group index */ + tmp = IMSIC_MMIO_PAGE_SHIFT * 2; + if ( !dt_property_read_u32(node, "riscv,group-index-shift", + &imsic_cfg.group_index_shift) ) + imsic_cfg.group_index_shift = tmp; + if ( imsic_cfg.group_index_shift < tmp ) + { + printk(XENLOG_ERR "%s: group index shift too small\n", node->name); + return -ENOENT; + } + tmp = imsic_cfg.group_index_bits + imsic_cfg.group_index_shift - 1; + if ( tmp >= BITS_PER_LONG ) + { + printk(XENLOG_ERR "%s: group index shift too big\n", node->name); + return -EINVAL; + } + + /* Find number of interrupt identities */ + if ( !dt_property_read_u32(node, "riscv,num-ids", &imsic_cfg.nr_ids) ) + { + printk(XENLOG_ERR "%s: number of interrupt identities not found\n", + node->name); + return -ENOENT; + } + + if ( (imsic_cfg.nr_ids < IMSIC_MIN_ID) || + (imsic_cfg.nr_ids > IMSIC_MAX_ID) || + ((imsic_cfg.nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) ) + { + printk(XENLOG_ERR "%s: invalid number of interrupt identities\n", + node->name); + return -EINVAL; + } + + /* Compute base address */ + imsic_cfg.nr_mmios = 0; + rc = dt_device_get_address(node, imsic_cfg.nr_mmios, &base_addr, NULL); + if ( rc ) + { + printk(XENLOG_ERR "%s: first MMIO resource not found\n", node->name); + return rc; + } + + imsic_cfg.base_addr = base_addr; + imsic_cfg.base_addr &= ~(BIT(imsic_cfg.guest_index_bits + + imsic_cfg.hart_index_bits + + IMSIC_MMIO_PAGE_SHIFT, UL) - 1); + imsic_cfg.base_addr &= ~((BIT(imsic_cfg.group_index_bits, UL) - 1) << + imsic_cfg.group_index_shift); + + /* Find number of MMIO register sets */ + do { + imsic_cfg.nr_mmios++; + } while ( !dt_device_get_address(node, imsic_cfg.nr_mmios, &base_addr, NULL) ); + + return 0; +} + +int __init imsic_init(const struct dt_device_node *node) +{ + int rc; + unsigned long reloff, cpuid; + uint32_t nr_parent_irqs, index, nr_handlers = 0; + paddr_t base_addr; + unsigned int nr_mmios; + + /* Parse IMSIC node */ + rc = imsic_parse_node(node, &nr_parent_irqs); + if ( rc ) + return rc; + + nr_mmios = imsic_cfg.nr_mmios; + + /* Allocate MMIO resource array */ + imsic_cfg.mmios = xzalloc_array(struct imsic_mmios, nr_mmios); + if ( !imsic_cfg.mmios ) + return -ENOMEM; + + imsic_cfg.msi = xzalloc_array(struct imsic_msi, nr_parent_irqs); + if ( !imsic_cfg.msi ) + return -ENOMEM; + + /* Check MMIO register sets */ + for ( unsigned int i = 0; i < nr_mmios; i++ ) + { + imsic_cfg.mmios[i].cpus = xzalloc_array(unsigned long, + BITS_TO_LONGS(nr_parent_irqs)); + if ( !imsic_cfg.mmios[i].cpus ) + return -ENOMEM; + + rc = dt_device_get_address(node, i, &imsic_cfg.mmios[i].base_addr, + &imsic_cfg.mmios[i].size); + if ( rc ) + { + printk(XENLOG_ERR "%s: unable to parse MMIO regset %d\n", + node->name, i); + goto imsic_init_err; + } + + base_addr = imsic_cfg.mmios[i].base_addr; + base_addr &= ~(BIT(imsic_cfg.guest_index_bits + + imsic_cfg.hart_index_bits + + IMSIC_MMIO_PAGE_SHIFT, UL) - 1); + base_addr &= ~((BIT(imsic_cfg.group_index_bits, UL) - 1) << + imsic_cfg.group_index_shift); + if ( base_addr != imsic_cfg.base_addr ) + { + rc = -EINVAL; + printk(XENLOG_ERR "%s: address mismatch for regset %d\n", + node->name, i); + goto imsic_init_err; + } + } + + /* Configure handlers for target CPUs */ + for ( unsigned int i = 0; i < nr_parent_irqs; i++ ) + { + rc = imsic_get_parent_cpuid(node, i, &cpuid); + if ( rc ) + { + printk(XENLOG_WARNING "%s: cpu ID for parent irq%d not found\n", + node->name, i); + continue; + } + + if ( cpuid >= num_possible_cpus() ) + { + printk(XENLOG_WARNING "%s: unsupported cpu ID=%lu for parent irq%d\n", + node->name, cpuid, i); + continue; + } + + /* Find MMIO location of MSI page */ + index = nr_mmios; + reloff = i * BIT(imsic_cfg.guest_index_bits, UL) * IMSIC_MMIO_PAGE_SZ; + for ( unsigned int j = 0; nr_mmios; j++ ) + { + if ( reloff < imsic_cfg.mmios[j].size ) + { + index = j; + break; + } + + /* + * MMIO region size may not be aligned to + * BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ + * if holes are present. + */ + reloff -= ROUNDUP(imsic_cfg.mmios[j].size, + BIT(imsic_cfg.guest_index_bits, UL) * IMSIC_MMIO_PAGE_SZ); + } + + if ( index >= nr_mmios ) + { + printk(XENLOG_WARNING "%s: MMIO not found for parent irq%d\n", + node->name, i); + continue; + } + + if ( !IS_ALIGNED(imsic_cfg.msi[cpuid].base_addr + reloff, PAGE_SIZE) ) + { + printk(XENLOG_WARNING "%s: MMIO address 0x%lx is not aligned on a page\n", + node->name, imsic_cfg.msi[cpuid].base_addr + reloff); + imsic_cfg.msi[cpuid].offset = 0; + imsic_cfg.msi[cpuid].base_addr = 0; + continue; + } + + bitmap_set(imsic_cfg.mmios[index].cpus, cpuid, 1); + imsic_cfg.msi[cpuid].base_addr = imsic_cfg.mmios[index].base_addr; + imsic_cfg.msi[cpuid].offset = reloff; + nr_handlers++; + } + + if ( !nr_handlers ) + { + printk(XENLOG_ERR "%s: No CPU handlers found\n", node->name); + rc = -ENODEV; + goto imsic_init_err; + } + + return 0; + + imsic_init_err: + for ( unsigned int i = 0; i < nr_mmios; i++ ) + XFREE(imsic_cfg.mmios[i].cpus); + XFREE(imsic_cfg.mmios); + XFREE(imsic_cfg.msi); + + return rc; +} diff --git a/xen/arch/riscv/include/asm/imsic.h b/xen/arch/riscv/include/asm/imsic.h new file mode 100644 index 0000000000..ed51cac780 --- /dev/null +++ b/xen/arch/riscv/include/asm/imsic.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: MIT */ + +/* + * xen/arch/riscv/imsic.h + * + * RISC-V Incoming MSI Controller support + * + * (c) 2023 Microchip Technology Inc. + */ + +#ifndef ASM__RISCV__IMSIC_H +#define ASM__RISCV__IMSIC_H + +#include <xen/types.h> + +#define IMSIC_MMIO_PAGE_SHIFT 12 +#define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT) + +#define IMSIC_MIN_ID 63 +#define IMSIC_MAX_ID 2047 + +struct imsic_msi { + paddr_t base_addr; + unsigned long offset; +}; + +struct imsic_mmios { + paddr_t base_addr; + unsigned long size; + unsigned long *cpus; +}; + +struct imsic_config { + /* base address */ + paddr_t base_addr; + + /* Bits representing Guest index, HART index, and Group index */ + unsigned int guest_index_bits; + unsigned int hart_index_bits; + unsigned int group_index_bits; + unsigned int group_index_shift; + + /* imsic phandle */ + unsigned int phandle; + + /* number of parent irq */ + unsigned int nr_parent_irqs; + + /* number off interrupt identities */ + unsigned int nr_ids; + + /* mmios */ + unsigned int nr_mmios; + struct imsic_mmios *mmios; + + /* MSI */ + struct imsic_msi *msi; +}; + +struct dt_device_node; +int imsic_init(const struct dt_device_node *node); + +const struct imsic_config *imsic_get_config(void); + +#endif /* ASM__RISCV__IMSIC_H */ -- 2.49.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |