forked from Mirrors/opensbi
		
	lib: utils/ipi: Add Andes fdt ipi driver support
Move Andes PLICSW ipi device to fdt ipi framework, this patch is based
on Leo's modified IPI scheme on PLICSW.
Current IPI scheme uses bit 0 of pending reigster on PLICSW to send IPI
from hart 0 to hart 7, but bit 0 needs to be hardwired to 0 according
to spec. After some investigation, self-IPI seems to be seldom or never
used, so we re-order the IPI scheme to support 8 core platforms.
dts example (Quad-core AX45MP):
  plicsw: interrupt-controller@e6400000 {
          compatible = "andestech,plicsw";
          reg = <0x00000000 0xe6400000 0x00000000 0x00400000>;
          interrupts-extended = <&CPU0_intc 3
                                 &CPU1_intc 3
                                 &CPU2_intc 3
                                 &CPU3_intc 3>;
          interrupt-controller;
          #address-cells = <2>;
          #interrupt-cells = <2>;
  };
Signed-off-by: Yu Chien Peter Lin <peterlin@andestech.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
			
			
This commit is contained in:
		
				
					committed by
					
						
						Anup Patel
					
				
			
			
				
	
			
			
			
						parent
						
							6f3258e671
						
					
				
				
					commit
					ce7c490719
				
			@@ -889,6 +889,60 @@ int fdt_parse_plmt_node(void *fdt, int nodeoffset, unsigned long *plmt_base,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int fdt_parse_plicsw_node(void *fdt, int nodeoffset, unsigned long *plicsw_base,
 | 
			
		||||
			  unsigned long *size, u32 *hart_count)
 | 
			
		||||
{
 | 
			
		||||
	const fdt32_t *val;
 | 
			
		||||
	int rc, i, count;
 | 
			
		||||
	uint64_t reg_addr, reg_size, cpu_offset, cpu_intc_offset;
 | 
			
		||||
	u32 phandle, hwirq, hartid, hcount;
 | 
			
		||||
 | 
			
		||||
	if (nodeoffset < 0 || !fdt || !plicsw_base ||
 | 
			
		||||
	    !hart_count || !size)
 | 
			
		||||
		return SBI_EINVAL;
 | 
			
		||||
 | 
			
		||||
	rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
 | 
			
		||||
				    ®_addr, ®_size);
 | 
			
		||||
	if (rc < 0 || !plicsw_base || !size)
 | 
			
		||||
		return SBI_ENODEV;
 | 
			
		||||
	*plicsw_base = reg_addr;
 | 
			
		||||
	*size = reg_size;
 | 
			
		||||
 | 
			
		||||
	val = fdt_getprop(fdt, nodeoffset, "interrupts-extended", &count);
 | 
			
		||||
	if (!val || count < sizeof(fdt32_t))
 | 
			
		||||
		return 0;
 | 
			
		||||
	count = count / sizeof(fdt32_t);
 | 
			
		||||
 | 
			
		||||
	hcount = 0;
 | 
			
		||||
	for (i = 0; i < (count / 2); i++) {
 | 
			
		||||
		phandle = fdt32_to_cpu(val[2 * i]);
 | 
			
		||||
		hwirq = fdt32_to_cpu(val[2 * i + 1]);
 | 
			
		||||
 | 
			
		||||
		cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
 | 
			
		||||
		if (cpu_intc_offset < 0)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset);
 | 
			
		||||
		if (cpu_intc_offset < 0)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
 | 
			
		||||
 | 
			
		||||
		if (rc)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (SBI_HARTMASK_MAX_BITS <= hartid)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (hwirq == IRQ_M_SOFT)
 | 
			
		||||
			hcount++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*hart_count = hcount;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int fdt_parse_compat_addr(void *fdt, uint64_t *addr,
 | 
			
		||||
			  const char *compatible)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -14,10 +14,19 @@ config FDT_IPI_MSWI
 | 
			
		||||
	select IPI_MSWI
 | 
			
		||||
	default n
 | 
			
		||||
 | 
			
		||||
config FDT_IPI_PLICSW
 | 
			
		||||
	bool "Andes PLICSW FDT driver"
 | 
			
		||||
	select IPI_PLICSW
 | 
			
		||||
	default n
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
config IPI_MSWI
 | 
			
		||||
	bool "ACLINT MSWI support"
 | 
			
		||||
	default n
 | 
			
		||||
 | 
			
		||||
config IPI_PLICSW
 | 
			
		||||
	bool "Andes PLICSW support"
 | 
			
		||||
	default n
 | 
			
		||||
 | 
			
		||||
endmenu
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								lib/utils/ipi/andes_plicsw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								lib/utils/ipi/andes_plicsw.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2022 Andes Technology Corporation
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Zong Li <zong@andestech.com>
 | 
			
		||||
 *   Nylon Chen <nylon7@andestech.com>
 | 
			
		||||
 *   Leo Yu-Chi Liang <ycliang@andestech.com>
 | 
			
		||||
 *   Yu Chien Peter Lin <peterlin@andestech.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_io.h>
 | 
			
		||||
#include <sbi/sbi_domain.h>
 | 
			
		||||
#include <sbi/sbi_ipi.h>
 | 
			
		||||
#include <sbi_utils/ipi/andes_plicsw.h>
 | 
			
		||||
 | 
			
		||||
struct plicsw_data plicsw;
 | 
			
		||||
 | 
			
		||||
static inline void plicsw_claim(void)
 | 
			
		||||
{
 | 
			
		||||
	u32 hartid = current_hartid();
 | 
			
		||||
 | 
			
		||||
	if (plicsw.hart_count <= hartid)
 | 
			
		||||
		ebreak();
 | 
			
		||||
 | 
			
		||||
	plicsw.source_id[hartid] =
 | 
			
		||||
		readl((void *)plicsw.addr + PLICSW_CONTEXT_BASE +
 | 
			
		||||
		      PLICSW_CONTEXT_CLAIM + PLICSW_CONTEXT_STRIDE * hartid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void plicsw_complete(void)
 | 
			
		||||
{
 | 
			
		||||
	u32 hartid = current_hartid();
 | 
			
		||||
	u32 source = plicsw.source_id[hartid];
 | 
			
		||||
 | 
			
		||||
	writel(source, (void *)plicsw.addr + PLICSW_CONTEXT_BASE +
 | 
			
		||||
			       PLICSW_CONTEXT_CLAIM +
 | 
			
		||||
			       PLICSW_CONTEXT_STRIDE * hartid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void plic_sw_pending(u32 target_hart)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * The pending array registers are w1s type.
 | 
			
		||||
	 * IPI pending array mapping as following:
 | 
			
		||||
	 *
 | 
			
		||||
	 * Pending array start address: base + 0x1000
 | 
			
		||||
	 * ---------------------------------
 | 
			
		||||
	 * | hart3 | hart2 | hart1 | hart0 |
 | 
			
		||||
	 * ---------------------------------
 | 
			
		||||
	 * Each hartX can send IPI to another hart by setting the
 | 
			
		||||
	 * bitY to its own region (see the below).
 | 
			
		||||
	 *
 | 
			
		||||
	 * In each hartX region:
 | 
			
		||||
	 * <---------- PICSW_PENDING_STRIDE -------->
 | 
			
		||||
	 * | bit7 | ... | bit3 | bit2 | bit1 | bit0 |
 | 
			
		||||
	 * ------------------------------------------
 | 
			
		||||
	 * The bitY of hartX region indicates that hartX sends an
 | 
			
		||||
	 * IPI to hartY.
 | 
			
		||||
	 */
 | 
			
		||||
	u32 hartid	    = current_hartid();
 | 
			
		||||
	u32 word_index	    = hartid / 4;
 | 
			
		||||
	u32 per_hart_offset = PLICSW_PENDING_STRIDE * hartid;
 | 
			
		||||
	u32 val		    = 1 << target_hart << per_hart_offset;
 | 
			
		||||
 | 
			
		||||
	writel(val, (void *)plicsw.addr + PLICSW_PENDING_BASE + word_index * 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void plicsw_ipi_send(u32 target_hart)
 | 
			
		||||
{
 | 
			
		||||
	if (plicsw.hart_count <= target_hart)
 | 
			
		||||
		ebreak();
 | 
			
		||||
 | 
			
		||||
	/* Set PLICSW IPI */
 | 
			
		||||
	plic_sw_pending(target_hart);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void plicsw_ipi_clear(u32 target_hart)
 | 
			
		||||
{
 | 
			
		||||
	if (plicsw.hart_count <= target_hart)
 | 
			
		||||
		ebreak();
 | 
			
		||||
 | 
			
		||||
	/* Clear PLICSW IPI */
 | 
			
		||||
	plicsw_claim();
 | 
			
		||||
	plicsw_complete();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct sbi_ipi_device plicsw_ipi = {
 | 
			
		||||
	.name      = "andes_plicsw",
 | 
			
		||||
	.ipi_send  = plicsw_ipi_send,
 | 
			
		||||
	.ipi_clear = plicsw_ipi_clear
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int plicsw_warm_ipi_init(void)
 | 
			
		||||
{
 | 
			
		||||
	u32 hartid = current_hartid();
 | 
			
		||||
 | 
			
		||||
	/* Clear PLICSW IPI */
 | 
			
		||||
	plicsw_ipi_clear(hartid);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int plicsw_cold_ipi_init(struct plicsw_data *plicsw)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	/* Setup source priority */
 | 
			
		||||
	uint32_t *priority = (void *)plicsw->addr + PLICSW_PRIORITY_BASE;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < plicsw->hart_count; i++)
 | 
			
		||||
		writel(1, &priority[i]);
 | 
			
		||||
 | 
			
		||||
	/* Setup target enable */
 | 
			
		||||
	uint32_t enable_mask = PLICSW_HART_MASK;
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < plicsw->hart_count; i++) {
 | 
			
		||||
		uint32_t *enable = (void *)plicsw->addr + PLICSW_ENABLE_BASE +
 | 
			
		||||
				   PLICSW_ENABLE_STRIDE * i;
 | 
			
		||||
		writel(enable_mask, enable);
 | 
			
		||||
		writel(enable_mask, enable + 1);
 | 
			
		||||
		enable_mask <<= 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Add PLICSW region to the root domain */
 | 
			
		||||
	rc = sbi_domain_root_add_memrange(plicsw->addr, plicsw->size,
 | 
			
		||||
					  PLICSW_REGION_ALIGN,
 | 
			
		||||
					  SBI_DOMAIN_MEMREGION_MMIO);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	sbi_ipi_set_device(&plicsw_ipi);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								lib/utils/ipi/fdt_ipi_plicsw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								lib/utils/ipi/fdt_ipi_plicsw.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2022 Andes Technology Corporation
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Zong Li <zong@andestech.com>
 | 
			
		||||
 *   Nylon Chen <nylon7@andestech.com>
 | 
			
		||||
 *   Leo Yu-Chi Liang <ycliang@andestech.com>
 | 
			
		||||
 *   Yu Chien Peter Lin <peterlin@andestech.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_io.h>
 | 
			
		||||
#include <sbi_utils/fdt/fdt_helper.h>
 | 
			
		||||
#include <sbi_utils/ipi/fdt_ipi.h>
 | 
			
		||||
#include <sbi_utils/ipi/andes_plicsw.h>
 | 
			
		||||
 | 
			
		||||
extern struct plicsw_data plicsw;
 | 
			
		||||
 | 
			
		||||
int fdt_plicsw_cold_ipi_init(void *fdt, int nodeoff,
 | 
			
		||||
				const struct fdt_match *match)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = fdt_parse_plicsw_node(fdt, nodeoff, &plicsw.addr, &plicsw.size,
 | 
			
		||||
				   &plicsw.hart_count);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	rc = plicsw_cold_ipi_init(&plicsw);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct fdt_match ipi_plicsw_match[] = {
 | 
			
		||||
	{ .compatible = "andestech,plicsw" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct fdt_ipi fdt_ipi_plicsw = {
 | 
			
		||||
	.match_table = ipi_plicsw_match,
 | 
			
		||||
	.cold_init   = fdt_plicsw_cold_ipi_init,
 | 
			
		||||
	.warm_init   = plicsw_warm_ipi_init,
 | 
			
		||||
	.exit	     = NULL,
 | 
			
		||||
};
 | 
			
		||||
@@ -8,9 +8,13 @@
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
libsbiutils-objs-$(CONFIG_IPI_MSWI) += ipi/aclint_mswi.o
 | 
			
		||||
libsbiutils-objs-$(CONFIG_IPI_PLICSW) += ipi/andes_plicsw.o
 | 
			
		||||
 | 
			
		||||
libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi.o
 | 
			
		||||
libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi_drivers.o
 | 
			
		||||
 | 
			
		||||
carray-fdt_ipi_drivers-$(CONFIG_FDT_IPI_MSWI) += fdt_ipi_mswi
 | 
			
		||||
libsbiutils-objs-$(CONFIG_FDT_IPI_MSWI) += ipi/fdt_ipi_mswi.o
 | 
			
		||||
 | 
			
		||||
carray-fdt_ipi_drivers-$(CONFIG_FDT_IPI_PLICSW) += fdt_ipi_plicsw
 | 
			
		||||
libsbiutils-objs-$(CONFIG_FDT_IPI_PLICSW) += ipi/fdt_ipi_plicsw.o
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user