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 <ovpanait@gmail.com>
Link: https://lore.kernel.org/r/20220531181435.3473549-14-ovpanait@gmail.com
Signed-off-by: Michal Simek <michal.simek@amd.com>
			
			
This commit is contained in:
		
							parent
							
								
									9df16c5937
								
							
						
					
					
						commit
						816226d27e
					
				|  | @ -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(); | ||||
|  |  | |||
|  | @ -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,8 +25,82 @@ 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. | ||||
|  * | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -0,0 +1,180 @@ | |||
| // SPDX-License-Identifier: GPL-2.0+
 | ||||
| /*
 | ||||
|  * Copyright (C) 2022, Ovidiu Panait <ovpanait@gmail.com> | ||||
|  */ | ||||
| #include <common.h> | ||||
| #include <cpu.h> | ||||
| #include <dm.h> | ||||
| #include <asm/cpuinfo.h> | ||||
| #include <asm/global_data.h> | ||||
| #include <asm/pvr.h> | ||||
| 
 | ||||
| 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, | ||||
| }; | ||||
		Loading…
	
		Reference in New Issue