From 816226d27efa22d89821dfd0796f763a24c33944 Mon Sep 17 00:00:00 2001 From: Ovidiu Panait Date: Tue, 31 May 2022 21:14:35 +0300 Subject: [PATCH] cpu: add CPU driver for microblaze Add a basic CPU driver that retrieves information about the microblaze CPU core. cpu_ops handlers are implemented so that the "cpu" command can work properly: U-Boot-mONStR> cpu list 0: cpu@0 MicroBlaze @ 50MHz, Rev: 11.0, FPGA family: zynq7000 U-Boot-mONStR> cpu detail 0: cpu@0 MicroBlaze @ 50MHz, Rev: 11.0, FPGA family: zynq7000 ID = 0, freq = 50 MHz: L1 cache, MMU Note: cpu_ver_lookup[] and family_string_lookup[] arrays were imported from linux. Signed-off-by: Ovidiu Panait Link: https://lore.kernel.org/r/20220531181435.3473549-14-ovpanait@gmail.com Signed-off-by: Michal Simek --- arch/microblaze/cpu/cpuinfo.c | 111 +++++++++++++++++++++ arch/microblaze/include/asm/cpuinfo.h | 79 +++++++++++++++ drivers/cpu/Kconfig | 9 ++ drivers/cpu/Makefile | 1 + drivers/cpu/microblaze_cpu.c | 180 ++++++++++++++++++++++++++++++++++ 5 files changed, 380 insertions(+) create mode 100644 drivers/cpu/microblaze_cpu.c diff --git a/arch/microblaze/cpu/cpuinfo.c b/arch/microblaze/cpu/cpuinfo.c index 3f0b1d2..f021f4e 100644 --- a/arch/microblaze/cpu/cpuinfo.c +++ b/arch/microblaze/cpu/cpuinfo.c @@ -8,6 +8,117 @@ DECLARE_GLOBAL_DATA_PTR; +#if CONFIG_IS_ENABLED(CPU_MICROBLAZE) +/* These key value are as per MBV field in PVR0 */ +static const struct microblaze_version_map cpu_ver_lookup[] = { + {"5.00.a", 0x01}, + {"5.00.b", 0x02}, + {"5.00.c", 0x03}, + {"6.00.a", 0x04}, + {"6.00.b", 0x06}, + {"7.00.a", 0x05}, + {"7.00.b", 0x07}, + {"7.10.a", 0x08}, + {"7.10.b", 0x09}, + {"7.10.c", 0x0a}, + {"7.10.d", 0x0b}, + {"7.20.a", 0x0c}, + {"7.20.b", 0x0d}, + {"7.20.c", 0x0e}, + {"7.20.d", 0x0f}, + {"7.30.a", 0x10}, + {"7.30.b", 0x11}, + {"8.00.a", 0x12}, + {"8.00.b", 0x13}, + {"8.10.a", 0x14}, + {"8.20.a", 0x15}, + {"8.20.b", 0x16}, + {"8.30.a", 0x17}, + {"8.40.a", 0x18}, + {"8.40.b", 0x19}, + {"8.50.a", 0x1a}, + {"8.50.b", 0x1c}, + {"8.50.c", 0x1e}, + {"9.0", 0x1b}, + {"9.1", 0x1d}, + {"9.2", 0x1f}, + {"9.3", 0x20}, + {"9.4", 0x21}, + {"9.5", 0x22}, + {"9.6", 0x23}, + {"10.0", 0x24}, + {"11.0", 0x25}, + {NULL, 0}, +}; + +static const struct microblaze_version_map family_string_lookup[] = { + {"virtex2", 0x4}, + {"virtex2pro", 0x5}, + {"spartan3", 0x6}, + {"virtex4", 0x7}, + {"virtex5", 0x8}, + {"spartan3e", 0x9}, + {"spartan3a", 0xa}, + {"spartan3an", 0xb}, + {"spartan3adsp", 0xc}, + {"spartan6", 0xd}, + {"virtex6", 0xe}, + {"virtex7", 0xf}, + /* FIXME There is no key code defined for spartan2 */ + {"spartan2", 0xf0}, + {"kintex7", 0x10}, + {"artix7", 0x11}, + {"zynq7000", 0x12}, + {"UltraScale Virtex", 0x13}, + {"UltraScale Kintex", 0x14}, + {"UltraScale+ Zynq", 0x15}, + {"UltraScale+ Virtex", 0x16}, + {"UltraScale+ Kintex", 0x17}, + {"Spartan7", 0x18}, + {NULL, 0}, +}; + +static const char *lookup_string(u32 code, + const struct microblaze_version_map *entry) +{ + for (; entry->string; ++entry) + if (entry->code == code) + return entry->string; + + return "(unknown)"; +} + +static const u32 lookup_code(const char *string, + const struct microblaze_version_map *entry) +{ + for (; entry->string; ++entry) + if (!strcmp(entry->string, string)) + return entry->code; + + return 0; +} + +const char *microblaze_lookup_fpga_family_string(const u32 code) +{ + return lookup_string(code, family_string_lookup); +} + +const char *microblaze_lookup_cpu_version_string(const u32 code) +{ + return lookup_string(code, cpu_ver_lookup); +} + +const u32 microblaze_lookup_fpga_family_code(const char *string) +{ + return lookup_code(string, family_string_lookup); +} + +const u32 microblaze_lookup_cpu_version_code(const char *string) +{ + return lookup_code(string, cpu_ver_lookup); +} +#endif /* CONFIG_CPU_MICROBLAZE */ + void microblaze_early_cpuinfo_init(void) { struct microblaze_cpuinfo *ci = gd_cpuinfo(); diff --git a/arch/microblaze/include/asm/cpuinfo.h b/arch/microblaze/include/asm/cpuinfo.h index c27dd40..86d2c8a 100644 --- a/arch/microblaze/include/asm/cpuinfo.h +++ b/arch/microblaze/include/asm/cpuinfo.h @@ -13,6 +13,11 @@ * @icache_line_length: Instruction cache line length in bytes. * @dcache_size: Size of data cache memory in bytes. * @dcache_line_length: Data cache line length in bytes. + * @use_mmu: MMU support flag. + * @cpu_freq: Cpu clock frequency in Hz. + * @addr_size: Address bus width in bits. + * @ver_code: Cpu version code. + * @fpga_code: FPGA family version code. */ struct microblaze_cpuinfo { u32 icache_size; @@ -20,9 +25,83 @@ struct microblaze_cpuinfo { u32 dcache_size; u32 dcache_line_length; + +#if CONFIG_IS_ENABLED(CPU_MICROBLAZE) + u32 use_mmu; + u32 cpu_freq; + u32 addr_size; + + u32 ver_code; + u32 fpga_code; +#endif /* CONFIG_CPU_MICROBLAZE */ +}; + +/** + * struct microblaze_version_data - Maps a hex version code to a cpu/fpga name. + */ +struct microblaze_version_map { + const char *string; + const u32 code; }; /** + * microblaze_lookup_cpu_version_code() - Get hex version code for the + * specified cpu name string. + * + * This function searches the cpu_ver_lookup[] array for the hex version code + * associated with a specific CPU name. The version code is returned if a match + * is found, otherwise 0. + * + * @string: cpu name string + * + * Return: >0 if the entry is found, 0 otherwise. + */ +const u32 microblaze_lookup_cpu_version_code(const char *string); + +/** + * microblaze_lookup_fpga_family_code() - Get hex version code for the + * specified fpga family name. + * + * This function searches the family_string_lookup[] array for the hex version + * code associated with a specific fpga family name. The version code is + * returned if a match is found, otherwise 0. + * + * @string: fpga family name string + * + * Return: >0 if the entry is found, 0 otherwise. + */ +const u32 microblaze_lookup_fpga_family_code(const char *string); + +/** + * microblaze_lookup_cpu_version_string() - Get cpu name for the specified cpu + * version code. + * + * This function searches the cpu_ver_lookup[] array for the cpu name string + * associated with a specific version code. The cpu name is returned if a match + * is found, otherwise "(unknown)". + * + * @code: cpu version code + * + * Return: Pointer to the cpu name if the entry is found, otherwise "(unknown)". + */ +const char *microblaze_lookup_cpu_version_string(const u32 code); + +/** + * microblaze_lookup_fpga_family_string() - Get fpga family name for the + * specified version code. + * + * This function searches the family_string_lookup[] array for the fpga family + * name string associated with a specific version code. The fpga family name is + * returned if a match is found, otherwise "(unknown)". + * + * @code: fpga family version code + * + * Return: Pointer to the fpga family name if the entry is found, otherwise + * "(unknown)". + */ +const char *microblaze_lookup_fpga_family_string(const u32 code); + +/** * microblaze_early_cpuinfo_init() - Initialize cpuinfo with default values. * * Initializes the global data cpuinfo structure with default values (cache diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig index 7897281..2187433 100644 --- a/drivers/cpu/Kconfig +++ b/drivers/cpu/Kconfig @@ -19,3 +19,12 @@ config CPU_RISCV depends on CPU && RISCV help Support CPU cores for RISC-V architecture. + +config CPU_MICROBLAZE + bool "Enable Microblaze CPU driver" + depends on CPU && MICROBLAZE + select EVENT + select DM_EVENT + select XILINX_MICROBLAZE0_PVR + help + Support CPU cores for Microblaze architecture. diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile index c853263..20884b1 100644 --- a/drivers/cpu/Makefile +++ b/drivers/cpu/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_ARCH_IMX8) += imx8_cpu.o obj-$(CONFIG_ARCH_AT91) += at91_cpu.o obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o obj-$(CONFIG_CPU_RISCV) += riscv_cpu.o +obj-$(CONFIG_CPU_MICROBLAZE) += microblaze_cpu.o obj-$(CONFIG_SANDBOX) += cpu_sandbox.o diff --git a/drivers/cpu/microblaze_cpu.c b/drivers/cpu/microblaze_cpu.c new file mode 100644 index 0000000..969a104 --- /dev/null +++ b/drivers/cpu/microblaze_cpu.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022, Ovidiu Panait + */ +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define update_cpuinfo_pvr(pvr, ci, name) \ +{ \ + u32 tmp = PVR_##name(pvr); \ + if (ci != tmp) \ + printf("PVR value for " #name " does not match static data!\n");\ + ci = tmp; \ +} + +static int microblaze_cpu_probe_all(void *ctx, struct event *event) +{ + int ret; + + ret = cpu_probe_all(); + if (ret) + return log_msg_ret("Microblaze cpus probe failed\n", ret); + + return 0; +} +EVENT_SPY(EVT_DM_POST_INIT, microblaze_cpu_probe_all); + +static void microblaze_set_cpuinfo_pvr(struct microblaze_cpuinfo *ci) +{ + u32 pvr[PVR_FULL_COUNT]; + + microblaze_get_all_pvrs(pvr); + + update_cpuinfo_pvr(pvr, ci->icache_size, ICACHE_BYTE_SIZE); + update_cpuinfo_pvr(pvr, ci->icache_line_length, ICACHE_LINE_LEN); + + update_cpuinfo_pvr(pvr, ci->dcache_size, DCACHE_BYTE_SIZE); + update_cpuinfo_pvr(pvr, ci->dcache_line_length, DCACHE_LINE_LEN); + + update_cpuinfo_pvr(pvr, ci->use_mmu, USE_MMU); + update_cpuinfo_pvr(pvr, ci->ver_code, VERSION); + update_cpuinfo_pvr(pvr, ci->fpga_code, TARGET_FAMILY); +} + +static void microblaze_set_cpuinfo_static(struct udevice *dev, + struct microblaze_cpuinfo *ci) +{ + const char *hw_ver = CONFIG_XILINX_MICROBLAZE0_HW_VER; + const char *fpga_family = CONFIG_XILINX_MICROBLAZE0_FPGA_FAMILY; + + ci->icache_size = dev_read_u32_default(dev, "i-cache-size", 0); + ci->icache_line_length = dev_read_u32_default(dev, + "i-cache-line-size", 0); + + ci->dcache_size = dev_read_u32_default(dev, "d-cache-size", 0); + ci->dcache_line_length = dev_read_u32_default(dev, + "d-cache-line-size", 0); + + ci->cpu_freq = dev_read_u32_default(dev, "clock-frequency", 0); + ci->addr_size = dev_read_u32_default(dev, "xlnx,addr-size", 32); + ci->use_mmu = dev_read_u32_default(dev, "xlnx,use-mmu", 0); + + ci->ver_code = microblaze_lookup_cpu_version_code(hw_ver); + ci->fpga_code = microblaze_lookup_fpga_family_code(fpga_family); +} + +static int microblaze_cpu_probe(struct udevice *dev) +{ + microblaze_set_cpuinfo_static(dev, gd_cpuinfo()); + + if (microblaze_cpu_has_pvr_full()) + microblaze_set_cpuinfo_pvr(gd_cpuinfo()); + else + debug("No PVR support. Using only static CPU info.\n"); + + return 0; +} + +static int microblaze_cpu_get_desc(const struct udevice *dev, char *buf, + int size) +{ + struct microblaze_cpuinfo *ci = gd_cpuinfo(); + const char *cpu_ver, *fpga_family; + u32 cpu_freq_mhz; + int ret; + + cpu_freq_mhz = ci->cpu_freq / 1000000; + cpu_ver = microblaze_lookup_cpu_version_string(ci->ver_code); + fpga_family = microblaze_lookup_fpga_family_string(ci->fpga_code); + + ret = snprintf(buf, size, + "MicroBlaze @ %uMHz, Rev: %s, FPGA family: %s", + cpu_freq_mhz, cpu_ver, fpga_family); + + return 0; +} + +static int microblaze_cpu_get_info(const struct udevice *dev, + struct cpu_info *info) +{ + struct microblaze_cpuinfo *ci = gd_cpuinfo(); + + info->cpu_freq = ci->cpu_freq; + info->address_width = ci->addr_size; + + if (ci->icache_size || ci->dcache_size) + info->features |= BIT(CPU_FEAT_L1_CACHE); + + if (ci->use_mmu) + info->features |= BIT(CPU_FEAT_MMU); + + return 0; +} + +static int microblaze_cpu_get_count(const struct udevice *dev) +{ + return 1; +} + +static const struct cpu_ops microblaze_cpu_ops = { + .get_desc = microblaze_cpu_get_desc, + .get_info = microblaze_cpu_get_info, + .get_count = microblaze_cpu_get_count, +}; + +static const struct udevice_id microblaze_cpu_ids[] = { + { .compatible = "xlnx,microblaze-11.0" }, + { .compatible = "xlnx,microblaze-10.0" }, + { .compatible = "xlnx,microblaze-9.6" }, + { .compatible = "xlnx,microblaze-9.5" }, + { .compatible = "xlnx,microblaze-9.4" }, + { .compatible = "xlnx,microblaze-9.3" }, + { .compatible = "xlnx,microblaze-9.2" }, + { .compatible = "xlnx,microblaze-9.1" }, + { .compatible = "xlnx,microblaze-9.0" }, + { .compatible = "xlnx,microblaze-8.50.c" }, + { .compatible = "xlnx,microblaze-8.50.b" }, + { .compatible = "xlnx,microblaze-8.50.a" }, + { .compatible = "xlnx,microblaze-8.40.b" }, + { .compatible = "xlnx,microblaze-8.40.a" }, + { .compatible = "xlnx,microblaze-8.30.a" }, + { .compatible = "xlnx,microblaze-8.20.b" }, + { .compatible = "xlnx,microblaze-8.20.a" }, + { .compatible = "xlnx,microblaze-8.10.a" }, + { .compatible = "xlnx,microblaze-8.00.b" }, + { .compatible = "xlnx,microblaze-8.00.a" }, + { .compatible = "xlnx,microblaze-7.30.b" }, + { .compatible = "xlnx,microblaze-7.30.a" }, + { .compatible = "xlnx,microblaze-7.20.d" }, + { .compatible = "xlnx,microblaze-7.20.c" }, + { .compatible = "xlnx,microblaze-7.20.b" }, + { .compatible = "xlnx,microblaze-7.20.a" }, + { .compatible = "xlnx,microblaze-7.10.d" }, + { .compatible = "xlnx,microblaze-7.10.c" }, + { .compatible = "xlnx,microblaze-7.10.b" }, + { .compatible = "xlnx,microblaze-7.10.a" }, + { .compatible = "xlnx,microblaze-7.00.b" }, + { .compatible = "xlnx,microblaze-7.00.a" }, + { .compatible = "xlnx,microblaze-6.00.b" }, + { .compatible = "xlnx,microblaze-6.00.a" }, + { .compatible = "xlnx,microblaze-5.00.c" }, + { .compatible = "xlnx,microblaze-5.00.b" }, + { .compatible = "xlnx,microblaze-5.00.a" }, + { } +}; + +U_BOOT_DRIVER(microblaze_cpu) = { + .name = "microblaze_cpu", + .id = UCLASS_CPU, + .of_match = microblaze_cpu_ids, + .probe = microblaze_cpu_probe, + .ops = µblaze_cpu_ops, + .flags = DM_FLAG_PRE_RELOC, +}; -- 2.7.4