summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2017-08-16 14:22:23 +1000
committerChris Johns <chrisj@rtems.org>2017-08-20 11:11:46 +1000
commit6b7efdb2ed77760e3c68c5cf450a9620a1cd6562 (patch)
tree7af6121eedd638496f7dc8c2ad380e9917692f14 /cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c
parentdev/i2c: Add I2C device support for FPGA Slave, LM25066A, TMP112, ADS1113, AD... (diff)
downloadrtems-6b7efdb2ed77760e3c68c5cf450a9620a1cd6562.tar.bz2
libmisc/rtems-fdt: Add RTEMS FDT wrapper and shell command to libmisc.
- Provide application support for handling FDT blobs in RTEMS. This is useful when interfacing FPGA fabrics. - Provide a shell command to list a blob as well as provide read and write access to addresses in the FTB. Closes #3099.
Diffstat (limited to 'cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c')
-rw-r--r--cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c639
1 files changed, 639 insertions, 0 deletions
diff --git a/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c b/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c
new file mode 100644
index 0000000000..9412b37750
--- /dev/null
+++ b/cpukit/libmisc/rtems-fdt/rtems-fdt-shell.c
@@ -0,0 +1,639 @@
+/*
+ * COPYRIGHT (c) 2013-2017 Chris Johns <chrisj@rtems.org>
+ *
+ * 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 <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rtems/shell.h>
+#include <rtems/rtems-fdt-shell.h>
+
+/**
+ * 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 %" PRIu32 " 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: %" PRIu32 " 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] <command>\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, "<filename> : Load a FDT blob" },
+ { "uld", rtems_fdt_shell_uld, "Uload an FDT blob" },
+ { "ls", rtems_fdt_shell_ls, "<path> : List the nodes at the path and optionally below" },
+ { "wr", rtems_fdt_shell_wr, "<path> [<offset>] <value> : Write the value." },
+ { "rd", rtems_fdt_shell_rd, "<path> [<offset>] : Read the value." },
+ { "set", rtems_fdt_shell_set, "<path> [<offset>] <mask> : Set the mask bits" },
+ { "cl", rtems_fdt_shell_cl, "<path> [<offset>] <mask> : Clear the mask bits." },
+ { "up", rtems_fdt_shell_up, "<path> [<offset>] <mask> <value> : Update the mask bit with value" },
+ { "tst", rtems_fdt_shell_tst, "<path> [<offset>] <mask> <value> : Testing loop for masked value." },
+ { "nap", rtems_fdt_shell_nap, "<time> : Sleep for the time period. It is in milli-seconds." },
+ { "to", rtems_fdt_shell_to, "<value> : Set the test timeout (seconds)" },
+};
+
+#define RTEMS_FDT_COMMANDS (sizeof (table) / sizeof (const rtems_fdt_shell_cmd))
+
+static int
+rtems_fdt_shell_command (int argc, char* argv[])
+{
+ int arg;
+ size_t t;
+
+ for (arg = 1; arg < argc; arg++)
+ {
+ if (argv[arg][0] != '-')
+ break;
+
+ switch (argv[arg][1])
+ {
+ case 'h':
+ rtems_fdt_shell_usage (argv[0]);
+ return 0;
+ case 'l':
+ printf ("%s: commands are:\n", argv[0]);
+ for (t = 0; t < RTEMS_FDT_COMMANDS; ++t)
+ printf (" %-3s %s\n", table[t].name, table[t].help);
+ return 0;
+ default:
+ printf ("error: unknown option: %s\n", argv[arg]);
+ return 1;
+ }
+ }
+
+ if ((argc - arg) < 1)
+ printf ("error: you need to provide a command, try %s -h\n", argv[0]);
+ else
+ {
+ for (t = 0; t < RTEMS_FDT_COMMANDS; ++t)
+ {
+ if (strncmp (argv[arg], table[t].name, strlen (argv[arg])) == 0)
+ {
+ int r = table[t].handler (argc - arg, argv + 1);
+ return r;
+ }
+ }
+ printf ("error: command not found: %s (try -h)\n", argv[arg]);
+ }
+
+ return 1;
+}
+
+void
+rtems_fdt_add_shell_command(void)
+{
+ rtems_shell_add_cmd ("fdt", "mem",
+ "Flattened device tree", rtems_fdt_shell_command);
+}
+
+rtems_fdt_handle*
+rtems_fdt_get_shell_handle (void)
+{
+ return &cmd_fdt_handle;
+}