/* * COPYRIGHT (c) 2013-2017 Chris Johns * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ /** * @file * * @ingroup rtems_fdt * * @brief RTEMS Flattened Device Tree Shell Command * * Command to play with the memory in a FDT. */ #include #include #include #include #include #include #include #include #include /** * The type of the shell handlers we have. */ typedef int (*rtems_fdt_shell_handler) (int argc, char *argv[]); /** * Table of handlers we parse to invoke the command. */ typedef struct { const char* name; /**< The sub-command's name. */ rtems_fdt_shell_handler handler; /**< The sub-command's handler. */ const char* help; /**< The sub-command's help. */ } rtems_fdt_shell_cmd; /** * The timeout for the test loop in seconds. */ static long rtems_fdt_test_timeout = 5; /** * The FDT handle. Only one user of these command a time. */ static rtems_fdt_handle cmd_fdt_handle; static void rtems_fdt_write (uint32_t address, uint32_t value) { volatile uint32_t* ap = (uint32_t*) address; *ap = value; } static uint32_t rtems_fdt_read (uint32_t address) { volatile uint32_t* ap = (uint32_t*) address; return *ap; } static int rtems_fdt_wrong_number_of_args (void) { printf ("error: wrong number of arguments\n"); return 1; } static int rtems_fdt_invalid_args (const char* arg) { printf ("error: invalid argument: %s\n", arg); return 1; } static int rtems_fdt_extra_args (const char* arg) { printf ("error: extra argument is invalid: %s\n", arg); return 1; } static int rtems_fdt_check_error (int errval, const char* message, const char* path) { if (errval < 0) { if (path) printf ("error: %s: %s: (%d) %s\n", message, path, errval, rtems_fdt_strerror (errval)); else printf ("error: %s: (%d) %s\n", message, errval, rtems_fdt_strerror (errval)); return 1; } return 0; } static bool rtems_fdt_get_value32 (const char* path, const char* property, size_t size, uint32_t* value) { const void* prop; int node; int length; node = rtems_fdt_path_offset(&cmd_fdt_handle, path); if (node < 0) { rtems_fdt_check_error (node, "path lookup", path); return false; } prop = rtems_fdt_getprop(&cmd_fdt_handle, node, property, &length); if (length < 0) { rtems_fdt_check_error (length, "get property", path); return false; } if (length != sizeof (uint32_t)) { printf ("error: property is not sizeof(uint32_t): %s\n", path); return false; } *value = rtems_fdt_get_uint32 (prop); return true; } static int rtems_fdt_shell_ld (int argc, char *argv[]) { if (argc != 2) return rtems_fdt_wrong_number_of_args (); return rtems_fdt_check_error (rtems_fdt_load (argv[1], &cmd_fdt_handle), "loading FTB", argv[1]); } static int rtems_fdt_shell_uld (int argc, char *argv[]) { if (argc != 2) return rtems_fdt_wrong_number_of_args (); return rtems_fdt_check_error (rtems_fdt_unload (&cmd_fdt_handle), "unloading FTB", argv[1]); } static int rtems_fdt_shell_ls (int argc, char *argv[]) { char* path = NULL; bool recursive = false; bool long_path = false; bool debug = false; int arg = 1; size_t path_len = 0; int num_entries = 0; int i = 0; while (arg < argc) { if (argv[arg][0] == '-') { if (argv[arg][2] != 0) return rtems_fdt_invalid_args (argv[arg]); switch (argv[arg][1]) { case 'l': long_path = true; break; case 'r': recursive = true; break; case 'd': debug = true; break; default: return rtems_fdt_invalid_args (argv[arg]); } } else { if (path) return rtems_fdt_extra_args (argv[arg]); if (strcmp (argv[arg], "/") != 0) path = argv[arg]; } ++arg; } if (!path) { path = ""; } /* Eliminate trailing slashes. */ path_len = strlen (path); if (path_len > 0 && path[path_len - 1] == '/') path_len--; /* Loop through the entries, looking for matches. */ num_entries = rtems_fdt_num_entries(&cmd_fdt_handle); printf("Total: %d\n", num_entries); for (i = 0; i < num_entries; i++) { /* Add it to the result set. */ const char *name = rtems_fdt_entry_name(&cmd_fdt_handle, i); size_t name_len = strlen(name); if ((name_len > path_len) && ((strncmp (path, name, path_len) == 0) && (name[path_len] == '/')) && (recursive || (index(&name[path_len+1], '/') == 0))) { if (long_path) { printf ("%s", name); } else if (name_len != path_len) { printf ("%s", &name[path_len + 1]); } if (debug) { /* Get properties if we're in debug mode. */ int proplen = 0; int offset = rtems_fdt_entry_offset(&cmd_fdt_handle, i); const void *prop = rtems_fdt_getprop(&cmd_fdt_handle, offset, "reg", &proplen); const void *prop2 = rtems_fdt_getprop(&cmd_fdt_handle, offset, "mask", &proplen); if (prop) { printf(" addr 0x%08" PRIx32, *(uint32_t *)prop); } proplen = 0; if (prop2) { printf(" mask 0x%08" PRIx32, *(uint32_t *)prop2); } } printf("\n"); } } return 0; } static int rtems_fdt_shell_wr (int argc, char *argv[]) { uint32_t address; uint32_t offset = 0; uint32_t value; if ((argc < 3) || (argc > 4)) return rtems_fdt_wrong_number_of_args (); if (argc == 3) { value = strtoul (argv[2], 0, 0); } else { offset = strtoul (argv[2], 0, 0); value = strtoul (argv[3], 0, 0); } if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) return 1; address += offset; printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 "\n", address, value); rtems_fdt_write (address, value); return 0; } static int rtems_fdt_shell_rd (int argc, char *argv[]) { uint32_t address; uint32_t offset = 0; if ((argc < 1) || (argc > 3)) return rtems_fdt_wrong_number_of_args (); if (argc == 3) offset = strtoul (argv[2], 0, 0); if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) return 1; address += offset; printf ("0x%08" PRIx32 " => 0x%08" PRIx32 "\n", address, rtems_fdt_read (address)); return 0; } static int rtems_fdt_shell_set (int argc, char *argv[]) { uint32_t address; uint32_t offset = 0; uint32_t value; int mask_arg; uint32_t mask; if ((argc < 3) || (argc > 4)) return rtems_fdt_wrong_number_of_args (); if (argc == 3) mask_arg = 2; else { offset = strtoul (argv[2], 0, 0); mask_arg = 3; } if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) return 1; if (isdigit (argv[mask_arg][0])) mask = strtoul (argv[mask_arg], 0, 0); else { if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) return 1; } address += offset; value = rtems_fdt_read (address); printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 " = 0x%08" PRIx32 " | 0x%08" PRIx32 "\n", address, value | mask, value, mask); rtems_fdt_write (address, value | mask); return 0; } static int rtems_fdt_shell_cl (int argc, char *argv[]) { uint32_t address; uint32_t offset = 0; uint32_t value; int mask_arg; uint32_t mask; if ((argc < 3) || (argc > 4)) return rtems_fdt_wrong_number_of_args (); if (argc == 3) mask_arg = 2; else { offset = strtoul (argv[2], 0, 0); mask_arg = 3; } if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) return 1; if (isdigit (argv[mask_arg][0])) mask = strtoul (argv[mask_arg], 0, 0); else { if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) return 1; } address += offset; value = rtems_fdt_read (address); printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 " = 0x%08" PRIx32 \ " & ~0x%08" PRIx32 " (0x%08" PRIx32 ")\n", address, value & ~mask, value, mask, ~mask); rtems_fdt_write (address, value & ~mask); return 0; } static int rtems_fdt_shell_up (int argc, char *argv[]) { uint32_t address; uint32_t offset = 0; uint32_t set; uint32_t value; int mask_arg; uint32_t mask; if ((argc < 4) || (argc > 5)) return rtems_fdt_wrong_number_of_args (); if (argc == 4) mask_arg = 2; else { offset = strtoul (argv[2], 0, 0); mask_arg = 3; } set = strtoul (argv[mask_arg + 1], 0, 0); if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) return 1; if (isdigit (argv[mask_arg][0])) mask = strtoul (argv[mask_arg], 0, 0); else { if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) return 1; } address += offset; value = rtems_fdt_read (address); printf ("0x%08" PRIx32 " <= 0x%08" PRIx32 " = (0x%08" PRIx32 \ " & ~0x%08" PRIx32 " (0x%08" PRIx32 ")) | 0x%08" PRIx32 "\n", address, (value & ~mask) | set, value, mask, ~mask, set); rtems_fdt_write (address, (value & ~mask) | set); return 0; } static int rtems_fdt_shell_tst (int argc, char *argv[]) { uint32_t address; uint32_t offset = 0; uint32_t test; uint32_t value = 0; int mask_arg; uint32_t mask; time_t start; if ((argc < 4) || (argc > 5)) return rtems_fdt_wrong_number_of_args (); if (argc == 4) mask_arg = 2; else { offset = strtoul (argv[2], 0, 0); mask_arg = 3; } test = strtoul (argv[mask_arg + 1], 0, 0); if (!rtems_fdt_get_value32 (argv[1], "reg", sizeof (uint32_t), &address)) return 1; if (isdigit (argv[mask_arg][0])) mask = strtoul (argv[mask_arg], 0, 0); else { if (!rtems_fdt_get_value32 (argv[mask_arg], "mask", sizeof (uint32_t), &mask)) return 1; } address += offset; start = time (NULL); printf ("0x%08" PRIx32 " => (value & 0x%08" PRIx32 ") == 0x%08" PRIx32 \ " for %ld seconds\n", address, mask, test, rtems_fdt_test_timeout); while ((time (NULL) - start) < rtems_fdt_test_timeout) { int i; for (i = 0; i < 10000; ++i) { value = rtems_fdt_read (address); if ((value & mask) == test) return 0; } } printf ("0x%08" PRIx32 " => 0x%08" PRIx32 ": timeout\n", address, value); return 1; } static int rtems_fdt_shell_nap (int argc, char *argv[]) { uint32_t time; if (argc != 2) return rtems_fdt_wrong_number_of_args (); time = strtoul (argv[1], 0, 0); if (time == 0) { printf ("error: 0 is not a valid time; check you have a valid number.\n"); return 1; } usleep (time * 1000); return 0; } static int rtems_fdt_shell_to (int argc, char *argv[]) { uint32_t to; if (argc == 1) { printf ("timeout: %ld seconds\n", rtems_fdt_test_timeout); return 0; } if (argc != 2) return rtems_fdt_wrong_number_of_args (); to = strtoul (argv[1], 0, 0); if (to == 0) { printf ("error: 0 is not a valid timeout; check you have a number.\n"); return 1; } rtems_fdt_test_timeout = to; return 0; } static void rtems_fdt_shell_usage (const char* arg) { printf ("%s: FDT Help\n", arg); printf (" %s [-hl] \n", arg); printf (" where:\n"); printf (" command: The FDT subcommand. See -l for a list plus help.\n"); printf (" -h: This help\n"); printf (" -l: The command list.\n"); } static const rtems_fdt_shell_cmd table[] = { { "ld", rtems_fdt_shell_ld, " : Load a FDT blob" }, { "uld", rtems_fdt_shell_uld, "Uload an FDT blob" }, { "ls", rtems_fdt_shell_ls, " : List the nodes at the path and optionally below" }, { "wr", rtems_fdt_shell_wr, " [] : Write the value." }, { "rd", rtems_fdt_shell_rd, " [] : Read the value." }, { "set", rtems_fdt_shell_set, " [] : Set the mask bits" }, { "cl", rtems_fdt_shell_cl, " [] : Clear the mask bits." }, { "up", rtems_fdt_shell_up, " [] : Update the mask bit with value" }, { "tst", rtems_fdt_shell_tst, " [] : Testing loop for masked value." }, { "nap", rtems_fdt_shell_nap, "