1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2021 4 * K��ry Maincent, Bootlin, <kory.maincent@bootlin.com> 5 */ 6 7#include <common.h> 8#include <bootdev.h> 9#include <command.h> 10#include <dm.h> 11#include <malloc.h> 12#include <extension_board.h> 13#include <mapmem.h> 14#include <linux/libfdt.h> 15#include <fdt_support.h> 16 17static LIST_HEAD(extension_list); 18 19static int extension_apply(struct extension *extension) 20{ 21 char *overlay_cmd; 22 ulong extrasize, overlay_addr; 23 struct fdt_header *blob; 24 25 if (!working_fdt) { 26 printf("No FDT memory address configured. Please configure\n" 27 "the FDT address via \"fdt addr <address>\" command.\n"); 28 return CMD_RET_FAILURE; 29 } 30 31 overlay_cmd = env_get("extension_overlay_cmd"); 32 if (!overlay_cmd) { 33 printf("Environment extension_overlay_cmd is missing\n"); 34 return CMD_RET_FAILURE; 35 } 36 37 overlay_addr = env_get_hex("extension_overlay_addr", 0); 38 if (!overlay_addr) { 39 printf("Environment extension_overlay_addr is missing\n"); 40 return CMD_RET_FAILURE; 41 } 42 43 env_set("extension_overlay_name", extension->overlay); 44 if (run_command(overlay_cmd, 0) != 0) 45 return CMD_RET_FAILURE; 46 47 extrasize = env_get_hex("filesize", 0); 48 if (!extrasize) 49 return CMD_RET_FAILURE; 50 51 fdt_shrink_to_minimum(working_fdt, extrasize); 52 53 blob = map_sysmem(overlay_addr, 0); 54 if (!fdt_valid(&blob)) 55 return CMD_RET_FAILURE; 56 57 /* apply method prints messages on error */ 58 if (fdt_overlay_apply_verbose(working_fdt, blob)) 59 return CMD_RET_FAILURE; 60 61 return CMD_RET_SUCCESS; 62} 63 64static int do_extension_list(struct cmd_tbl *cmdtp, int flag, 65 int argc, char *const argv[]) 66{ 67 int i = 0; 68 struct extension *extension; 69 70 if (list_empty(&extension_list)) { 71 printf("No extension registered - Please run \"extension scan\"\n"); 72 return CMD_RET_SUCCESS; 73 } 74 75 list_for_each_entry(extension, &extension_list, list) { 76 printf("Extension %d: %s\n", i++, extension->name); 77 printf("\tManufacturer: \t\t%s\n", extension->owner); 78 printf("\tVersion: \t\t%s\n", extension->version); 79 printf("\tDevicetree overlay: \t%s\n", extension->overlay); 80 printf("\tOther information: \t%s\n", extension->other); 81 } 82 return CMD_RET_SUCCESS; 83} 84 85static int extension_scan(bool show) 86{ 87 struct extension *extension, *next; 88 int extension_num; 89 90 list_for_each_entry_safe(extension, next, &extension_list, list) { 91 list_del(&extension->list); 92 free(extension); 93 } 94 extension_num = extension_board_scan(&extension_list); 95 if (show && extension_num >= 0) 96 printf("Found %d extension board(s).\n", extension_num); 97 98 /* either the number of extensions, or -ve for error */ 99 return extension_num; 100} 101 102 103static int do_extension_scan(struct cmd_tbl *cmdtp, int flag, 104 int argc, char *const argv[]) 105{ 106 int extension_num; 107 108 extension_num = extension_scan(true); 109 if (extension_num < 0) 110 return CMD_RET_FAILURE; 111 112 return CMD_RET_SUCCESS; 113} 114 115static int do_extension_apply(struct cmd_tbl *cmdtp, int flag, 116 int argc, char *const argv[]) 117{ 118 struct extension *extension = NULL; 119 struct list_head *entry; 120 int i = 0, extension_id, ret; 121 122 if (argc < 2) 123 return CMD_RET_USAGE; 124 125 if (strcmp(argv[1], "all") == 0) { 126 ret = CMD_RET_FAILURE; 127 list_for_each_entry(extension, &extension_list, list) { 128 ret = extension_apply(extension); 129 if (ret != CMD_RET_SUCCESS) 130 break; 131 } 132 } else { 133 extension_id = simple_strtol(argv[1], NULL, 10); 134 list_for_each(entry, &extension_list) { 135 if (i == extension_id) { 136 extension = list_entry(entry, struct extension, list); 137 break; 138 } 139 i++; 140 } 141 142 if (!extension) { 143 printf("Wrong extension number\n"); 144 return CMD_RET_FAILURE; 145 } 146 147 ret = extension_apply(extension); 148 } 149 150 return ret; 151} 152 153static struct cmd_tbl cmd_extension[] = { 154 U_BOOT_CMD_MKENT(scan, 1, 1, do_extension_scan, "", ""), 155 U_BOOT_CMD_MKENT(list, 1, 0, do_extension_list, "", ""), 156 U_BOOT_CMD_MKENT(apply, 2, 0, do_extension_apply, "", ""), 157}; 158 159static int do_extensionops(struct cmd_tbl *cmdtp, int flag, int argc, 160 char *const argv[]) 161{ 162 struct cmd_tbl *cp; 163 164 /* Drop the extension command */ 165 argc--; 166 argv++; 167 168 cp = find_cmd_tbl(argv[0], cmd_extension, ARRAY_SIZE(cmd_extension)); 169 if (cp) 170 return cp->cmd(cmdtp, flag, argc, argv); 171 172 return CMD_RET_USAGE; 173} 174 175U_BOOT_CMD(extension, 3, 1, do_extensionops, 176 "Extension board management sub system", 177 "scan - scan plugged extension(s) board(s)\n" 178 "extension list - lists available extension(s) board(s)\n" 179 "extension apply <extension number|all> - applies DT overlays corresponding to extension boards\n" 180); 181 182static int extension_bootdev_hunt(struct bootdev_hunter *info, bool show) 183{ 184 int ret; 185 186 ret = env_set_hex("extension_overlay_addr", 187 env_get_hex("fdtoverlay_addr_r", 0)); 188 if (ret) 189 return log_msg_ret("env", ret); 190 191 ret = extension_scan(show); 192 if (ret < 0) 193 return log_msg_ret("ext", ret); 194 195 return 0; 196} 197 198/* extensions should have a uclass - for now we use UCLASS_SIMPLE_BUS uclass */ 199BOOTDEV_HUNTER(extension_bootdev_hunter) = { 200 .prio = BOOTDEVP_1_PRE_SCAN, 201 .uclass = UCLASS_SIMPLE_BUS, 202 .hunt = extension_bootdev_hunt, 203}; 204