From: Chao-ying Fu <c...@mips.com>

Add support for Coherence Manager (CM) and IOCU discovery and
configuration on the P8700 platform.

Signed-off-by: Chao-ying Fu <c...@mips.com>
Signed-off-by: Uros Stajic <uros.sta...@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 <c...@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

Reply via email to