From 4b1568cbb90eaaa9fb138f5cac22ea60ed7e7136 Mon Sep 17 00:00:00 2001
From: sheinz <shein@bk.ru>
Date: Mon, 18 Jul 2016 13:12:21 +0300
Subject: [PATCH] SPIFFS: flash access refactoring.

---
 extras/spiffs/esp_spi_flash.c    | 336 -------------------------------
 extras/spiffs/esp_spi_flash.h    |  11 -
 extras/spiffs/esp_spiffs.c       | 132 ++----------
 extras/spiffs/esp_spiffs.h       |   2 +-
 extras/spiffs/esp_spiffs_flash.c | 250 +++++++++++++++++++++++
 extras/spiffs/esp_spiffs_flash.h |  64 ++++++
 6 files changed, 332 insertions(+), 463 deletions(-)
 delete mode 100644 extras/spiffs/esp_spi_flash.c
 delete mode 100644 extras/spiffs/esp_spi_flash.h
 create mode 100644 extras/spiffs/esp_spiffs_flash.c
 create mode 100644 extras/spiffs/esp_spiffs_flash.h

diff --git a/extras/spiffs/esp_spi_flash.c b/extras/spiffs/esp_spi_flash.c
deleted file mode 100644
index 74f8d98..0000000
--- a/extras/spiffs/esp_spi_flash.c
+++ /dev/null
@@ -1,336 +0,0 @@
-#include "esp_spi_flash.h"
-#include "flashchip.h"
-#include "espressif/spi_flash.h"
-#include "FreeRTOS.h"
-#include "esp/rom.h"
-#include "esp/spi_regs.h"
-
-
-static uint32_t IRAM read_status(sdk_flashchip_t *flashchip, uint32_t *status)
-{
-    uint32_t _status;
-
-    do {
-        SPI(0).RSTATUS = 0;
-        SPI(0).CMD = SPI_CMD_READ_SR;
-        while (SPI(0).CMD) {}
-        _status = SPI(0).RSTATUS & flashchip->status_mask;
-    } while ( _status & 0b1);
-
-    *status = _status;
-
-    return 0;
-}
-
-static uint32_t IRAM wait_idle(sdk_flashchip_t *flashchip)
-{
-    while (DPORT.SPI_READY & DPORT_SPI_READY_IDLE) {}
-    uint32_t a3;
-    return read_status(flashchip, &a3);
-}
-
-static uint32_t IRAM write_enable(sdk_flashchip_t *flashchip)
-{
-    uint32_t local0 = 0;
-
-    wait_idle(flashchip);
-
-    SPI(0).CMD = SPI_CMD_WRITE_ENABLE;
-    while (SPI(0).CMD) {}
-
-    if (!(local0 & 0b1)) {
-        do {
-            read_status(flashchip, &local0);
-        } while (!(local0 & (1<<1)));
-    }
-    return 0;
-}
-
-static uint32_t IRAM page_program(sdk_flashchip_t *flashchip, uint32_t dest_addr,
-    uint32_t *buf, uint32_t size)
-{
-    if (size & 0b11) {
-        return 1;
-    }
-
-    // check if block to write doesn't cross page boundary
-    if (flashchip->page_size < size + (dest_addr % flashchip->page_size)) {
-        return 1;
-    }
-    wait_idle(flashchip);
-    if (size < 1) {
-        return 0;
-    }
-
-    // a12 = 0x60000200
-    // a0 =  0x00FFFFFF
-    // a6 = (dest_addr & 0x00FFFFFF) | 0x20000000
-    while (size >= 32) {
-        SPI(0).ADDR = (dest_addr & 0x00FFFFFF) | 0x20000000;
-        // a4 - loop variable += 4
-        // a5 = buf[0]
-        for (uint8_t i = 0; i != 8; i++) {
-            SPI(0).W[i] = buf[i];
-        }
-        size -= 32;
-        dest_addr += 32;
-        buf += 8;
-        if (write_enable(flashchip)) {
-            return 1;
-        }
-        SPI(0).CMD = SPI_CMD_PP;
-        while (SPI(0).CMD) {}   // wait for reg->cmd to be 0
-        wait_idle(flashchip);
-        // a0 = 0x00FFFFFF
-        if (size < 1) {
-            return 0;
-        }
-    }
-    // a7 = 0x00FFFFFF & dest_addr
-    // a4 = size << 24;
-    // a4 = a7 | a4
-    SPI(0).ADDR = (size << 24) | (0x00FFFFFF & dest_addr);
-    // a6 = 0b11 & size
-    // a3 = size >> 2;
-    // a5 = a3 + 1
-    uint32_t words = size >> 2;
-    if (0b11 & size) {
-        words += 1;
-    }
-    words = words & 0xFF;
-    if (words != 0) {
-        // a4 = 0
-        uint8_t i = 0;
-
-        if (words & 0b1) {  // bit 0 is set in a3
-            SPI(0).W[0] = buf[0];
-            i++;
-        }
-        // a6 = a3 >> 1;
-        if (words >> 1) {
-            // a6 =  0x600000200
-            // buff[0]
-            for (; i != words; i++) {
-                SPI(0).W[i] = buf[i];
-            }
-        }
-    }
-
-    if (write_enable(flashchip)) {
-        return 1;
-    }
-    SPI(0).CMD = SPI_CMD_PP;
-    while (SPI(0).CMD) {}   // wait for reg->cmd to be 0
-    wait_idle(flashchip);
-    // a0 = 0x00FFFFFF
-    return 0;
-}
-
-static uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr,
-        uint32_t *dst, uint32_t size)
-{
-    // a12 = dst
-    if ((addr + size) > flashchip->chip_size) {
-        return 1;
-    }
-
-    // a14 = addr
-    // a13 = size
-    wait_idle(flashchip);
-    if (size < 1) {
-        return 0;
-    }
-    // SPI(0).CMD
-    while (size >= 32) {
-        // a8 = addr | 0x20000000;
-        SPI(0).ADDR = addr | 0x20000000;
-        SPI(0).CMD = SPI_CMD_READ;
-        while (SPI(0).CMD) {};
-        for (uint32_t a2 = 0; a2 < 8; a2++) {
-            *dst = SPI(0).W[a2];
-            dst++;
-        }
-        size -= 32;
-        addr += 32;
-    }
-
-    if (size >= 1) {
-        // a7 = size << 24;
-        // a7 = addr | a7
-        SPI(0).ADDR = addr | (size << 24);
-        SPI(0).CMD = SPI_CMD_READ;
-        while (SPI(0).CMD) {};
-        // a10 = size & 0b11
-        uint8_t a7 = size >> 2;
-        // a9 = a7 + 1
-        if (size & 0b11) {
-           // a7 = a7 + 1
-           a7++;
-        }
-        // a7 = a7 & 0xFF
-        if (!a7) {
-            return 0;
-        }
-        uint8_t a2 = 0;
-        if (a7 & 0b1) {
-            a2 = 1;
-            // a11 = SPI(0).W0
-            *dst = SPI(0).W[0];
-            dst += 1;
-        }
-        size = a7 >> 1;
-        if (!size) {
-            return 0;
-        }
-        for (; a2 != a7; a2++) {
-            *dst = SPI(0).W[a2];
-            dst += 1;
-        }
-    }
-
-    return 0;
-}
-
-/**
- * Reverse engineered implementation of spi_flash.o:sdk_SPIRead
- */
-static uint32_t IRAM spi_read(uint32_t dest_addr, void *src, uint32_t size)
-{
-    if (read_data(&sdk_flashchip, dest_addr, (uint32_t*)src, size)) {
-        return 1;
-    } else {
-        return 0;
-    }
-}
-
-/**
- * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_read
- */
-uint32_t IRAM esp_spi_flash_read(uint32_t dest_addr, void *src, uint32_t size)
-{
-    if (src) {
-        vPortEnterCritical();
-        Cache_Read_Disable();
-        uint32_t result = spi_read(dest_addr, src, size);
-        Cache_Read_Enable(0, 0, 1);
-        vPortExitCritical();
-        return result;
-    } else {
-        return 1;
-    }
-}
-
-/**
- * Reverse engineered implementation of spi_flash.o:sdk_SPIWrite
- */
-static uint32_t IRAM spi_write(uint32_t dest_addr, void *dst, uint32_t size)
-{
-    if (sdk_flashchip.chip_size < (dest_addr + size)) {
-        return 1;
-    }
-
-    uint32_t write_bytes_to_page = sdk_flashchip.page_size -
-        (dest_addr % sdk_flashchip.page_size);
-
-    if (size < write_bytes_to_page) {
-        if (page_program(&sdk_flashchip, dest_addr, (uint32_t*)dst, size)) {
-            return 1;
-        } else {
-            return 0;
-        }
-    }
-
-    if (page_program(&sdk_flashchip, dest_addr, (uint32_t*)dst, write_bytes_to_page)) {
-        return 1;
-    }
-
-    uint32_t offset = write_bytes_to_page;
-    uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size;
-    for (uint8_t i = 0; i != pages_to_write; i++) {
-        if (page_program(&sdk_flashchip, dest_addr + offset,
-                    dst + ((offset>>2)<<2), sdk_flashchip.page_size)) {
-            return 1;
-        }
-        offset += sdk_flashchip.page_size;
-    }
-
-    if (page_program(&sdk_flashchip, dest_addr + offset,
-                dst + ((offset>>2)<<2), size - offset)) {
-        return 1;
-    } else {
-        return 0;
-    }
-}
-
-/**
- * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_write
- */
-uint32_t IRAM esp_spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size)
-{
-    if (dst) {
-        if (size & 0b11) {  // not 4-byte aligned
-            size = size >> 2;
-            size = (size << 2) + 1;
-        }
-        vPortEnterCritical();
-        Cache_Read_Disable();
-        uint32_t result = spi_write(dest_addr, dst, size);
-        Cache_Read_Enable(0, 0, 1);
-        vPortExitCritical();
-        return result;
-    } else {
-        return 1;
-    }
-}
-
-static uint32_t IRAM sector_erase(sdk_flashchip_t *chip, uint32_t addr)
-{
-    // a12 -> addr
-    // a0 = addr & 0xFFF
-    if (addr & 0xFFF) {
-        return 1;
-    }
-
-    wait_idle(chip);
-    SPI(0).ADDR = addr & 0x00FFFFFF;
-    SPI(0).CMD = SPI_CMD_SE;
-    while (SPI(0).CMD) {};
-    wait_idle(chip);
-
-    return 0;
-}
-
-/**
- * Reverse engineered implementation of spi_flash.o:sdk_SPIEraseSector
- */
-static uint32_t IRAM spi_erase_sector(uint32_t sector)
-{
-    if (sector >= (sdk_flashchip.chip_size / sdk_flashchip.sector_size)) {
-        return 1;
-    }
-
-    if (write_enable(&sdk_flashchip)) {
-        return 1;
-    }
-
-    if (sector_erase(&sdk_flashchip, sdk_flashchip.sector_size * sector)) {
-        return 1;
-    }
-    return 0;
-}
-
-/**
- * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_erase_sector
- */
-uint32_t IRAM esp_spi_flash_erase(uint32_t sector)
-{
-    vPortEnterCritical();
-    Cache_Read_Disable();
-
-    uint32_t result = spi_erase_sector(sector);
-
-    Cache_Read_Enable(0, 0, 1);
-    vPortExitCritical();
-
-    return result;
-}
diff --git a/extras/spiffs/esp_spi_flash.h b/extras/spiffs/esp_spi_flash.h
deleted file mode 100644
index 6bf43e9..0000000
--- a/extras/spiffs/esp_spi_flash.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __ESP_SPI_FLASH_H__
-#define __ESP_SPI_FLASH_H__
-
-#include <stdint.h>
-#include "common_macros.h"
-
-uint32_t IRAM esp_spi_flash_read(uint32_t dest_addr, void *src, uint32_t size);
-uint32_t IRAM esp_spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size);
-uint32_t IRAM esp_spi_flash_erase(uint32_t sector);
-
-#endif  // __ESP_SPI_FLASH_H__
diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c
index a3c6b12..1574be2 100644
--- a/extras/spiffs/esp_spiffs.c
+++ b/extras/spiffs/esp_spiffs.c
@@ -11,7 +11,7 @@
 #include <stdbool.h>
 #include <esp/uart.h>
 #include <fcntl.h>
-#include "esp_spi_flash.h"
+#include "esp_spiffs_flash.h"
 
 spiffs fs;
 
@@ -31,113 +31,19 @@ static fs_buf_t cache_buf = {0};
 
 #define ESP_SPIFFS_CACHE_PAGES     5
 
-
-/*
- * Flash addresses and size alignment is a rip-off of Arduino implementation.
- */
-
 static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst)
 {
-    uint32_t result = SPIFFS_OK;
-    uint32_t alignedBegin = (addr + 3) & (~3);
-    uint32_t alignedEnd = (addr + size) & (~3);
-    if (alignedEnd < alignedBegin) {
-        alignedEnd = alignedBegin;
+    if (esp_spiffs_flash_read(addr, dst, size) == ESP_SPIFFS_FLASH_ERROR) {
+        return SPIFFS_ERR_INTERNAL;
     }
 
-    if (addr < alignedBegin) {
-        uint32_t nb = alignedBegin - addr;
-        uint32_t tmp;
-        if (esp_spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) {
-            printf("spi_flash_read failed\n");
-            return SPIFFS_ERR_INTERNAL;
-        }
-        memcpy(dst, &tmp + 4 - nb, nb);
-    }
-
-    if (alignedEnd != alignedBegin) {
-        if (esp_spi_flash_read(alignedBegin,
-                    (uint32_t*) (dst + alignedBegin - addr),
-                    alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) {
-            printf("spi_flash_read failed\n");
-            return SPIFFS_ERR_INTERNAL;
-        }
-    }
-
-    if (addr + size > alignedEnd) {
-        uint32_t nb = addr + size - alignedEnd;
-        uint32_t tmp;
-        if (esp_spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) {
-            printf("spi_flash_read failed\n");
-            return SPIFFS_ERR_INTERNAL;
-        }
-
-        memcpy(dst + size - nb, &tmp, nb);
-    }
-
-    return result;
+    return SPIFFS_OK;
 }
 
-static const int UNALIGNED_WRITE_BUFFER_SIZE = 512;
-
 static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src)
 {
-    uint32_t alignedBegin = (addr + 3) & (~3);
-    uint32_t alignedEnd = (addr + size) & (~3);
-    if (alignedEnd < alignedBegin) {
-        alignedEnd = alignedBegin;
-    }
-
-    if (addr < alignedBegin) {
-        uint32_t ofs = alignedBegin - addr;
-        uint32_t nb = (size < ofs) ? size : ofs;
-        uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff};
-        memcpy(tmp + 4 - ofs, src, nb);
-        if (esp_spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4)
-                != SPI_FLASH_RESULT_OK) {
-            printf("spi_flash_write failed\n");
-            return SPIFFS_ERR_INTERNAL;
-        }
-    }
-
-    if (alignedEnd != alignedBegin) {
-        uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr);
-        uint32_t srcAlign = ((uint32_t) srcLeftover) & 3;
-        if (!srcAlign) {
-            if (esp_spi_flash_write(alignedBegin, (uint32_t*) srcLeftover,
-                    alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) {
-                printf("spi_flash_write failed\n");
-                return SPIFFS_ERR_INTERNAL;
-            }
-        }
-        else {
-            uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE];
-            for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) {
-                size_t willCopy = sizeLeft < sizeof(buf) ? sizeLeft : sizeof(buf);
-                memcpy(buf, srcLeftover, willCopy);
-
-                if (esp_spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy)
-                        != SPI_FLASH_RESULT_OK) {
-                    printf("spi_flash_write failed\n");
-                    return SPIFFS_ERR_INTERNAL;
-                }
-
-                sizeLeft -= willCopy;
-                srcLeftover += willCopy;
-                alignedBegin += willCopy;
-            }
-        }
-    }
-
-    if (addr + size > alignedEnd) {
-        uint32_t nb = addr + size - alignedEnd;
-        uint32_t tmp = 0xffffffff;
-        memcpy(&tmp, src + size - nb, nb);
-
-        if (esp_spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) {
-            printf("spi_flash_write failed\n");
-            return SPIFFS_ERR_INTERNAL;
-        }
+    if (esp_spiffs_flash_write(addr, src, size) == ESP_SPIFFS_FLASH_ERROR) {
+        return SPIFFS_ERR_INTERNAL;
     }
 
     return SPIFFS_OK;
@@ -145,19 +51,15 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src)
 
 static s32_t esp_spiffs_erase(u32_t addr, u32_t size)
 {
-    if (addr % SPI_FLASH_SEC_SIZE) {
-        printf("Unaligned erase addr=%x\n", addr);
-    }
-    if (size % SPI_FLASH_SEC_SIZE) {
-        printf("Unaligned erase size=%d\n", size);
+    uint32_t sectors = size / SPI_FLASH_SEC_SIZE;
+
+    for (uint32_t i = 0; i < sectors; i++) {
+        if (esp_spiffs_flash_erase_sector(addr + (SPI_FLASH_SEC_SIZE * i))
+                == ESP_SPIFFS_FLASH_ERROR) {
+            return SPIFFS_ERR_INTERNAL;
+        }
     }
 
-    const uint32_t sector = addr / SPI_FLASH_SEC_SIZE;
-    const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE;
-
-    for (uint32_t i = 0; i < sectorCount; ++i) {
-        esp_spi_flash_erase(sector + i);
-    }
     return SPIFFS_OK;
 }
 
@@ -196,8 +98,8 @@ int32_t esp_spiffs_mount()
     printf("SPIFFS memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n",
             work_buf.size, fds_buf.size, cache_buf.size);
 
-    int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf, 
-            (uint8_t*)fds_buf.buf, fds_buf.size, 
+    int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf,
+            (uint8_t*)fds_buf.buf, fds_buf.size,
             cache_buf.buf, cache_buf.size, 0);
 
     if (err != SPIFFS_OK) {
@@ -213,7 +115,7 @@ int32_t esp_spiffs_mount()
 long _write_r(struct _reent *r, int fd, const char *ptr, int len )
 {
     if(fd != r->_stdout->_file) {
-        long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET), 
+        long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET),
                 (char*)ptr, len);
         return ret;
     }
@@ -296,6 +198,6 @@ int _stat_r(struct _reent *r, const char *pathname, void *buf)
 }
 
 off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence)
-{   
+{
     return SPIFFS_lseek(&fs, (spiffs_file)(fd - FD_OFFSET), offset, whence);
 }
diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h
index f074017..60dc7f0 100644
--- a/extras/spiffs/esp_spiffs.h
+++ b/extras/spiffs/esp_spiffs.h
@@ -14,7 +14,7 @@ extern spiffs fs;
 
 /**
  * Prepare for SPIFFS mount.
- * 
+ *
  * The function allocates all the necessary buffers.
  */
 void esp_spiffs_init();
diff --git a/extras/spiffs/esp_spiffs_flash.c b/extras/spiffs/esp_spiffs_flash.c
new file mode 100644
index 0000000..1098551
--- /dev/null
+++ b/extras/spiffs/esp_spiffs_flash.c
@@ -0,0 +1,250 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 sheinz (https://github.com/sheinz)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "esp_spiffs_flash.h"
+#include "flashchip.h"
+#include "espressif/spi_flash.h"
+#include "FreeRTOS.h"
+#include "esp/rom.h"
+#include "esp/spi_regs.h"
+
+
+#define SPI_WRITE_MAX_SIZE  32
+#define SPI_READ_MAX_SIZE   32
+
+/**
+ * Low level SPI flash write. Write block of data up to SPI_WRITE_MAX_SIZE.
+ */
+static inline uint32_t IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr,
+        uint8_t *buf, uint32_t size)
+{
+    SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24);
+
+    uint32_t data = 0;
+    // Copy more than size, in order not to handle unaligned size.
+    // The exact size will be written to flash
+    for (uint32_t i = 0; i != SPI_WRITE_MAX_SIZE; i++) { data >>= 8;
+        data |= (uint32_t)buf[i] << 24;
+
+        if (i & 0b11) {
+            SPI(0).W[i >> 2] = data;
+        }
+    }
+
+    if (SPI_write_enable(chip)) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+
+    SPI(0).CMD = SPI_CMD_PP;
+    while (SPI(0).CMD) {}
+    Wait_SPI_Idle(chip);
+
+    return ESP_SPIFFS_FLASH_OK;
+}
+
+/**
+ * Write a page of flash. Data block should bot cross page boundary.
+ */
+static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_addr,
+    uint8_t *buf, uint32_t size)
+{
+    // check if block to write doesn't cross page boundary
+    if (flashchip->page_size < size + (dest_addr % flashchip->page_size)) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+
+    if (size < 1) {
+        return ESP_SPIFFS_FLASH_OK;
+    }
+
+    Wait_SPI_Idle(flashchip);
+
+    while (size >= SPI_WRITE_MAX_SIZE) {
+        if (spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE)) {
+            return ESP_SPIFFS_FLASH_ERROR;
+        }
+
+        size -= SPI_WRITE_MAX_SIZE;
+        dest_addr += SPI_WRITE_MAX_SIZE;
+        buf += SPI_WRITE_MAX_SIZE;
+
+        if (size < 1) {
+            return ESP_SPIFFS_FLASH_OK;
+        }
+    }
+
+    if (spi_write_data(flashchip, dest_addr, buf, size)) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+    return ESP_SPIFFS_FLASH_OK;
+}
+
+/**
+ * Split block of data into pages and write pages.
+ */
+static uint32_t IRAM spi_write(uint32_t addr, uint8_t *dst, uint32_t size)
+{
+    if (sdk_flashchip.chip_size < (addr + size)) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+
+    uint32_t write_bytes_to_page = sdk_flashchip.page_size -
+        (addr % sdk_flashchip.page_size);  // TODO: place for optimization
+
+    if (size < write_bytes_to_page) {
+        if (spi_write_page(&sdk_flashchip, addr, dst, size)) {
+            return ESP_SPIFFS_FLASH_ERROR;
+        }
+        return ESP_SPIFFS_FLASH_OK;
+    }
+
+    if (spi_write_page(&sdk_flashchip, addr, dst, write_bytes_to_page)) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+
+    uint32_t offset = write_bytes_to_page;
+    uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size;
+    for (uint8_t i = 0; i != pages_to_write; i++) {
+        if (spi_write_page(&sdk_flashchip, addr + offset,
+                    dst + offset, sdk_flashchip.page_size)) {
+            return ESP_SPIFFS_FLASH_ERROR;
+        }
+        offset += sdk_flashchip.page_size;
+    }
+
+    if (spi_write_page(&sdk_flashchip, addr + offset,
+                dst + offset, size - offset)) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+    return ESP_SPIFFS_FLASH_OK;
+}
+
+uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size)
+{
+    uint32_t result = ESP_SPIFFS_FLASH_ERROR;
+
+    if (buf) {
+        vPortEnterCritical();
+        Cache_Read_Disable();
+
+        result = spi_write(addr, buf, size);
+
+        Cache_Read_Enable(0, 0, 1);
+        vPortExitCritical();
+    }
+
+    return result;
+}
+
+/**
+ * Read SPI flash up to SPI_READ_MAX_SIZE size.
+ */
+static inline void IRAM read_block(sdk_flashchip_t *chip, uint32_t addr,
+        uint8_t *buf, uint32_t size)
+{
+    SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24);
+    SPI(0).CMD = SPI_CMD_READ;
+    while (SPI(0).CMD) {};
+    uint32_t data = 0;
+    for (uint32_t i = 0; i < size; i++) {
+        if (!(i & 0b11)) {
+            data = SPI(0).W[i>>2];
+        }
+        buf[i] = 0xFF & data;
+        data >>= 8;
+    }
+}
+
+/**
+ * Read SPI flash data. Data region doesn't need to be page aligned.
+ */
+static inline uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr,
+        uint8_t *dst, uint32_t size)
+{
+    if (size < 1) {
+        return ESP_SPIFFS_FLASH_OK;
+    }
+
+    if ((addr + size) > flashchip->chip_size) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+
+    Wait_SPI_Idle(flashchip);
+
+    while (size >= SPI_READ_MAX_SIZE) {
+        read_block(flashchip, addr, dst, SPI_READ_MAX_SIZE);
+        dst += SPI_READ_MAX_SIZE;
+        size -= SPI_READ_MAX_SIZE;
+        addr += SPI_READ_MAX_SIZE;
+    }
+
+    if (size > 0) {
+        read_block(flashchip, addr, dst, size);
+    }
+
+    return ESP_SPIFFS_FLASH_OK;
+}
+
+uint32_t IRAM esp_spiffs_flash_read(uint32_t dest_addr, uint8_t *buf, uint32_t size)
+{
+    uint32_t result = ESP_SPIFFS_FLASH_ERROR;
+
+    if (buf) {
+        vPortEnterCritical();
+        Cache_Read_Disable();
+
+        result = read_data(&sdk_flashchip, dest_addr, buf, size);
+
+        Cache_Read_Enable(0, 0, 1);
+        vPortExitCritical();
+    }
+
+    return result;
+}
+
+uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr)
+{
+    if ((addr + sdk_flashchip.sector_size) > sdk_flashchip.chip_size) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+
+    if (addr & 0xFFF) {
+        return ESP_SPIFFS_FLASH_ERROR;
+    }
+
+    vPortEnterCritical();
+    Cache_Read_Disable();
+
+    SPI_write_enable(&sdk_flashchip);
+
+    Wait_SPI_Idle(&sdk_flashchip);
+    SPI(0).ADDR = addr & 0x00FFFFFF;
+    SPI(0).CMD = SPI_CMD_SE;
+    while (SPI(0).CMD) {};
+    Wait_SPI_Idle(&sdk_flashchip);
+
+    Cache_Read_Enable(0, 0, 1);
+    vPortExitCritical();
+
+    return ESP_SPIFFS_FLASH_OK;
+}
diff --git a/extras/spiffs/esp_spiffs_flash.h b/extras/spiffs/esp_spiffs_flash.h
new file mode 100644
index 0000000..abfe864
--- /dev/null
+++ b/extras/spiffs/esp_spiffs_flash.h
@@ -0,0 +1,64 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 sheinz (https://github.com/sheinz)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef __ESP_SPIFFS_FLASH_H__
+#define __ESP_SPIFFS_FLASH_H__
+
+#include <stdint.h>
+#include "common_macros.h"
+
+#define ESP_SPIFFS_FLASH_OK        0
+#define ESP_SPIFFS_FLASH_ERROR     1
+
+/**
+ * Read data from SPI flash.
+ *
+ * @param addr Address to read from. Can be not aligned.
+ * @param buf Buffer to read to. Doesn't have to be aligned.
+ * @param size Size of data to read. Buffer size must be >= than data size.
+ *
+ * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR
+ */
+uint32_t IRAM esp_spiffs_flash_read(uint32_t addr, uint8_t *buf, uint32_t size);
+
+/**
+ * Write data to SPI flash.
+ *
+ * @param addr Address to write to. Can be not aligned.
+ * @param buf Buffer of data to write to flash. Doesn't have to be aligned.
+ * @param size Size of data to write. Buffer size must be >= than data size.
+ *
+ * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR
+ */
+uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size);
+
+/**
+ * Erase a sector.
+ *
+ * @param addr Address of sector to erase. Must be sector aligned.
+ *
+ * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR
+ */
+uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr);
+
+#endif  // __ESP_SPIFFS_FLASH_H__