[PATCH 08/11] riscv: p8700: Add Coherence Manager (CM) and IOCU support
Uros Stajic
uros.stajic at htecgroup.com
Tue Jun 3 15:40:24 CEST 2025
From: Chao-ying Fu <cfu at mips.com>
Add support for Coherence Manager (CM) and IOCU discovery and
configuration on the P8700 platform.
Signed-off-by: Chao-ying Fu <cfu at mips.com>
Signed-off-by: Uros Stajic <uros.stajic at htecgroup.com>
---
arch/riscv/cpu/p8700/Makefile | 2 +
arch/riscv/cpu/p8700/cache.c | 10 +++
arch/riscv/cpu/p8700/cm-iocu.c | 75 ++++++++++++++++
arch/riscv/cpu/p8700/cm.c | 92 +++++++++++++++++++
arch/riscv/include/asm/arch-p8700/cm.h | 61 +++++++++++++
arch/riscv/include/asm/arch-p8700/p8700.h | 31 +++++++
arch/riscv/include/asm/global_data.h | 2 +
arch/riscv/include/asm/io.h | 86 ++++++++++++++++++
board/mips/boston-riscv/Makefile | 1 +
board/mips/boston-riscv/iocu.c | 104 ++++++++++++++++++++++
10 files changed, 464 insertions(+)
create mode 100644 arch/riscv/cpu/p8700/cm-iocu.c
create mode 100644 arch/riscv/cpu/p8700/cm.c
create mode 100644 arch/riscv/include/asm/arch-p8700/cm.h
create mode 100644 board/mips/boston-riscv/iocu.c
diff --git a/arch/riscv/cpu/p8700/Makefile b/arch/riscv/cpu/p8700/Makefile
index f9fcd20e2ab..3c93a703d84 100644
--- a/arch/riscv/cpu/p8700/Makefile
+++ b/arch/riscv/cpu/p8700/Makefile
@@ -6,3 +6,5 @@ obj-y += dram.o
obj-y += cpu.o
obj-y += cache.o
obj-y += emu-amo.o
+obj-y += cm.o
+obj-y += cm-iocu.o
diff --git a/arch/riscv/cpu/p8700/cache.c b/arch/riscv/cpu/p8700/cache.c
index 27641035d80..e9297d9881b 100644
--- a/arch/riscv/cpu/p8700/cache.c
+++ b/arch/riscv/cpu/p8700/cache.c
@@ -37,6 +37,11 @@ static void probe_cache_config(void)
void flush_dcache_range(unsigned long start, unsigned long end)
{
+ DECLARE_GLOBAL_DATA_PTR;
+
+ if (gd->flags & GD_FLG_COHERENT_DMA)
+ return;
+
if (lsize == 0)
probe_cache_config();
@@ -56,6 +61,11 @@ void flush_dcache_range(unsigned long start, unsigned long end)
void invalidate_dcache_range(unsigned long start, unsigned long end)
{
+ DECLARE_GLOBAL_DATA_PTR;
+
+ if (gd->flags & GD_FLG_COHERENT_DMA)
+ return;
+
if (lsize == 0)
probe_cache_config();
diff --git a/arch/riscv/cpu/p8700/cm-iocu.c b/arch/riscv/cpu/p8700/cm-iocu.c
new file mode 100644
index 00000000000..5850647d20f
--- /dev/null
+++ b/arch/riscv/cpu/p8700/cm-iocu.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MIPS Coherence Manager (CM) Support
+ *
+ * Copyright (c) 2016 Imagination Technologies Ltd.
+ */
+#include <asm/io.h>
+#include <asm/global_data.h>
+#include <asm/arch-p8700/cm.h>
+
+__weak bool plat_iocu_usable(unsigned int cluster, unsigned int iocu)
+{
+ return true;
+}
+
+static int init_cluster_iocus(unsigned int cluster)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ void __iomem *global_gcrs;
+ u32 cfg, num_iocus, num_iocus_usable, local_cluster;
+ int i;
+
+ local_cluster = mips_cluster_id();
+
+ global_gcrs = mips_cm_base();
+ if (cluster != local_cluster) {
+ // FIXME
+ return 1;
+ }
+
+ cfg = __raw_readl(global_gcrs + GCR_CONFIG);
+ num_iocus = cfg >> GCR_CONFIG_NUMIOCU_SHIFT;
+ num_iocus &= GCR_CONFIG_NUMIOCU_MASK;
+ gd->arch.num_iocus += num_iocus;
+
+ /* Discover how many IOCUs are usable */
+ num_iocus_usable = num_iocus;
+ for (i = num_iocus - 1; i >= 0; i--) {
+ if (!plat_iocu_usable(cluster, i))
+ num_iocus_usable--;
+ }
+ gd->arch.num_iocus_usable += num_iocus_usable;
+
+ /* If the cluster has no usable IOCUs there's nothing to do */
+ if (num_iocus_usable == 0) {
+ if (cluster != local_cluster)
+ power_down_cluster(cluster);
+
+ return 0;
+ }
+
+ /* If the IOCUs are in the local cluster we're good to go already */
+ if (cluster == local_cluster)
+ return 0;
+
+ /* Ensure that the cluster's L2 cache is initialised */
+ return init_cluster_l2(cluster);
+}
+
+int mips_cm_init_iocus(void)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ unsigned int cluster;
+ int err;
+
+ for (cluster = 0; cluster < mips_cm_num_clusters(); cluster++) {
+ err = init_cluster_iocus(cluster);
+ if (err) {
+ gd->arch.num_iocus_usable = err;
+ return 0;
+ }
+ }
+
+ return 0;
+}
diff --git a/arch/riscv/cpu/p8700/cm.c b/arch/riscv/cpu/p8700/cm.c
new file mode 100644
index 00000000000..ce7485c564d
--- /dev/null
+++ b/arch/riscv/cpu/p8700/cm.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MIPS Coherence Manager (CM) Support
+ *
+ * Copyright (c) 2016 Imagination Technologies Ltd.
+ */
+
+#include <asm/io.h>
+#include <asm/arch-p8700/cm.h>
+#include <asm/arch-p8700/p8700.h>
+
+static void mips_cpc_init(void)
+{
+}
+
+__weak const struct mmio_region *get_mmio_regions(void)
+{
+ return NULL;
+}
+
+static void p8700_setup_mmio_limits(void)
+{
+ void __iomem *gcrs = mips_cm_base();
+ const struct mmio_region *rgn = get_mmio_regions();
+ unsigned int num_clusters = mips_cm_num_clusters();
+ unsigned int limit = MIPS_CM_MMIO_LIMIT / num_clusters;
+ unsigned int i, reg_off;
+
+ if (!rgn)
+ return;
+
+ if (num_clusters != 1) {
+ // FIXME! Need to support multiple clusters.
+ return;
+ }
+
+ reg_off = GCR_MMIO0_BOTTOM;
+
+ for (i = 0; rgn[i].addr_high; i++) {
+ __raw_writeq(rgn[i].addr_high & GCR_MMIO0_TOP_ADDR,
+ gcrs + reg_off + (GCR_MMIO0_TOP - GCR_MMIO0_BOTTOM));
+
+ __raw_writeq((rgn[i].addr_low & GCR_MMIO0_BOTTOM_ADDR) |
+ (rgn[i].port << GCR_MMIO0_BOTTOM_PORT_SHIFT) |
+ (rgn[i].enable ? GCR_MMIO0_BOTTOM_ENABLE : 0),
+ gcrs + reg_off);
+ reg_off += GCR_MMIO1_BOTTOM - GCR_MMIO0_BOTTOM;
+ }
+
+ __raw_writel(limit, gcrs + GCR_MMIO_REQ_LIMIT);
+}
+
+int power_up_cluster(unsigned int cluster)
+{
+ return 0;
+}
+
+int power_down_cluster(unsigned int cluster)
+{
+ return 0;
+}
+
+int init_cluster_l2(unsigned int cluster)
+{
+ return 0;
+}
+
+int mips_cm_init(void)
+{
+ int err;
+
+ mips_cpc_init();
+
+ err = mips_cm_init_iocus();
+ if (err)
+ return err;
+
+ p8700_setup_mmio_limits();
+
+ return 0;
+}
+
+int arch_cpu_init(void)
+{
+ int err;
+
+ err = mips_cm_init();
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/arch/riscv/include/asm/arch-p8700/cm.h b/arch/riscv/include/asm/arch-p8700/cm.h
new file mode 100644
index 00000000000..ab5ea7385b2
--- /dev/null
+++ b/arch/riscv/include/asm/arch-p8700/cm.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021, Chao-ying Fu <cfu at mips.com>
+ */
+
+#ifndef __P8700_CM_H__
+#define __P8700_CM_H__
+
+#include <asm/arch-p8700/p8700.h>
+
+struct mmio_region {
+ phys_addr_t addr_low;
+ phys_addr_t addr_high;
+ unsigned int port : 4;
+ unsigned int enable : 1;
+};
+
+const struct mmio_region *get_mmio_regions(void);
+
+void setup_redirect(unsigned int cluster, unsigned int core,
+ unsigned int vp, unsigned int block);
+
+int mips_cm_init_iocus(void);
+
+int power_up_cluster(unsigned int cluster);
+int power_down_cluster(unsigned int cluster);
+int init_cluster_l2(unsigned int cluster);
+
+static inline void *mips_cm_base(void)
+{
+ return (void *)CM_BASE;
+}
+
+static inline void *mips_cpc_base(void)
+{
+ return (void *)CPC_BASE;
+}
+
+static inline unsigned int mips_cm_num_clusters(void)
+{
+ u32 cfg;
+
+ cfg = __raw_readl(mips_cm_base() + GCR_CONFIG);
+ cfg >>= GCR_CONFIG_NUMCLUSTERS_SHIFT;
+ cfg &= GCR_CONFIG_NUMCLUSTERS_MASK;
+
+ return cfg;
+}
+
+static inline unsigned int mips_cluster_id(void)
+{
+ u32 temp;
+
+ asm volatile("csrr %0, mhartid" : "=r"(temp));
+ temp >>= MHARTID_CLUSTER_SHIFT;
+ temp &= MHARTID_CLUSTER_MASK;
+
+ return temp;
+}
+
+#endif /* __P8700_CM_H__ */
diff --git a/arch/riscv/include/asm/arch-p8700/p8700.h b/arch/riscv/include/asm/arch-p8700/p8700.h
index 5ca9b4b9497..6c47de9a633 100644
--- a/arch/riscv/include/asm/arch-p8700/p8700.h
+++ b/arch/riscv/include/asm/arch-p8700/p8700.h
@@ -65,6 +65,10 @@
#define CM_BASE 0x16100000
#define CPC_BASE (CM_BASE + 0x8000)
+/* Block offsets */
+#define GCR_OFF_GLOBAL 0x0000
+#define GCR_OFF_LOCAL 0x2000
+
/* CPC Block offsets */
#define CPC_OFF_LOCAL 0x2000
@@ -75,6 +79,33 @@
#define CPC_Cx_CMD 0x0000
#define CPC_Cx_CMD_RESET 0x4
+/* GCR_CONFIG */
+#define GCR_CONFIG 0x0000
+#define GCR_REV 0x0030
+#define GCR_CONFIG_NUMCLUSTERS_SHIFT 23
+#define GCR_CONFIG_NUMCLUSTERS_MASK 0x7f
+#define GCR_CONFIG_NUMIOCU_SHIFT 8
+#define GCR_CONFIG_NUMIOCU_MASK 0xf
+#define GCR_CONFIG_NUMCORES_SHIFT 0
+#define GCR_CONFIG_NUMCORES_MASK 0xff
+
+/* GCR_REV CM versions */
+#define GCR_REV_CM3 0x0800
+#define GCR_REV_CM3_5 0x0900
+
+#define GCR_MMIO_REQ_LIMIT 0x06f8
+#define GCR_MMIO0_BOTTOM 0x0700
+#define GCR_MMIO0_BOTTOM_ADDR (0xffffffffull << 16)
+#define GCR_MMIO0_BOTTOM_PORT_SHIFT 2
+#define GCR_MMIO0_BOTTOM_PORT (0xf << 2)
+#define GCR_MMIO0_BOTTOM_DISABLE_LIMIT (0x1 << 1)
+#define GCR_MMIO0_BOTTOM_ENABLE (0x1 << 0)
+#define GCR_MMIO0_TOP 0x0708
+#define GCR_MMIO0_TOP_ADDR (0xffffffffull << 16)
+#define GCR_MMIO1_BOTTOM 0x0710
+
+#define MIPS_CM_MMIO_LIMIT 4
+
#define P8700_GCR_C0_COH_EN 0x20f8
#define P8700_GCR_C1_COH_EN 0x21f8
#define P8700_GCR_C2_COH_EN 0x22f8
diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
index 47b5e2cfc8f..7dbd164f154 100644
--- a/arch/riscv/include/asm/global_data.h
+++ b/arch/riscv/include/asm/global_data.h
@@ -44,6 +44,8 @@ struct arch_global_data {
ulong smbios_start; /* Start address of SMBIOS table */
#endif
struct resume_data *resume;
+ int num_iocus;
+ int num_iocus_usable;
};
#include <asm-generic/global_data.h>
diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h
index da165858034..fb79d0a8d32 100644
--- a/arch/riscv/include/asm/io.h
+++ b/arch/riscv/include/asm/io.h
@@ -15,6 +15,91 @@ static inline void sync(void)
{
}
+/*
+ * Generic virtual read/write. Note that we don't support half-word
+ * read/writes. We define __arch_*[bl] here, and leave __arch_*w
+ * to the architecture specific code.
+ */
+
+#if CONFIG_P8700_RISCV
+#ifdef CONFIG_ARCH_MAP_SYSMEM
+static inline void *map_sysmem(phys_addr_t paddr, unsigned long len)
+{
+ if (paddr < PHYS_SDRAM_0_SIZE + PHYS_SDRAM_1_SIZE)
+ paddr = paddr | 0x40000000;
+ return (void *)(uintptr_t)paddr;
+}
+
+static inline void *unmap_sysmem(const void *vaddr)
+{
+ phys_addr_t paddr = (phys_addr_t)vaddr;
+
+ paddr = paddr & ~0x40000000;
+ return (void *)(uintptr_t)paddr;
+}
+
+static inline phys_addr_t map_to_sysmem(const void *ptr)
+{
+ return (phys_addr_t)(uintptr_t)ptr;
+}
+#endif
+
+static inline unsigned char __arch_getb(const volatile void __iomem *mem)
+{
+ unsigned char value;
+
+ asm volatile("lbu %0,0(%1)" : "=r"(value) : "r"(mem));
+
+ return value;
+}
+
+static inline unsigned short __arch_getw(const volatile void __iomem *mem)
+{
+ unsigned short value;
+
+ asm volatile("lhu %0,0(%1)" : "=r"(value) : "r"(mem));
+
+ return value;
+}
+
+static inline unsigned int __arch_getl(const volatile void __iomem *mem)
+{
+ unsigned int value;
+
+ asm volatile("lw %0,0(%1)" : "=r"(value) : "r"(mem));
+
+ return value;
+}
+
+static inline unsigned long long __arch_getq(const volatile void __iomem *mem)
+{
+ unsigned long long value;
+
+ asm volatile("ld %0,0(%1)" : "=r"(value) : "r"(mem));
+
+ return value;
+}
+
+static inline void __arch_putb(unsigned char value, volatile void __iomem *mem)
+{
+ asm volatile("sb %0,0(%1)"::"r"(value), "r"(mem));
+}
+
+static inline void __arch_putw(unsigned short value, volatile void __iomem *mem)
+{
+ asm volatile("sh %0,0(%1)"::"r"(value), "r"(mem));
+}
+
+static inline void __arch_putl(unsigned int value, volatile void __iomem *mem)
+{
+ asm volatile("sw %0,0(%1)"::"r"(value), "r"(mem));
+}
+
+static inline void __arch_putq(unsigned int value, volatile void __iomem *mem)
+{
+ asm volatile("sd %0,0(%1)"::"r"(value), "r"(mem));
+}
+#else
#define __arch_getb(a) (*(volatile unsigned char *)(a))
#define __arch_getw(a) (*(volatile unsigned short *)(a))
#define __arch_getl(a) (*(volatile unsigned int *)(a))
@@ -24,6 +109,7 @@ static inline void sync(void)
#define __arch_putw(v, a) (*(volatile unsigned short *)(a) = (v))
#define __arch_putl(v, a) (*(volatile unsigned int *)(a) = (v))
#define __arch_putq(v, a) (*(volatile unsigned long long *)(a) = (v))
+#endif
#define __raw_writeb(v, a) __arch_putb(v, a)
#define __raw_writew(v, a) __arch_putw(v, a)
diff --git a/board/mips/boston-riscv/Makefile b/board/mips/boston-riscv/Makefile
index 9032e2ad6bb..007f68287bd 100644
--- a/board/mips/boston-riscv/Makefile
+++ b/board/mips/boston-riscv/Makefile
@@ -5,4 +5,5 @@
obj-y += checkboard.o
obj-y += lowlevel_init.o
obj-y += boston-riscv.o
+obj-y += iocu.o
obj-y += reset.o
diff --git a/board/mips/boston-riscv/iocu.c b/board/mips/boston-riscv/iocu.c
new file mode 100644
index 00000000000..8478670f350
--- /dev/null
+++ b/board/mips/boston-riscv/iocu.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 Imagination Technologies Ltd.
+ */
+
+#include <dm.h>
+#include <env_callback.h>
+#include <asm/io.h>
+#include <asm/arch-p8700/cm.h>
+#include "boston-regs.h"
+
+static const struct mmio_region mmio_regions[] = {
+ { 0x10000000, 0x160f0000, .enable = 1 },
+ { 0x17ff0000, 0x17ff0000, .enable = 1 },
+ { 0 },
+};
+
+const struct mmio_region *get_mmio_regions(void)
+{
+ return mmio_regions;
+}
+
+static int set_io_coherent(bool coherent)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+
+ if (!coherent) {
+ printf("I/O: Non-Coherent (Forced by environment)\n");
+ goto noncoherent;
+ }
+
+ if (gd->arch.num_iocus_usable < 0) {
+ printf("I/O: Non-Coherent (IOCU init error %d)\n",
+ gd->arch.num_iocus_usable);
+ goto noncoherent;
+ }
+
+ if (gd->arch.num_iocus == 0) {
+ printf("I/O: Non-Coherent (No IOCU)\n");
+ goto noncoherent;
+ }
+
+ if (gd->arch.num_iocus_usable == 0) {
+ printf("I/O: Non-Coherent (IOCU not connected)\n");
+ goto noncoherent;
+ }
+
+ /*
+ * We have some number of connected IOCUs. Map all PCIe DMA access to
+ * hit the IOCU by offsetting the addresses as they pass from the PCIe
+ * controller to the NoC.
+ */
+ writel(0x10, (u32 *)BOSTON_PLAT_NOCPCIE0ADDR);
+ writel(0x10, (u32 *)BOSTON_PLAT_NOCPCIE1ADDR);
+ writel(0x10, (u32 *)BOSTON_PLAT_NOCPCIE2ADDR);
+
+ /* Record that I/O is coherent */
+ gd->flags |= GD_FLG_COHERENT_DMA;
+
+ printf("I/O: Coherent\n");
+ return 0;
+
+noncoherent:
+ /* Map all PCIe DMA access to its default, non-IOCU, target */
+ writel(0x00, (u32 *)BOSTON_PLAT_NOCPCIE0ADDR);
+ writel(0x00, (u32 *)BOSTON_PLAT_NOCPCIE1ADDR);
+ writel(0x00, (u32 *)BOSTON_PLAT_NOCPCIE2ADDR);
+
+ /* Record that I/O is not coherent */
+ gd->flags &= ~GD_FLG_COHERENT_DMA;
+ return 0;
+}
+
+static int on_io_coherent(const char *name, const char *value,
+ enum env_op op, int flags)
+{
+ switch (op) {
+ case env_op_create:
+ case env_op_overwrite:
+ if (!strcmp(value, "0")) {
+ set_io_coherent(false);
+ } else if (!strcmp(value, "1")) {
+ set_io_coherent(true);
+ } else {
+ printf("### io.coherent must equal 0 or 1\n");
+ return -EINVAL;
+ }
+ return 0;
+
+ case env_op_delete:
+ set_io_coherent(true);
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+U_BOOT_ENV_CALLBACK(io_coherent, on_io_coherent);
+
+int misc_init_f(void)
+{
+ return set_io_coherent(env_get_yesno("io.coherent") != 0);
+}
--
2.34.1
More information about the U-Boot
mailing list