diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c
index 79ff00f4..36c7b99f 100644
--- a/libflash/blocklevel.c
+++ b/libflash/blocklevel.c
@@ -1,4 +1,4 @@
-/* Copyright 2013-2015 IBM Corp.
+/* Copyright 2013-2017 IBM Corp.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+#include <inttypes.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <stdio.h>
 #include <stdbool.h>
-#include <errno.h>
 #include <string.h>
-#include <inttypes.h>
+#include <unistd.h>
 
 #include <libflash/errors.h>
 
+#include "libflash.h"
 #include "blocklevel.h"
 #include "ecc.h"
 
@@ -198,6 +199,12 @@ int blocklevel_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
 	}
 
 	/* Programmer may be making a horrible mistake without knowing it */
+	if (pos & bl->erase_mask) {
+		fprintf(stderr, "blocklevel_erase: pos (0x%"PRIx64") is not erase block (0x%08x) aligned\n",
+				pos, bl->erase_mask + 1);
+		return FLASH_ERR_ERASE_BOUNDARY;
+	}
+
 	if (len & bl->erase_mask) {
 		fprintf(stderr, "blocklevel_erase: len (0x%"PRIx64") is not erase block (0x%08x) aligned\n",
 				len, bl->erase_mask + 1);
@@ -270,6 +277,125 @@ static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint6
 	return same ? 0 : 1;
 }
 
+int blocklevel_smart_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
+{
+	uint64_t block_size;
+	void *erase_buf;
+	int rc;
+
+	if (!bl) {
+		errno = EINVAL;
+		return FLASH_ERR_PARM_ERROR;
+	}
+
+	FL_DBG("%s: pos: 0x%" PRIx64 ", len: 0x%" PRIx64 "\n", __func__, pos, len);
+
+	/* Nothing smart needs to be done, pos and len are aligned */
+	if ((pos & bl->erase_mask) == 0 && (len & bl->erase_mask) == 0) {
+		FL_DBG("No smarts needed, calling blocklevel_erase()\n");
+		return blocklevel_erase(bl, pos, len);
+	}
+
+	block_size = bl->erase_mask + 1;
+	erase_buf = malloc(block_size);
+	if (!erase_buf) {
+		errno = ENOMEM;
+		return FLASH_ERR_MALLOC_FAILED;
+	}
+
+	rc = reacquire(bl);
+	if (rc) {
+		free(erase_buf);
+		return rc;
+	}
+
+	if (pos & bl->erase_mask) {
+		/*
+		 * base_pos and base_len are the values in the first erase
+		 * block that we need to preserve: the region up to pos.
+		 */
+		uint64_t base_pos = pos & ~(bl->erase_mask);
+		uint64_t base_len = pos - base_pos;
+
+		/*
+		 * Read the entire block in case this is the ONLY block we're
+		 * modifying, we may need the end chunk of it later
+		 */
+		rc = bl->read(bl, base_pos, erase_buf, block_size);
+		if (rc)
+			goto out;
+
+		rc = bl->erase(bl, base_pos, block_size);
+		if (rc)
+			goto out;
+
+		rc = bl->write(bl, base_pos, erase_buf, base_len);
+		if (rc)
+			goto out;
+
+		/*
+		 * The requested erase fits entirely into this erase block and
+		 * so we need to write back the chunk at the end of the block
+		 */
+		if (base_pos + base_len + len < base_pos + block_size) {
+			rc = bl->write(bl, pos + len, erase_buf + pos + len,
+					block_size - base_len - len);
+			goto out;
+		}
+
+		pos += block_size - base_len;
+		len -= block_size - base_len;
+	}
+
+	/* Now we should be aligned, best to double check */
+	if (pos & bl->erase_mask) {
+		rc = FLASH_ERR_PARM_ERROR;
+		goto out;
+	}
+
+	if (len & ~(bl->erase_mask)) {
+		rc = bl->erase(bl, pos, len & ~(bl->erase_mask));
+		if (rc)
+			goto out;
+
+		pos += len & ~(bl->erase_mask);
+		len -= len & ~(bl->erase_mask);
+	}
+
+	/* Length should be less than a block now */
+	if (len > block_size) {
+		rc = FLASH_ERR_PARM_ERROR;
+		goto out;
+	}
+
+	if (len & bl->erase_mask) {
+		/*
+		 * top_pos is the first byte that must be preserved and
+		 * top_len is the length from top_pos to the end of the erase
+		 * block: the region that must be preserved
+		 */
+		uint64_t top_pos = pos + len;
+		uint64_t top_len = block_size - len;
+
+		rc = bl->read(bl, top_pos, erase_buf, top_len);
+		if (rc)
+			goto out;
+
+		rc = bl->erase(bl, pos, block_size);
+		if (rc)
+			goto out;
+
+		rc = bl->write(bl, top_pos, erase_buf, top_len);
+		if (rc)
+			goto out;
+	}
+
+out:
+	free(erase_buf);
+	release(bl);
+	return rc;
+}
+
 int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len)
 {
 	uint32_t erase_size;
diff --git a/libflash/blocklevel.h b/libflash/blocklevel.h
index 09f0096a..ba42c83d 100644
--- a/libflash/blocklevel.h
+++ b/libflash/blocklevel.h
@@ -1,4 +1,4 @@
-/* Copyright 2013-2015 IBM Corp.
+/* Copyright 2013-2017 IBM Corp.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -75,6 +75,14 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint64_
  */
 int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len);
 
+/*
+ * blocklevel_smart_erase() will handle unaligned erases.
+ * blocklevel_erase() expects a erase_granule aligned buffer and the
+ * erase length to be an exact multiple of erase_granule,
+ * blocklevel_smart_erase() solves this requirement by performing a
+ * read erase write under the hood.
+ */
+int blocklevel_smart_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len);
 /* Implemented in software at this level */
 int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len);
 
