| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 2015 Google, Inc |
| * Written by Simon Glass <sjg@chromium.org> |
| * Copyright (c) 2017 Álvaro Fernández Rojas <noltari@gmail.com> |
| * Copyright 2024 NXP |
| */ |
| |
| #include <command.h> |
| #include <cpu.h> |
| #include <display_options.h> |
| #include <dm.h> |
| #include <errno.h> |
| |
| static const char *cpu_feature_name[CPU_FEAT_COUNT] = { |
| "L1 cache", |
| "MMU", |
| "Microcode", |
| "Device ID", |
| }; |
| |
| static struct udevice *cpu_find_device(unsigned long cpu_id) |
| { |
| struct udevice *dev; |
| |
| for (uclass_first_device(UCLASS_CPU, &dev); dev; |
| uclass_next_device(&dev)) { |
| if (cpu_id == dev_seq(dev)) |
| return dev; |
| } |
| |
| return NULL; |
| } |
| |
| static int print_cpu_list(bool detail) |
| { |
| struct udevice *dev; |
| char buf[100]; |
| |
| for (uclass_first_device(UCLASS_CPU, &dev); |
| dev; |
| uclass_next_device(&dev)) { |
| struct cpu_plat *plat = dev_get_parent_plat(dev); |
| struct cpu_info info; |
| bool first = true; |
| int ret, i; |
| |
| ret = cpu_get_desc(dev, buf, sizeof(buf)); |
| printf("%3d: %-10s %s\n", dev_seq(dev), dev->name, |
| ret ? "<no description>" : buf); |
| if (!detail) |
| continue; |
| ret = cpu_get_info(dev, &info); |
| if (ret) { |
| printf("\t(no detail available"); |
| if (ret != -ENOSYS) |
| printf(": err=%d", ret); |
| printf(")\n"); |
| continue; |
| } |
| printf("\tID = %d, freq = ", plat->cpu_id); |
| print_freq(info.cpu_freq, ""); |
| for (i = 0; i < CPU_FEAT_COUNT; i++) { |
| if (info.features & (1 << i)) { |
| printf("%s%s", first ? ": " : ", ", |
| cpu_feature_name[i]); |
| first = false; |
| } |
| } |
| printf("\n"); |
| if (info.features & (1 << CPU_FEAT_UCODE)) |
| printf("\tMicrocode version %#x\n", |
| plat->ucode_version); |
| if (info.features & (1 << CPU_FEAT_DEVICE_ID)) |
| printf("\tDevice ID %#lx\n", plat->device_id); |
| } |
| |
| return 0; |
| } |
| |
| static int do_cpu_list(struct cmd_tbl *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| if (print_cpu_list(false)) |
| return CMD_RET_FAILURE; |
| |
| return 0; |
| } |
| |
| static int do_cpu_detail(struct cmd_tbl *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| if (print_cpu_list(true)) |
| return CMD_RET_FAILURE; |
| |
| return 0; |
| } |
| |
| static int do_cpu_release(struct cmd_tbl *cmdtp, int flag, int argc, |
| char *const argv[]) |
| { |
| struct udevice *dev; |
| unsigned long cpu_id; |
| unsigned long long boot_addr; |
| |
| if (argc != 3) |
| return CMD_RET_USAGE; |
| |
| cpu_id = dectoul(argv[1], NULL); |
| dev = cpu_find_device(cpu_id); |
| if (!dev) |
| return CMD_RET_FAILURE; |
| |
| boot_addr = simple_strtoull(argv[2], NULL, 16); |
| |
| if (cpu_release_core(dev, boot_addr)) |
| return CMD_RET_FAILURE; |
| |
| return 0; |
| } |
| |
| U_BOOT_LONGHELP(cpu, |
| "list - list available CPUs\n" |
| "cpu detail - show CPU detail\n" |
| "cpu release <core ID> <addr> - Release CPU <core ID> at <addr>\n" |
| " <core ID>: the sequence number in list subcommand outputs"); |
| |
| U_BOOT_CMD_WITH_SUBCMDS(cpu, "display information about CPUs", cpu_help_text, |
| U_BOOT_SUBCMD_MKENT(list, 1, 1, do_cpu_list), |
| U_BOOT_SUBCMD_MKENT(detail, 1, 0, do_cpu_detail), |
| U_BOOT_SUBCMD_MKENT(release, 3, 0, do_cpu_release)); |