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