diff --git a/cli_classic.c b/cli_classic.c
index 1170d33..146750e 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -31,42 +31,49 @@
 #include "flash.h"
 #include "flashchips.h"
 #include "programmer.h"
 #include "writeprotect.h"
 
 enum LONGOPT_RETURN_VALUES {
 	/* Start after ASCII chars */
 	LONGOPT_PRINT_STATUSREG = 256,
 	LONGOPT_PRINT_WP_STATUS,
 	LONGOPT_LIST_BP_RANGES,
 	LONGOPT_WP_ENABLE,
 	LONGOPT_WP_DISABLE,
 	LONGOPT_SET_BP_RANGE,
+	LONGOPT_PRINT_OTP_STATUS,
+	LONGOPT_READ_OTP,
+	LONGOPT_WRITE_OTP,
+	LONGOPT_ERASE_OTP,
+	LONGOPT_LOCK_OTP,
 };
 
 static void cli_classic_usage(const char *name)
 {
 	printf("Please note that the command line interface for flashrom has changed between\n"
 	       "0.9.5 and 0.9.6 and will change again before flashrom 1.0.\n\n");
 
 	printf("Usage: %s [-h|-R|-L|"
 #if CONFIG_PRINT_WIKI == 1
 	       "-z|"
 #endif
 	       "-p <programmername>[:<parameters>] [-c <chipname>]\n"
 	       "[-E|(-r|-w|-v) <file>] [-l <layoutfile> [-i <imagename>]...] [-n] [-f]]\n"
 	       "[-V[V[V]]] [-o <logfile>] [--print-status-reg] [--print-wp-status]\n"
 	       "[--wp-list] [--wp-enable[=<MODE>]] [--wp-disable]\n"
-	       "[--wp-set-range start=<start>,len=<len>]\n\n", name);
+	       "[--wp-set-range start=<start>,len=<len>] [--print-otp-status]\n"
+	       "[--read-otp file=<file>[,reg=<region>]] [--erase-otp [reg=<region>]]\n"
+	       "[--write-otp file=<file>[,reg=<region>]] [--lock-otp [reg=<region>]]\n\n", name);
 
 	printf(" -h | --help                        print this help text\n"
 	       " -R | --version                     print version (release)\n"
 	       " -r | --read <file>                 read flash and save to <file>\n"
 	       " -w | --write <file>                write <file> to flash\n"
 	       " -v | --verify <file>               verify flash against <file>\n"
 	       " -E | --erase                       erase flash memory\n"
 	       " -V | --verbose                     more verbose output\n"
 	       " -c | --chip <chipname>             probe only for specified flash chip\n"
 	       " -f | --force                       force specific operations (see man page)\n"
 	       " -n | --noverify                    don't auto-verify\n"
 	       " -l | --layout <layoutfile>         read ROM layout from <layoutfile>\n"
 	       " -i | --image <name>                only flash image <name> from flash layout\n"
@@ -74,47 +81,55 @@ static void cli_classic_usage(const char *name)
 	       " -L | --list-supported              print supported devices\n"
 #if CONFIG_PRINT_WIKI == 1
 	       " -z | --list-supported-wiki         print supported devices in wiki syntax\n"
 #endif
 	       "      --print-status-reg            print detailed contents of status register(s)\n"
 	       "      --print-wp-status             print write protection mode of status register(s)\n"
 	       "      --wp-list                     print list of write protection ranges\n"
 	       "      --wp-enable[=MODE]            enable write protection of status register(s)\n"
 	       "                                    MODE can be one of HARDWARE (default),\n"
 	       "                                    PERMANENT, POWER_CYCLE, SOFTWARE (see man page)\n"
 	       "      --wp-disable                  disable any write protection of status register(s)\n"
 	       "      --wp-set-range start=<start>,len=<len>\n"
 	       "                                    set write protection range (see man page)\n"
+	       "      --print-otp-status            print OTP memory regions and lock status\n"
+	       "      --read-otp <file>[,reg=<region>]\n"
+	       "                                    read OTP memory <region> (defaults to 1)\n"
+	       "                                    and save to <file>\n"
+	       "      --write-otp file=<file>[,reg=<region>]\n"
+	       "                                    write <file> to OTP memory <region> (defaults to 1)\n"
+	       "      --erase-otp [reg=<region>]    erase OTP memory <region> (defaults to 1)\n"
+	       "      --lock-otp [reg=<region>]     lock OTP memory <region> (defaults to 1)\n"
 	       " -p | --programmer <name>[:<param>] specify the programmer device. One of\n");
 	list_programmers_linebreak(4, 80, 0);
 	printf(".\n\nYou can specify one of -h, -R, -L, "
 #if CONFIG_PRINT_WIKI == 1
 	         "-z, "
 #endif
 	         "-E, -r, -w, -v or no operation.\n"
 	       "If no operation is specified, flashrom will only probe for flash chips.\n");
 }
 
 static void cli_classic_abort_usage(void)
 {
 	printf("Please run \"flashrom --help\" for usage info.\n");
 	exit(1);
 }
 
-static void cli_statreg_wp_support(struct flashctx *flash)
+static void cli_infra_support(struct flashctx *flash, char const *infra)
 {
-	msg_ginfo("flashrom does not (yet) support write protection for chip \"%s\".\n"
+	msg_ginfo("flashrom does not (yet) support %s infrastructure for chip \"%s\".\n"
 		  "You could add support and send the patch to flashrom@flashrom.org\n",
-		  flash->chip->name);
+		  infra, flash->chip->name);
 }
 
 static int check_filename(char *filename, char *type)
 {
 	if (!filename || (filename[0] == '\0')) {
 		fprintf(stderr, "Error: No %s file specified.\n", type);
 		return 1;
 	}
 	/* Not an error, but maybe the user intended to specify a CLI option instead of a file name. */
 	if (filename[0] == '-')
 		fprintf(stderr, "Warning: Supplied %s file name starts with -\n", type);
 	return 0;
 }
@@ -123,26 +138,27 @@ int main(int argc, char *argv[])
 {
 	const struct flashchip *chip = NULL;
 	/* Probe for up to eight flash chips. */
 	struct flashctx flashes[8] = {{0}};
 	struct flashctx *fill_flash;
 	const char *name;
 	int namelen, opt, i, j;
 	int startchip = -1, chipcount = 0, option_index = 0, force = 0;
 #if CONFIG_PRINT_WIKI == 1
 	int list_supported_wiki = 0;
 #endif
 	int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0, print_status_reg = 0;
 	int print_wp_status = 0, wp_list = 0, wp_enable = 0, wp_disable = 0, wp_set_range = 0;
+	int print_otp_status = 0, read_otp = 0, write_otp = 0, erase_otp = 0, lock_otp = 0;
 	int dont_verify_it = 0, list_supported = 0, operation_specified = 0;
 	enum programmer prog = PROGRAMMER_INVALID;
 	int ret = 0;
 
 	static const char optstring[] = "r:Rw:v:nVEfc:l:i:p:Lzho:";
 	static const struct option long_options[] = {
 		{"read",		1, NULL, 'r'},
 		{"write",		1, NULL, 'w'},
 		{"erase",		0, NULL, 'E'},
 		{"verify",		1, NULL, 'v'},
 		{"noverify",		0, NULL, 'n'},
 		{"chip",		1, NULL, 'c'},
 		{"verbose",		0, NULL, 'V'},
@@ -151,38 +167,47 @@ int main(int argc, char *argv[])
 		{"image",		1, NULL, 'i'},
 		{"list-supported",	0, NULL, 'L'},
 		{"list-supported-wiki",	0, NULL, 'z'},
 		{"programmer",		1, NULL, 'p'},
 		{"help",		0, NULL, 'h'},
 		{"version",		0, NULL, 'R'},
 		{"output",		1, NULL, 'o'},
 		{"print-status-reg",	0, NULL, LONGOPT_PRINT_STATUSREG},
 		{"print-wp-status",	0, NULL, LONGOPT_PRINT_WP_STATUS},
 		{"wp-list",		0, NULL, LONGOPT_LIST_BP_RANGES},
 		{"wp-enable",		optional_argument, NULL, LONGOPT_WP_ENABLE},
 		{"wp-disable",		0, NULL, LONGOPT_WP_DISABLE},
 		{"wp-set-range",	1, NULL, LONGOPT_SET_BP_RANGE},
+		{"print-otp-status",	0, NULL, LONGOPT_PRINT_OTP_STATUS},
+		{"read-otp",		1, NULL, LONGOPT_READ_OTP},
+		{"write-otp",		1, NULL, LONGOPT_WRITE_OTP},
+		{"erase-otp",		optional_argument, NULL, LONGOPT_ERASE_OTP},
+		{"lock-otp",		optional_argument, NULL, LONGOPT_LOCK_OTP},
 		{NULL,			0, NULL, 0},
 	};
 
 	char *filename = NULL;
 	char *layoutfile = NULL;
 #ifndef STANDALONE
 	char *logfile = NULL;
 #endif /* !STANDALONE */
 	char *tempstr = NULL;
 	char *pparam = NULL;
 	char *wp_mode_opt = NULL;
 	char const *wp_set_range_opt = NULL;
+	char const *read_otp_opt = NULL;
+	char const *write_otp_opt = NULL;
+	char const *erase_otp_opt = NULL;
+	char const *lock_otp_opt = NULL;
 
 	print_version();
 	print_banner();
 
 	if (selfcheck())
 		exit(1);
 
 	setbuf(stdout, NULL);
 	/* FIXME: Delay all operation_specified checks until after command
 	 * line parsing to allow --help overriding everything else.
 	 */
 	while ((opt = getopt_long(argc, argv, optstring,
 				  long_options, &option_index)) != EOF) {
@@ -346,28 +371,29 @@ int main(int argc, char *argv[])
 #ifdef STANDALONE
 			fprintf(stderr, "Log file not supported in standalone mode. Aborting.\n");
 			cli_classic_abort_usage();
 #else /* STANDALONE */
 			logfile = strdup(optarg);
 			if (logfile[0] == '\0') {
 				fprintf(stderr, "No log filename specified.\n");
 				cli_classic_abort_usage();
 			}
 #endif /* STANDALONE */
 			break;
 		/* FIXME(hatim): For the following long options, not _all_
 		 * of them are mutually exclusive per se (like wp_set_range
-		 * and wp_enable makes sense). There is scope for improvement
-		 * here, but for now let's treat each one as separate operation. */
+		 * and wp_enable, or read_otp and lock_otp makes sense). There
+		 * is scope for improvement here, but for now let's treat each
+		 * one as separate operation. */
 		case LONGOPT_PRINT_STATUSREG:
 			if (++operation_specified > 1) {
 				fprintf(stderr, "More than one operation "
 					"specified. Aborting.\n");
 				cli_classic_abort_usage();
 			}
 			print_status_reg = 1;
 			break;
 		case LONGOPT_PRINT_WP_STATUS:
 			if (++operation_specified > 1) {
 				fprintf(stderr, "More than one operation "
 					"specified. Aborting.\n");
 				cli_classic_abort_usage();
@@ -399,38 +425,84 @@ int main(int argc, char *argv[])
 				cli_classic_abort_usage();
 			}
 			wp_disable = 1;
 			break;
 		case LONGOPT_SET_BP_RANGE:
 			if (++operation_specified > 1) {
 				fprintf(stderr, "More than one operation "
 					"specified. Aborting.\n");
 				cli_classic_abort_usage();
 			}
 			wp_set_range_opt = strdup(optarg);
 			wp_set_range = 1;
 			break;
+		case LONGOPT_PRINT_OTP_STATUS:
+			if (++operation_specified > 1) {
+				fprintf(stderr, "More than one operation "
+					"specified. Aborting.\n");
+				cli_classic_abort_usage();
+			}
+			print_otp_status = 1;
+			break;
+		case LONGOPT_READ_OTP:
+			if (++operation_specified > 1) {
+				fprintf(stderr, "More than one operation "
+					"specified. Aborting.\n");
+				cli_classic_abort_usage();
+			}
+			read_otp_opt = strdup(optarg);
+			filename = extract_param(&read_otp_opt, "file", ",");
+			read_otp = 1;
+			break;
+		case LONGOPT_WRITE_OTP:
+			if (++operation_specified > 1) {
+				fprintf(stderr, "More than one operation "
+					"specified. Aborting.\n");
+				cli_classic_abort_usage();
+			}
+			write_otp_opt = strdup(optarg);
+			filename = extract_param(&write_otp_opt, "file", ",");
+			write_otp = 1;
+			break;
+		case LONGOPT_ERASE_OTP:
+			if (++operation_specified > 1) {
+				fprintf(stderr, "More than one operation "
+					"specified. Aborting.\n");
+				cli_classic_abort_usage();
+			}
+			erase_otp_opt = strdup(optarg);
+			erase_otp = 1;
+			break;
+		case LONGOPT_LOCK_OTP:
+			if (++operation_specified > 1) {
+				fprintf(stderr, "More than one operation "
+					"specified. Aborting.\n");
+				cli_classic_abort_usage();
+			}
+			lock_otp_opt = strdup(optarg);
+			lock_otp = 1;
+			break;
 		default:
 			cli_classic_abort_usage();
 			break;
 		}
 	}
 
 	if (optind < argc) {
 		fprintf(stderr, "Error: Extra parameter found.\n");
 		cli_classic_abort_usage();
 	}
 
-	if ((read_it | write_it | verify_it) && check_filename(filename, "image")) {
+	if ((read_it | write_it | verify_it | read_otp | write_otp) && check_filename(filename, "image")) {
 		cli_classic_abort_usage();
 	}
 	if (layoutfile && check_filename(layoutfile, "layout")) {
 		cli_classic_abort_usage();
 	}
 
 #ifndef STANDALONE
 	if (logfile && check_filename(logfile, "log"))
 		cli_classic_abort_usage();
 	if (logfile && open_logfile(logfile))
 		cli_classic_abort_usage();
 #endif /* !STANDALONE */
 
@@ -588,125 +660,126 @@ int main(int argc, char *argv[])
 		}
 		ret = 1;
 		goto out_shutdown;
 	} else if (!chip_to_probe) {
 		/* repeat for convenience when looking at foreign logs */
 		tempstr = flashbuses_to_text(flashes[0].chip->bustype);
 		msg_gdbg("Found %s flash chip \"%s\" (%d kB, %s).\n",
 			 flashes[0].chip->vendor, flashes[0].chip->name, flashes[0].chip->total_size, tempstr);
 		free(tempstr);
 	}
 
 	fill_flash = &flashes[0];
 
-	print_chip_support_status(fill_flash->chip);
+	print_chip_support_status(fill_flash);
 
 	unsigned int limitexceeded = count_max_decode_exceedings(fill_flash);
 	if (limitexceeded > 0 && !force) {
 		enum chipbustype commonbuses = fill_flash->mst->buses_supported & fill_flash->chip->bustype;
 
 		/* Sometimes chip and programmer have more than one bus in common,
 		 * and the limit is not exceeded on all buses. Tell the user. */
 		if ((bitcount(commonbuses) > limitexceeded)) {
 			msg_pdbg("There is at least one interface available which could support the size of\n"
 				 "the selected flash chip.\n");
 		}
 		msg_cerr("This flash chip is too big for this programmer (--verbose/-V gives details).\n"
 			 "Use --force/-f to override at your own risk.\n");
 		ret = 1;
 		goto out_shutdown;
 	}
 
 	if (!(read_it | write_it | verify_it | erase_it | print_status_reg |
-	      print_wp_status | wp_list | wp_enable | wp_disable | wp_set_range)) {
+	      print_wp_status | wp_list | wp_enable | wp_disable | wp_set_range |
+	      print_otp_status | read_otp | write_otp | erase_otp | lock_otp)) {
 		msg_ginfo("No operations were specified.\n");
 		goto out_shutdown;
 	}
 
 	/* Always verify write operations unless -n is used. */
 	if (write_it && !dont_verify_it)
 		verify_it = 1;
 
 	/* Map the selected flash chip again. */
 	if (map_flash(fill_flash) != 0) {
 		ret = 1;
 		goto out_shutdown;
 	}
 
 	if (print_status_reg) {
 		verbose_screen++;
 		if (fill_flash->chip->status_register) {
 			for (enum status_register_num SRn = SR1; SRn <= top_status_register(fill_flash); SRn++)
 				fill_flash->chip->status_register->print(fill_flash, SRn);
 			fill_flash->chip->status_register->print_wp_mode(fill_flash);
 			if (fill_flash->chip->wp)
 				print_range_generic(fill_flash);
 		} else
-			cli_statreg_wp_support(fill_flash);
+			cli_infra_support(fill_flash, "access protection");
 		goto out_shutdown;
 	}
 
 	if (print_wp_status) {
 		verbose_screen++;
 		if (fill_flash->chip->status_register || fill_flash->chip->wp) {
 			msg_ginfo("WP status -\n");
 			fill_flash->chip->status_register->print_wp_mode(fill_flash);
 			if (fill_flash->chip->wp)
 				print_range_generic(fill_flash);
 		} else
-			cli_statreg_wp_support(fill_flash);
+			cli_infra_support(fill_flash, "access protection");
 		goto out_shutdown;
 	}
 
 	if (wp_list) {
 		verbose_screen++;
 		if (fill_flash->chip->wp) {
 			msg_ginfo("Valid write protection ranges for chip \"%s\" are -\n",
 				  fill_flash->chip->name);
 			fill_flash->chip->wp->print_table(fill_flash);
 		} else
-			cli_statreg_wp_support(fill_flash);
+			cli_infra_support(fill_flash, "access protection");
 		goto out_shutdown;
 	}
 
 	if (wp_disable) {
 		verbose_screen++;
 		if (fill_flash->chip->wp) {
 			ret = fill_flash->chip->wp->disable(fill_flash);
 		} else
-			cli_statreg_wp_support(fill_flash);
+			cli_infra_support(fill_flash, "access protection");
 		goto out_shutdown;
 	}
 
 	if (wp_set_range) {
 		verbose_screen++;
 		if (fill_flash->chip->wp) {
 			char *endptr = NULL;
 			uint8_t start_cmp, end_cmp, has_cmp = pos_bit(fill_flash, CMP) != -1;
 			if (has_cmp)
 				start_cmp = get_cmp(fill_flash);
 			/* FIXME(hatim): Implement error checking */
 			uint32_t start = strtoul(extract_param(&wp_set_range_opt, "start", ","), &endptr, 0);
 			uint32_t len = strtoul(extract_param(&wp_set_range_opt, "len", ","), &endptr, 0);
 			msg_ginfo("Trying to protect %d kB starting from address 0x%06x...\n", len, start);
 			ret = fill_flash->chip->wp->set_range(fill_flash, start, len);
 			if (has_cmp && start_cmp != (end_cmp = get_cmp(fill_flash)))
 				msg_ginfo("CMP bit was %sset\n", end_cmp ? "" : "un");
 			if (ret)
 				msg_gerr("Failed to protect\n");
 			else
 				msg_ginfo("Protection successful!\n");
 		} else
-			cli_statreg_wp_support(fill_flash);
+			cli_infra_support(fill_flash, "access protection");
 		goto out_shutdown;
 	}
 
 	if (wp_enable) {
 		verbose_screen++;
 		if (fill_flash->chip->status_register) {
 			enum wp_mode wp_mode = WP_MODE_HARDWARE_UNPROTECTED;
 
 			if (wp_mode_opt) {
 				if (!strcasecmp(wp_mode_opt, "PERMANENT")) {
 					wp_mode = WP_MODE_PERMANENT;
 				} else if (!strcasecmp(wp_mode_opt, "POWER_CYCLE")) {
 					wp_mode = WP_MODE_POWER_CYCLE;
@@ -717,27 +790,187 @@ int main(int argc, char *argv[])
 				} else {
 					msg_gerr("Invalid write protection mode for status register(s)\n");
 					cli_classic_abort_usage();
 					ret = 1;
 					goto out_shutdown;
 				}
 			}
 			msg_ginfo("Setting write protection mode to %s ...\n",
 				  wp_mode_opt ? wp_mode_opt : "HARDWARE");
 			fill_flash->chip->status_register->set_wp_mode(fill_flash, wp_mode);
 			ret = !(wp_mode == fill_flash->chip->status_register->get_wp_mode(fill_flash));
 			msg_gerr("%s\n", ret ? "Failed" : "Success");
 		} else
-			cli_statreg_wp_support(fill_flash);
+			cli_infra_support(fill_flash, "access protection");
+		goto out_shutdown;
+	}
+
+	if (print_otp_status) {
+		verbose_screen++;
+		if (fill_flash->chip->otp) {
+			msg_ginfo("OTP status -\n");
+			ret = fill_flash->chip->otp->print_status(fill_flash);
+		} else
+			cli_infra_support(fill_flash, "OTP");
+
+		goto out_shutdown;
+	}
+
+	if (read_otp) {
+		verbose_screen++;
+		if (fill_flash->chip->otp) {
+			char *otp_region_opt = extract_param(&read_otp_opt, "reg", ",");
+			enum otp_region otp_region = OTP_REG_1;
+			if (otp_region_opt) {
+				char *endptr = NULL;
+				// FIXME(hatim): Implement error-checking (?)
+				otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1;
+			} else
+				msg_gdbg("OTP region not specified, using default region 1\n");
+
+			uint32_t len = fill_flash->chip->otp->region[otp_region].size;
+			uint8_t *buf = calloc(len, sizeof(uint8_t));
+			if (!buf) {
+				msg_gerr("Memory allocation failed\n");
+				ret = 1;
+				goto out_shutdown;
+			}
+			if (!fill_flash->chip->otp->read) {
+				msg_gerr("No OTP read function available for \"%s\"\n",
+					fill_flash->chip->name);
+				ret = 1;
+				free(buf);
+				goto out_shutdown;
+			}
+			msg_gdbg("Reading OTP memory...\n");
+			ret = fill_flash->chip->otp->read(fill_flash, buf, otp_region, 0x000000, len);
+			if (ret) {
+				msg_gerr("Reading OTP memory failed\n");
+				free(buf);
+				goto out_shutdown;
+			}
+			ret = write_buf_to_file(buf, len, filename);
+		} else
+			cli_infra_support(fill_flash, "OTP");
+
+		goto out_shutdown;
+	}
+
+	if (write_otp) {
+		verbose_screen++;
+		if (fill_flash->chip->otp) {
+			char *otp_region_opt = extract_param(&write_otp_opt, "reg", ",");
+			enum otp_region otp_region = OTP_REG_1;
+			if (otp_region_opt) {
+				char *endptr = NULL;
+				// FIXME(hatim): Implement error-checking (?)
+				otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1;
+			} else
+				msg_gdbg("OTP region not specified, using default region 1\n");
+
+			uint32_t len = fill_flash->chip->otp->region[otp_region].size;
+			uint8_t *buf = calloc(len, sizeof(uint8_t));
+			if (!buf) {
+				msg_gerr("Memory allocation failed\n");
+				ret = 1;
+				goto out_shutdown;
+			}
+
+			ret = read_buf_from_file(buf, len, filename);
+			if (ret) {
+				msg_gerr("Error reading from file \"%s\", failed\n", filename);
+				free(buf);
+				goto out_shutdown;
+			}
+			msg_gdbg("Reading form file \"%s\" complete\n", filename);
+			if (!fill_flash->chip->otp->write) {
+				msg_gerr("No OTP write function available for \"%s\"\n",
+					fill_flash->chip->name);
+				ret = 1;
+				free(buf);
+				goto out_shutdown;
+			}
+
+			msg_gdbg("Erasing OTP memory...\n");
+			ret = fill_flash->chip->otp->erase(fill_flash, otp_region);
+			if (ret) {
+				msg_gerr("Erasing OTP memory failed\n");
+				free(buf);
+				goto out_shutdown;
+			}
+			msg_gdbg("Erasing OTP memory done\n");
+
+			msg_gdbg("Writing OTP memory...\n");
+			ret = fill_flash->chip->otp->write(fill_flash, buf, otp_region, 0x000000, len);
+			if (ret) {
+				msg_gerr("Writing OTP memory failed\n");
+				free(buf);
+				goto out_shutdown;
+			}
+			msg_gdbg("Writing OTP memory done\n");
+			// FIXME(hatim): Verify written contents
+		} else
+			cli_infra_support(fill_flash, "OTP");
+
+		goto out_shutdown;
+	}
+
+	if (erase_otp) {
+		verbose_screen++;
+		if (fill_flash->chip->otp) {
+			char *otp_region_opt = NULL;
+			enum otp_region otp_region = OTP_REG_1;
+			if (!erase_otp_opt && (otp_region_opt = extract_param(&read_otp_opt, "reg", ","))) {
+				char *endptr = NULL;
+				// FIXME(hatim): Implement error-checking (?)
+				otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1;
+			} else
+				msg_gdbg("OTP region not specified, using default region 1\n");
+
+			msg_gdbg("Erasing OTP memory ...\n");
+			ret = fill_flash->chip->otp->erase(fill_flash, otp_region);
+			if (ret) {
+				msg_gerr("Erasing OTP memory failed\n");
+				goto out_shutdown;
+			}
+			msg_gdbg("Erasing OTP memory done\n");
+		} else
+			cli_infra_support(fill_flash, "OTP");
+
+		goto out_shutdown;
+	}
+
+	if (lock_otp) {
+		verbose_screen++;
+		if (fill_flash->chip->otp) {
+			char *otp_region_opt = NULL;
+			enum otp_region otp_region = OTP_REG_1;
+			if (!lock_otp_opt && (otp_region_opt = extract_param(&read_otp_opt, "reg", ","))) {
+				char *endptr = NULL;
+				// FIXME(hatim): Implement error-checking (?)
+				otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1;
+			} else
+				msg_gdbg("OTP region not specified, using default region 1\n");
+
+			msg_gdbg("Trying to lock OTP memory...\n");
+			ret = fill_flash->chip->otp->lock(fill_flash, otp_region);
+			if (ret) {
+				msg_gerr("Failed to lock\n");
+				goto out_shutdown;
+			}
+			msg_gdbg("OTP memory locked\n");
+		} else
+			cli_infra_support(fill_flash, "OTP");
+
 		goto out_shutdown;
 	}
 
 	/* FIXME: We should issue an unconditional chip reset here. This can be
 	 * done once we have a .reset function in struct flashchip.
 	 * Give the chip time to settle.
 	 */
 	programmer_delay(100000);
 	ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
 
 	unmap_flash(fill_flash);
 out_shutdown:
 	programmer_shutdown();
diff --git a/cli_common.c b/cli_common.c
index 71cc2dd..f0f23c9 100644
--- a/cli_common.c
+++ b/cli_common.c
@@ -48,70 +48,72 @@ char *flashbuses_to_text(enum chipbustype bustype)
 			ret = strcat_realloc(ret, "SPI, ");
 		if (bustype & BUS_PROG)
 			ret = strcat_realloc(ret, "Programmer-specific, ");
 		if (bustype == BUS_NONE)
 			ret = strcat_realloc(ret, "None, ");
 	}
 	/* Kill last comma. */
 	ret[strlen(ret) - 2] = '\0';
 	ret = realloc(ret, strlen(ret) + 1);
 	return ret;
 }
 
 
-void print_chip_support_status(const struct flashchip *chip)
+void print_chip_support_status(struct flashctx *flash)
 {
-	if (chip->feature_bits & FEATURE_OTP) {
-		msg_cdbg("This chip may contain one-time programmable memory. flashrom cannot read\n"
-			 "and may never be able to write it, hence it may not be able to completely\n"
-			 "clone the contents of this chip (see man page for details).\n");
+	if (flash->chip->otp) {
+		flash->chip->otp->print_status(flash);
+	} else if (flash->chip->feature_bits & FEATURE_OTP) {
+		msg_cdbg("This chip may contain one-time programmable memory. flashrom may be able\n"
+			 "to read, write, erase and/or lock it, if OTP infrastructure support is added.\n"
+			 "You could add support and send the patch to flashrom@flashrom.org\n");
 	}
 
-	if ((chip->tested.erase == NA) && (chip->tested.write == NA)) {
+	if ((flash->chip->tested.erase == NA) && (flash->chip->tested.write == NA)) {
 		msg_cdbg("This chip's main memory can not be erased/written by design.\n");
 	}
 
-	if ((chip->tested.probe == BAD) || (chip->tested.probe == NT) ||
-	    (chip->tested.read == BAD)  || (chip->tested.read == NT) ||
-	    (chip->tested.erase == BAD) || (chip->tested.erase == NT) ||
-	    (chip->tested.write == BAD) || (chip->tested.write == NT)){
+	if ((flash->chip->tested.probe == BAD) || (flash->chip->tested.probe == NT) ||
+	    (flash->chip->tested.read == BAD)  || (flash->chip->tested.read == NT) ||
+	    (flash->chip->tested.erase == BAD) || (flash->chip->tested.erase == NT) ||
+	    (flash->chip->tested.write == BAD) || (flash->chip->tested.write == NT)){
 		msg_cinfo("===\n");
-		if ((chip->tested.probe == BAD) ||
-		    (chip->tested.read == BAD) ||
-		    (chip->tested.erase == BAD) ||
-		    (chip->tested.write == BAD)) {
+		if ((flash->chip->tested.probe == BAD) ||
+		    (flash->chip->tested.read == BAD) ||
+		    (flash->chip->tested.erase == BAD) ||
+		    (flash->chip->tested.write == BAD)) {
 			msg_cinfo("This flash part has status NOT WORKING for operations:");
-			if (chip->tested.probe == BAD)
+			if (flash->chip->tested.probe == BAD)
 				msg_cinfo(" PROBE");
-			if (chip->tested.read == BAD)
+			if (flash->chip->tested.read == BAD)
 				msg_cinfo(" READ");
-			if (chip->tested.erase == BAD)
+			if (flash->chip->tested.erase == BAD)
 				msg_cinfo(" ERASE");
-			if (chip->tested.write == BAD)
+			if (flash->chip->tested.write == BAD)
 				msg_cinfo(" WRITE");
 			msg_cinfo("\n");
 		}
-		if ((chip->tested.probe == NT) ||
-		    (chip->tested.read == NT) ||
-		    (chip->tested.erase == NT) ||
-		    (chip->tested.write == NT)) {
+		if ((flash->chip->tested.probe == NT) ||
+		    (flash->chip->tested.read == NT) ||
+		    (flash->chip->tested.erase == NT) ||
+		    (flash->chip->tested.write == NT)) {
 			msg_cinfo("This flash part has status UNTESTED for operations:");
-			if (chip->tested.probe == NT)
+			if (flash->chip->tested.probe == NT)
 				msg_cinfo(" PROBE");
-			if (chip->tested.read == NT)
+			if (flash->chip->tested.read == NT)
 				msg_cinfo(" READ");
-			if (chip->tested.erase == NT)
+			if (flash->chip->tested.erase == NT)
 				msg_cinfo(" ERASE");
-			if (chip->tested.write == NT)
+			if (flash->chip->tested.write == NT)
 				msg_cinfo(" WRITE");
 			msg_cinfo("\n");
 		}
 		msg_cinfo("The test status of this chip may have been updated in the latest development\n"
 			  "version of flashrom. If you are running the latest development version,\n"
 			  "please email a report to flashrom@flashrom.org if any of the above operations\n"
 			  "work correctly for you with this flash chip. Please include the flashrom log\n"
 			  "file for all operations you tested (see the man page for details), and mention\n"
 			  "which mainboard or programmer you tested in the subject line.\n"
 			  "Thanks for your help!\n");
 	}
 }
 
diff --git a/flash.h b/flash.h
index c37426c..61c06c3 100644
--- a/flash.h
+++ b/flash.h
@@ -355,27 +355,27 @@ int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *
 
 /* Something happened that shouldn't happen, we'll abort. */
 #define ERROR_FATAL -0xee
 #define ERROR_FLASHROM_BUG -200
 /* We reached one of the hardcoded limits of flashrom. This can be fixed by
  * increasing the limit of a compile-time allocation or by switching to dynamic
  * allocation.
  * Note: If this warning is triggered, check first for runaway registrations.
  */
 #define ERROR_FLASHROM_LIMIT -201
 
 /* cli_common.c */
 char *flashbuses_to_text(enum chipbustype bustype);
-void print_chip_support_status(const struct flashchip *chip);
+void print_chip_support_status(struct flashctx *flash);
 
 /* cli_output.c */
 extern int verbose_screen;
 extern int verbose_logfile;
 #ifndef STANDALONE
 int open_logfile(const char * const filename);
 int close_logfile(void);
 void start_logging(void);
 #endif
 enum msglevel {
 	MSG_ERROR	= 0,
 	MSG_WARN	= 1,
 	MSG_INFO	= 2,
diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl
index d068ad9..9ed2fcc 100644
--- a/flashrom.8.tmpl
+++ b/flashrom.8.tmpl
@@ -43,26 +43,29 @@
 .TH FLASHROM 8 "2016-03-13" "0.9.9-unknown"
 .SH NAME
 flashrom \- detect, read, write, verify and erase flash chips
 .SH SYNOPSIS
 .B flashrom \fR[\fB\-h\fR|\fB\-R\fR|\fB\-L\fR|\fB\-z\fR|\
 \fB\-p\fR <programmername>[:<parameters>]
          [\fB\-E\fR|\fB\-r\fR <file>|\fB\-w\fR <file>|\fB\-v\fR <file>] \
 [\fB\-c\fR <chipname>]
          [\fB\-l\fR <file> [\fB\-i\fR <image>]] [\fB\-n\fR] [\fB\-f\fR]]
          [\fB\-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>]
          [\fB--print-status-reg\fR] [\fB--print-wp-status\fR]
          [\fB--wp-list\fR] [\fB--wp-enable\fR[=<MODE>]]
          [\fB--wp-disable\fR] [\fB--wp-set-range\fR start=<start>,len=<len>]
+         [\fB--print-otp-status\fR] [\fB--read-otp\fR file=<file>[,reg=<region>]]
+         [\fB--erase-otp\fR [reg=<region>]] [\fB--lock-otp\fR [reg=<region>]]
+         [\fB--write-otp\fR file=<file>[,reg=<region>]]
 .SH DESCRIPTION
 .B flashrom
 is a utility for detecting, reading, writing, verifying and erasing flash
 chips. It's often used to flash BIOS/EFI/coreboot/firmware images in-system
 using a supported mainboard. However, it also supports various external
 PCI/USB/parallel-port/serial-port based devices which can program flash chips,
 including some network cards (NICs), SATA/IDE controller cards, graphics cards,
 the Bus Pirate device, various FTDI FT2232/FT4232H/FT232H based USB devices, and more.
 .PP
 It supports a wide range of DIP32, PLCC32, DIP8, SO8/SOIC8, TSOP32, TSOP40,
 TSOP48, and BGA chips, which use various protocols such as LPC, FWH,
 parallel flash, or SPI.
 .SH OPTIONS
@@ -167,30 +170,75 @@ mode will lock the status register(s) until the next power down-up cycle. \
 After the cycle, SOFTWARE mode will be in effect.
 .sp
 *
 .B "SOFTWARE"
 mode allows writes to status register(s) irrespective of level of WP# pin.
 .TP
 .B "\-\-wp\-disable"
 Disable any write protection of status register(s) in effect. SOFTWARE mode \
 will be applied after disabling.
 .TP
 .B "\-\-wp\-set\-range start=<start>,len=<len>"
 Configure status register(s) to protect
 .B "<len>"
- kB of memory starting from address
-.B "<start>".
+kB of memory starting from address
+.B "<start>."
 Both start and len must be supplied. (Consider setting a write protection \
 mode to prevent against configuration changes to status register(s).)
+
+.TP
+.B "\-\-print\-otp\-status"
+Print details of OTP memory region(s) for the chip along with corresponding \
+modifier bits
+.TP
+.B "\-\-read\-otp file=<file>[,reg=<reg>]"
+Read OTP memory region
+.B "<reg>"
+of the chip, and save to
+.B "<file>."
+If
+.B "<reg>"
+is ommitted, then it defaults to the first OTP region.
+.sp
+Example:
+.sp
+.B "  flashrom \-p prog \-c chip \-\-read\-otp file=read_otp.bin,reg=2"
+.sp
+will read the the 2nd OTP region and save it to
+.B "read_otp.bin."
+.TP
+.B "\-\-write\-otp file=<file>[,reg=<reg>]"
+Write
+.B "<file>"
+to OTP memory region
+.B "<reg>."
+of the chip. If
+.B "<reg>"
+is ommitted, then it defaults to the first OTP region.
+.TP
+.B "\-\-erase\-otp [reg=<reg>]"
+Erase OTP memory region
+.B "<reg>."
+of the chip. If
+.B "<reg>"
+is ommitted, then it defaults to the first OTP region.
+.TP
+.B "\-\-lock\-otp [reg=<reg>]"
+Lock OTP memory region
+.B "<reg>."
+of the chip against any future writes (Please be cautious, this action is \
+almost always irreversible). If
+.B "<reg>"
+is ommitted, then it defaults to the first OTP region.
 .TP
 .B "\-f, \-\-force"
 Force one or more of the following actions:
 .sp
 * Force chip read and pretend the chip is there.
 .sp
 * Force chip access even if the chip is bigger than the maximum supported \
 size for the flash bus.
 .sp
 * Force erase even if erase is known bad.
 .sp
 * Force write even if write is known bad.
 .TP
