diff --git a/MAINTAINERS b/MAINTAINERS
index fb9ba37984..0f8e1f6d4c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -747,6 +747,7 @@ T:	git https://gitlab.denx.de/u-boot/u-boot.git
 F:	common/log*
 F:	cmd/log.c
 F:	doc/develop/logging.rst
+F:	lib/getopt.c
 F:	test/log/
 F:	test/py/tests/test_log.py
 
diff --git a/doc/api/getopt.rst b/doc/api/getopt.rst
new file mode 100644
index 0000000000..773f79aeb6
--- /dev/null
+++ b/doc/api/getopt.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+
+Option Parsing
+==============
+
+.. kernel-doc:: include/getopt.h
+   :internal:
diff --git a/doc/api/index.rst b/doc/api/index.rst
index 1c261bcb73..d90e70e16a 100644
--- a/doc/api/index.rst
+++ b/doc/api/index.rst
@@ -8,6 +8,7 @@ U-Boot API documentation
 
    dfu
    efi
+   getopt
    linker_lists
    pinctrl
    rng
diff --git a/include/getopt.h b/include/getopt.h
new file mode 100644
index 0000000000..6f5811e64b
--- /dev/null
+++ b/include/getopt.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * getopt.h - a simple getopt(3) implementation.
+ *
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ */
+
+#ifndef __GETOPT_H
+#define __GETOPT_H
+
+/**
+ * struct getopt_state - Saved state across getopt() calls
+ */
+struct getopt_state {
+	/**
+	 * @index: Index of the next unparsed argument of @argv. If getopt() has
+	 * parsed all of @argv, then @index will equal @argc.
+	 */
+	int index;
+	/* private: */
+	/** @arg_index: Index within the current argument */
+	int arg_index;
+	union {
+		/* public: */
+		/**
+		 * @opt: Option being parsed when an error occurs. @opt is only
+		 * valid when getopt() returns ``?`` or ``:``.
+		 */
+		int opt;
+		/**
+		 * @arg: The argument to an option, NULL if there is none. @arg
+		 * is only valid when getopt() returns an option character.
+		 */
+		char *arg;
+	/* private: */
+	};
+};
+
+/**
+ * getopt_init_state() - Initialize a &struct getopt_state
+ * @gs: The state to initialize
+ *
+ * This must be called before using @gs with getopt().
+ */
+void getopt_init_state(struct getopt_state *gs);
+
+int __getopt(struct getopt_state *gs, int argc, char *const argv[],
+	     const char *optstring, bool silent);
+
+/**
+ * getopt() - Parse short command-line options
+ * @gs: Internal state and out-of-band return arguments. This must be
+ *      initialized with getopt_init_context() beforehand.
+ * @argc: Number of arguments, not including the %NULL terminator
+ * @argv: Argument list, terminated by %NULL
+ * @optstring: Option specification, as described below
+ *
+ * getopt() parses short options. Short options are single characters. They may
+ * be followed by a required argument or an optional argument. Arguments to
+ * options may occur in the same argument as an option (like ``-larg``), or
+ * in the following argument (like ``-l arg``). An argument containing
+ * options begins with a ``-``. If an option expects no arguments, then it may
+ * be immediately followed by another option (like ``ls -alR``).
+ *
+ * @optstring is a list of accepted options. If an option is followed by ``:``
+ * in @optstring, then it expects a mandatory argument. If an option is followed
+ * by ``::`` in @optstring, it expects an optional argument. @gs.arg points
+ * to the argument, if one is parsed.
+ *
+ * getopt() stops parsing options when it encounters the first non-option
+ * argument, when it encounters the argument ``--``, or when it runs out of
+ * arguments. For example, in ``ls -l foo -R``, option parsing will stop when
+ * getopt() encounters ``foo``, if ``l`` does not expect an argument. However,
+ * the whole list of arguments would be parsed if ``l`` expects an argument.
+ *
+ * An example invocation of getopt() might look like::
+ *
+ *     char *argv[] = { "program", "-cbx", "-a", "foo", "bar", 0 };
+ *     int opt, argc = ARRAY_SIZE(argv) - 1;
+ *     struct getopt_state gs;
+ *
+ *     getopt_init_state(&gs);
+ *     while ((opt = getopt(&gs, argc, argv, "a::b:c")) != -1)
+ *         printf("opt = %c, index = %d, arg = \"%s\"\n", opt, gs.index, gs.arg);
+ *     printf("%d argument(s) left\n", argc - gs.index);
+ *
+ * and would produce an output of::
+ *
+ *     opt = c, index = 1, arg = "<NULL>"
+ *     opt = b, index = 2, arg = "x"
+ *     opt = a, index = 4, arg = "foo"
+ *     1 argument(s) left
+ *
+ * For further information, refer to the getopt(3) man page.
+ *
+ * Return:
+ * * An option character if an option is found. @gs.arg is set to the
+ *   argument if there is one, otherwise it is set to ``NULL``.
+ * * ``-1`` if there are no more options, if a non-option argument is
+ *   encountered, or if an ``--`` argument is encountered.
+ * * ``'?'`` if we encounter an option not in @optstring. @gs.opt is set to
+ *   the unknown option.
+ * * ``':'`` if an argument is required, but no argument follows the
+ *   option. @gs.opt is set to the option missing its argument.
+ *
+ * @gs.index is always set to the index of the next unparsed argument in @argv.
+ */
+static inline int getopt(struct getopt_state *gs, int argc,
+			 char *const argv[], const char *optstring)
+{
+	return __getopt(gs, argc, argv, optstring, false);
+}
+
+/**
+ * getopt_silent() - Parse short command-line options silently
+ * @gs: State
+ * @argc: Argument count
+ * @argv: Argument list
+ * @optstring: Option specification
+ *
+ * Same as getopt(), except no error messages are printed.
+ */
+static inline int getopt_silent(struct getopt_state *gs, int argc,
+				char *const argv[], const char *optstring)
+{
+	return __getopt(gs, argc, argv, optstring, true);
+}
+
+#endif /* __GETOPT_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 8efb154f73..a3346eee04 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -542,6 +542,11 @@ config HEXDUMP
 	help
 	  This enables functions for printing dumps of binary data.
 
+config GETOPT
+	bool "Enable getopt"
+	help
+	  This enables functions for parsing command-line options.
+
 config OF_LIBFDT
 	bool "Enable the FDT library"
 	default y if OF_CONTROL
diff --git a/lib/Makefile b/lib/Makefile
index 0cd7bea282..7c7fb9aae7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -106,6 +106,7 @@ obj-y += string.o
 obj-y += tables_csum.o
 obj-y += time.o
 obj-y += hexdump.o
+obj-$(CONFIG_GETOPT) += getopt.o
 obj-$(CONFIG_TRACE) += trace.o
 obj-$(CONFIG_LIB_UUID) += uuid.o
 obj-$(CONFIG_LIB_RAND) += rand.o
diff --git a/lib/getopt.c b/lib/getopt.c
new file mode 100644
index 0000000000..8b4515dc19
--- /dev/null
+++ b/lib/getopt.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * getopt.c - a simple getopt(3) implementation. See getopt.h for explanation.
+ *
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ */
+
+#define LOG_CATEGORY LOGC_CORE
+
+#include <common.h>
+#include <getopt.h>
+#include <log.h>
+
+void getopt_init_state(struct getopt_state *gs)
+{
+	gs->index = 1;
+	gs->arg_index = 1;
+}
+
+int __getopt(struct getopt_state *gs, int argc, char *const argv[],
+	     const char *optstring, bool silent)
+{
+	char curopt;   /* current option character */
+	const char *curoptp; /* pointer to the current option in optstring */
+
+	while (1) {
+		log_debug("arg_index: %d index: %d\n", gs->arg_index,
+			  gs->index);
+
+		/* `--` indicates the end of options */
+		if (gs->arg_index == 1 && argv[gs->index] &&
+		    !strcmp(argv[gs->index], "--")) {
+			gs->index++;
+			return -1;
+		}
+
+		/* Out of arguments */
+		if (gs->index >= argc)
+			return -1;
+
+		/* Can't parse non-options */
+		if (*argv[gs->index] != '-')
+			return -1;
+
+		/* We have found an option */
+		curopt = argv[gs->index][gs->arg_index];
+		if (curopt)
+			break;
+		/*
+		 * no more options in current argv[] element; try the next one
+		 */
+		gs->index++;
+		gs->arg_index = 1;
+	}
+
+	/* look up current option in optstring */
+	curoptp = strchr(optstring, curopt);
+
+	if (!curoptp) {
+		if (!silent)
+			printf("%s: invalid option -- %c\n", argv[0], curopt);
+		gs->opt = curopt;
+		gs->arg_index++;
+		return '?';
+	}
+
+	if (*(curoptp + 1) != ':') {
+		/* option with no argument. Just return it */
+		gs->arg = NULL;
+		gs->arg_index++;
+		return curopt;
+	}
+
+	if (*(curoptp + 1) && *(curoptp + 2) == ':') {
+		/* optional argument */
+		if (argv[gs->index][gs->arg_index + 1]) {
+			/* optional argument with directly following arg */
+			gs->arg = argv[gs->index++] + gs->arg_index + 1;
+			gs->arg_index = 1;
+			return curopt;
+		}
+		if (gs->index + 1 == argc) {
+			/* We are at the last argv[] element */
+			gs->arg = NULL;
+			gs->index++;
+			return curopt;
+		}
+		if (*argv[gs->index + 1] != '-') {
+			/*
+			 * optional argument with arg in next argv[] element
+			 */
+			gs->index++;
+			gs->arg = argv[gs->index++];
+			gs->arg_index = 1;
+			return curopt;
+		}
+
+		/* no optional argument found */
+		gs->arg = NULL;
+		gs->arg_index = 1;
+		gs->index++;
+		return curopt;
+	}
+
+	if (argv[gs->index][gs->arg_index + 1]) {
+		/* required argument with directly following arg */
+		gs->arg = argv[gs->index++] + gs->arg_index + 1;
+		gs->arg_index = 1;
+		return curopt;
+	}
+
+	gs->index++;
+	gs->arg_index = 1;
+
+	if (gs->index >= argc || argv[gs->index][0] == '-') {
+		if (!silent)
+			printf("option requires an argument -- %c\n", curopt);
+		gs->opt = curopt;
+		return ':';
+	}
+
+	gs->arg = argv[gs->index++];
+	return curopt;
+}
